mirror of
https://github.com/ascribe/onion.git
synced 2024-11-15 01:25:17 +01:00
Merge remote-tracking branch 'remotes/origin/AD-456-ikonotv-branded-page-for-registra' into AD-885-convert-roles-to-acls
This commit is contained in:
commit
1106cebf98
12
gulpfile.js
12
gulpfile.js
@ -189,17 +189,7 @@ function bundle(watch) {
|
||||
.pipe(gulpif(!argv.production, sourcemaps.write())) // writes .map file
|
||||
.on('error', notify.onError('Error: <%= error.message %>'))
|
||||
.pipe(gulpif(argv.production, uglify({
|
||||
mangle: true,
|
||||
compress: {
|
||||
sequences: true,
|
||||
dead_code: true,
|
||||
conditionals: true,
|
||||
booleans: true,
|
||||
unused: true,
|
||||
if_return: true,
|
||||
join_vars: true,
|
||||
drop_console: true
|
||||
}
|
||||
mangle: true
|
||||
})))
|
||||
.on('error', notify.onError('Error: <%= error.message %>'))
|
||||
.pipe(gulp.dest('./build/js'))
|
||||
|
@ -12,8 +12,8 @@ class ContractListActions {
|
||||
);
|
||||
}
|
||||
|
||||
fetchContractList() {
|
||||
OwnershipFetcher.fetchContractList()
|
||||
fetchContractList(isActive) {
|
||||
OwnershipFetcher.fetchContractList(isActive)
|
||||
.then((contracts) => {
|
||||
this.actions.updateContractList(contracts.results);
|
||||
})
|
||||
@ -23,8 +23,8 @@ class ContractListActions {
|
||||
});
|
||||
}
|
||||
|
||||
makeContractPublic(contract){
|
||||
contract.public = true;
|
||||
|
||||
changeContract(contract){
|
||||
return Q.Promise((resolve, reject) => {
|
||||
OwnershipFetcher.makeContractPublic(contract)
|
||||
.then((res) => {
|
||||
@ -38,14 +38,12 @@ class ContractListActions {
|
||||
}
|
||||
|
||||
removeContract(contractId){
|
||||
return Q.Promise((resolve, reject) => {
|
||||
return Q.Promise( (resolve, reject) => {
|
||||
OwnershipFetcher.deleteContract(contractId)
|
||||
.then((res) => {
|
||||
console.log('Contract deleted');
|
||||
resolve(res);
|
||||
})
|
||||
.catch( (err) => {
|
||||
console.log('Error while deleting');
|
||||
console.logGlobal(err);
|
||||
reject(err);
|
||||
});
|
||||
|
68
js/actions/notification_actions.js
Normal file
68
js/actions/notification_actions.js
Normal file
@ -0,0 +1,68 @@
|
||||
'use strict';
|
||||
|
||||
import alt from '../alt';
|
||||
import Q from 'q';
|
||||
|
||||
import NotificationFetcher from '../fetchers/notification_fetcher';
|
||||
|
||||
class NotificationActions {
|
||||
constructor() {
|
||||
this.generateActions(
|
||||
'updatePieceListNotifications',
|
||||
'updateEditionListNotifications',
|
||||
'updateEditionNotifications',
|
||||
'updatePieceNotifications',
|
||||
'updateContractAgreementListNotifications'
|
||||
);
|
||||
}
|
||||
|
||||
fetchPieceListNotifications() {
|
||||
NotificationFetcher
|
||||
.fetchPieceListNotifications()
|
||||
.then((res) => {
|
||||
this.actions.updatePieceListNotifications(res);
|
||||
})
|
||||
.catch((err) => console.logGlobal(err));
|
||||
}
|
||||
|
||||
fetchPieceNotifications(pieceId) {
|
||||
NotificationFetcher
|
||||
.fetchPieceNotifications(pieceId)
|
||||
.then((res) => {
|
||||
this.actions.updatePieceNotifications(res);
|
||||
})
|
||||
.catch((err) => console.logGlobal(err));
|
||||
}
|
||||
|
||||
fetchEditionListNotifications() {
|
||||
NotificationFetcher
|
||||
.fetchEditionListNotifications()
|
||||
.then((res) => {
|
||||
this.actions.updateEditionListNotifications(res);
|
||||
})
|
||||
.catch((err) => console.logGlobal(err));
|
||||
}
|
||||
|
||||
fetchEditionNotifications(editionId) {
|
||||
NotificationFetcher
|
||||
.fetchEditionNotifications(editionId)
|
||||
.then((res) => {
|
||||
this.actions.updateEditionNotifications(res);
|
||||
})
|
||||
.catch((err) => console.logGlobal(err));
|
||||
}
|
||||
|
||||
fetchContractAgreementListNotifications() {
|
||||
return Q.Promise((resolve, reject) => {
|
||||
NotificationFetcher
|
||||
.fetchContractAgreementListNotifications()
|
||||
.then((res) => {
|
||||
this.actions.updateContractAgreementListNotifications(res);
|
||||
resolve(res);
|
||||
})
|
||||
.catch((err) => console.logGlobal(err));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export default alt.createActions(NotificationActions);
|
@ -26,6 +26,7 @@ import EventActions from './actions/event_actions';
|
||||
import GoogleAnalyticsHandler from './third_party/ga';
|
||||
import RavenHandler from './third_party/raven';
|
||||
import IntercomHandler from './third_party/intercom';
|
||||
import NotificationsHandler from './third_party/notifications';
|
||||
/* eslint-enable */
|
||||
|
||||
initLogging();
|
||||
@ -71,9 +72,8 @@ class AppGateway {
|
||||
type = settings.type;
|
||||
subdomain = settings.subdomain;
|
||||
}
|
||||
|
||||
EventActions.applicationWillBoot(settings);
|
||||
Router.run(getRoutes(type, subdomain), Router.HistoryLocation, (App) => {
|
||||
window.appRouter = Router.run(getRoutes(type, subdomain), Router.HistoryLocation, (App) => {
|
||||
React.render(
|
||||
<App />,
|
||||
document.getElementById('main')
|
||||
|
@ -160,7 +160,7 @@ let AccordionListItemTableEditions = React.createClass({
|
||||
let content = item.acl;
|
||||
return {
|
||||
'content': content,
|
||||
'requestAction': item.request_action
|
||||
'notifications': item.notifications
|
||||
}; },
|
||||
'acl',
|
||||
getLangText('Actions'),
|
||||
|
@ -61,8 +61,7 @@ let AccordionListItemWallet = React.createClass({
|
||||
},
|
||||
|
||||
getGlyphicon(){
|
||||
if ((this.props.content.request_action && this.props.content.request_action.length > 0) ||
|
||||
(this.props.content.request_action_editions)){
|
||||
if ((this.props.content.notifications && this.props.content.notifications.length > 0)){
|
||||
return (
|
||||
<OverlayTrigger
|
||||
delay={500}
|
||||
|
@ -1,27 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
import React from 'react';
|
||||
|
||||
let ButtonSubmitOrClose = React.createClass({
|
||||
propTypes: {
|
||||
submitted: React.PropTypes.bool.isRequired,
|
||||
text: React.PropTypes.string.isRequired
|
||||
},
|
||||
|
||||
render() {
|
||||
if (this.props.submitted){
|
||||
return (
|
||||
<div className="modal-footer">
|
||||
<img src="https://s3-us-west-2.amazonaws.com/ascribe0/media/thumbnails/ascribe_animated_medium.gif" />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<div className="modal-footer">
|
||||
<button type="submit" className="btn btn-ascribe-inv">{this.props.text}</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
export default ButtonSubmitOrClose;
|
@ -1,32 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import AppConstants from '../../constants/application_constants';
|
||||
import { getLangText } from '../../utils/lang_utils.js';
|
||||
|
||||
let ButtonSubmitOrClose = React.createClass({
|
||||
propTypes: {
|
||||
submitted: React.PropTypes.bool.isRequired,
|
||||
text: React.PropTypes.string.isRequired,
|
||||
onClose: React.PropTypes.func.isRequired
|
||||
},
|
||||
|
||||
render() {
|
||||
if (this.props.submitted){
|
||||
return (
|
||||
<div className="modal-footer">
|
||||
<img src={AppConstants.baseUrl + 'static/img/ascribe_animated_medium.gif'} />
|
||||
</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}>{getLangText('CLOSE')}</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
export default ButtonSubmitOrClose;
|
@ -14,7 +14,7 @@ import CoaActions from '../../actions/coa_actions';
|
||||
import CoaStore from '../../stores/coa_store';
|
||||
import PieceListActions from '../../actions/piece_list_actions';
|
||||
import PieceListStore from '../../stores/piece_list_store';
|
||||
import EditionListActions from '../../actions/edition_list_actions';
|
||||
import EditionListActions from '../../actions/edition_list_actions';;
|
||||
|
||||
import HistoryIterator from './history_iterator';
|
||||
|
||||
@ -234,13 +234,15 @@ let EditionSummary = React.createClass({
|
||||
|
||||
getActions(){
|
||||
let actions = null;
|
||||
if (this.props.edition.request_action && this.props.edition.request_action.length > 0){
|
||||
if (this.props.edition &&
|
||||
this.props.edition.notifications &&
|
||||
this.props.edition.notifications.length > 0){
|
||||
actions = (
|
||||
<ListRequestActions
|
||||
pieceOrEditions={[this.props.edition]}
|
||||
currentUser={this.props.currentUser}
|
||||
handleSuccess={this.showNotification}
|
||||
requestActions={this.props.edition.request_action}/>);
|
||||
notifications={this.props.edition.notifications}/>);
|
||||
}
|
||||
|
||||
else {
|
||||
|
@ -61,8 +61,7 @@ let EditionContainer = React.createClass({
|
||||
},
|
||||
|
||||
render() {
|
||||
console.log(this.state);
|
||||
if('title' in this.state.edition) {
|
||||
if(this.state.edition && this.state.edition.title) {
|
||||
return (
|
||||
<Edition
|
||||
edition={this.state.edition}
|
||||
|
@ -39,7 +39,7 @@ let FurtherDetailsFileuploader = React.createClass({
|
||||
return null;
|
||||
}
|
||||
|
||||
let otherDataIds = this.props.otherData ? this.props.otherData.map((data)=>{return data.id; }).join() : null;
|
||||
let otherDataIds = this.props.otherData ? this.props.otherData.map((data) => data.id).join() : null;
|
||||
|
||||
return (
|
||||
<Property
|
||||
|
@ -172,17 +172,16 @@ let PieceContainer = React.createClass({
|
||||
return {'id': this.state.piece.id};
|
||||
},
|
||||
|
||||
getActions(){
|
||||
getActions() {
|
||||
if (this.state.piece &&
|
||||
this.state.piece.request_action &&
|
||||
this.state.piece.request_action.length > 0) {
|
||||
this.state.piece.notifications &&
|
||||
this.state.piece.notifications.length > 0) {
|
||||
return (
|
||||
<ListRequestActions
|
||||
pieceOrEditions={this.state.piece}
|
||||
currentUser={this.state.currentUser}
|
||||
handleSuccess={this.loadPiece}
|
||||
requestActions={this.state.piece.request_action}/>
|
||||
);
|
||||
notifications={this.state.piece.notifications}/>);
|
||||
}
|
||||
else {
|
||||
return (
|
||||
@ -206,7 +205,7 @@ let PieceContainer = React.createClass({
|
||||
},
|
||||
|
||||
render() {
|
||||
if('title' in this.state.piece) {
|
||||
if(this.state.piece && this.state.piece.title) {
|
||||
return (
|
||||
<Piece
|
||||
piece={this.state.piece}
|
||||
|
@ -41,7 +41,9 @@ let Form = React.createClass({
|
||||
// It will make use of the GlobalNotification
|
||||
isInline: React.PropTypes.bool,
|
||||
|
||||
autoComplete: React.PropTypes.string
|
||||
autoComplete: React.PropTypes.string,
|
||||
|
||||
onReset: React.PropTypes.func
|
||||
},
|
||||
|
||||
getDefaultProps() {
|
||||
@ -60,9 +62,15 @@ let Form = React.createClass({
|
||||
};
|
||||
},
|
||||
|
||||
reset(){
|
||||
for (let ref in this.refs){
|
||||
if (typeof this.refs[ref].reset === 'function'){
|
||||
reset() {
|
||||
// If onReset prop is defined from outside,
|
||||
// notify component that a form reset is happening.
|
||||
if(this.props.onReset && typeof this.props.onReset === 'function') {
|
||||
this.props.onReset();
|
||||
}
|
||||
|
||||
for(let ref in this.refs) {
|
||||
if (this.refs[ref].reset && typeof this.refs[ref].reset === 'function'){
|
||||
this.refs[ref].reset();
|
||||
}
|
||||
}
|
||||
@ -70,7 +78,6 @@ let Form = React.createClass({
|
||||
},
|
||||
|
||||
submit(event){
|
||||
|
||||
if(event) {
|
||||
event.preventDefault();
|
||||
}
|
||||
@ -79,7 +86,7 @@ let Form = React.createClass({
|
||||
this.clearErrors();
|
||||
|
||||
// selecting http method based on props
|
||||
if(this[this.props.method]) {
|
||||
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 + ')');
|
||||
@ -93,6 +100,20 @@ let Form = React.createClass({
|
||||
.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())
|
||||
@ -100,13 +121,13 @@ let Form = React.createClass({
|
||||
.catch(this.handleError);
|
||||
},
|
||||
|
||||
getFormData(){
|
||||
getFormData() {
|
||||
let data = {};
|
||||
for (let ref in this.refs){
|
||||
for(let ref in this.refs){
|
||||
data[this.refs[ref].props.name] = this.refs[ref].state.value;
|
||||
}
|
||||
|
||||
if ('getFormData' in this.props){
|
||||
if (this.props.getFormData && typeof this.props.getFormData === 'function'){
|
||||
data = mergeOptionsWithDuplicates(data, this.props.getFormData());
|
||||
}
|
||||
|
||||
@ -118,11 +139,12 @@ let Form = React.createClass({
|
||||
},
|
||||
|
||||
handleSuccess(response){
|
||||
if ('handleSuccess' in this.props){
|
||||
if(this.props.handleSuccess && typeof this.props.handleSuccess === 'function') {
|
||||
this.props.handleSuccess(response);
|
||||
}
|
||||
for (var ref in this.refs){
|
||||
if ('handleSuccess' in this.refs[ref]){
|
||||
|
||||
for(let ref in this.refs) {
|
||||
if(this.refs[ref] && this.refs[ref].handleSuccess && typeof this.refs[ref].handleSuccess === 'function'){
|
||||
this.refs[ref].handleSuccess();
|
||||
}
|
||||
}
|
||||
@ -134,9 +156,9 @@ let Form = React.createClass({
|
||||
|
||||
handleError(err){
|
||||
if (err.json) {
|
||||
for (var input in err.json.errors){
|
||||
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]);
|
||||
this.refs[input].setErrors(err.json.errors[input]);
|
||||
} else {
|
||||
this.setState({errors: this.state.errors.concat(err.json.errors[input])});
|
||||
}
|
||||
@ -164,8 +186,8 @@ let Form = React.createClass({
|
||||
},
|
||||
|
||||
clearErrors(){
|
||||
for (var ref in this.refs){
|
||||
if ('clearErrors' in this.refs[ref]){
|
||||
for(let ref in this.refs){
|
||||
if (this.refs[ref] && this.refs[ref].clearErrors && typeof this.refs[ref].clearErrors === 'function'){
|
||||
this.refs[ref].clearErrors();
|
||||
}
|
||||
}
|
||||
@ -185,8 +207,16 @@ let Form = React.createClass({
|
||||
buttons = (
|
||||
<div className="row" style={{margin: 0}}>
|
||||
<p className="pull-right">
|
||||
<Button className="btn btn-default btn-sm ascribe-margin-1px" type="submit">{this.props.buttonSubmitText}</Button>
|
||||
<Button className="btn btn-danger btn-delete btn-sm ascribe-margin-1px" onClick={this.reset}>CANCEL</Button>
|
||||
<Button
|
||||
className="btn btn-default btn-sm ascribe-margin-1px"
|
||||
type="submit">
|
||||
{this.props.buttonSubmitText}
|
||||
</Button>
|
||||
<Button
|
||||
className="btn btn-danger btn-delete btn-sm ascribe-margin-1px"
|
||||
type="reset">
|
||||
CANCEL
|
||||
</Button>
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
@ -251,6 +281,7 @@ let Form = React.createClass({
|
||||
role="form"
|
||||
className={className}
|
||||
onSubmit={this.submit}
|
||||
onReset={this.reset}
|
||||
autoComplete={this.props.autoComplete}>
|
||||
{this.getFakeAutocompletableInputs()}
|
||||
{this.getErrors()}
|
||||
|
@ -35,7 +35,7 @@ let ContractAgreementForm = React.createClass({
|
||||
|
||||
componentDidMount() {
|
||||
ContractListStore.listen(this.onChange);
|
||||
ContractListActions.fetchContractList();
|
||||
ContractListActions.fetchContractList({is_active: 'True'});
|
||||
},
|
||||
|
||||
componentWillUnmount() {
|
||||
@ -50,9 +50,11 @@ let ContractAgreementForm = React.createClass({
|
||||
this.setState({selectedContract: event.target.selectedIndex});
|
||||
},
|
||||
|
||||
handleSubmitSuccess(response) {
|
||||
let notification = new GlobalNotificationModel(response.notification, 'success', 10000);
|
||||
handleSubmitSuccess() {
|
||||
let notification = 'Contract agreement send';
|
||||
notification = new GlobalNotificationModel(notification, 'success', 10000);
|
||||
GlobalNotificationActions.appendGlobalNotification(notification);
|
||||
this.refs.form.reset();
|
||||
},
|
||||
|
||||
getFormData(){
|
||||
@ -60,8 +62,8 @@ let ContractAgreementForm = React.createClass({
|
||||
},
|
||||
|
||||
getContracts() {
|
||||
if (this.state.contractList && this.state.contractList.count > 0) {
|
||||
let contractList = this.state.contractList.results;
|
||||
if (this.state.contractList && this.state.contractList.length > 0) {
|
||||
let contractList = this.state.contractList;
|
||||
return (
|
||||
<Property
|
||||
name='contract'
|
||||
@ -81,7 +83,7 @@ let ContractAgreementForm = React.createClass({
|
||||
<option
|
||||
name={i}
|
||||
key={i}
|
||||
value={ contract.name }>
|
||||
value={ contract.id }>
|
||||
{ contract.name }
|
||||
</option>
|
||||
);
|
||||
@ -99,7 +101,7 @@ let ContractAgreementForm = React.createClass({
|
||||
ref='form'
|
||||
url={ApiUrls.ownership_contract_agreements}
|
||||
getFormData={this.getFormData}
|
||||
handleSuccess={this.props.handleSuccess}
|
||||
handleSuccess={this.handleSubmitSuccess}
|
||||
buttons={<button
|
||||
type="submit"
|
||||
className="btn ascribe-btn ascribe-btn-login">
|
||||
|
@ -16,6 +16,8 @@ import ReactS3FineUploader from '../ascribe_uploader/react_s3_fine_uploader';
|
||||
import AppConstants from '../../constants/application_constants';
|
||||
import ApiUrls from '../../constants/api_urls';
|
||||
|
||||
|
||||
|
||||
import { getLangText } from '../../utils/lang_utils';
|
||||
import { getCookie } from '../../utils/fetch_api_utils';
|
||||
import { formSubmissionValidation } from '../ascribe_uploader/react_s3_fine_uploader_utils';
|
||||
@ -48,17 +50,17 @@ let CreateContractForm = React.createClass({
|
||||
},
|
||||
|
||||
handleCreateSuccess(response) {
|
||||
ContractListActions.fetchContractList({is_active: 'True'});
|
||||
let notification = new GlobalNotificationModel(getLangText('Contract %s successfully created', response.name), 'success', 5000);
|
||||
GlobalNotificationActions.appendGlobalNotification(notification);
|
||||
|
||||
// also refresh contract lists for the rest of the contract settings page
|
||||
ContractListActions.fetchContractList();
|
||||
this.refs.form.reset();
|
||||
},
|
||||
|
||||
|
||||
render() {
|
||||
return (
|
||||
<Form
|
||||
ref='form'
|
||||
url={ApiUrls.ownership_contract_list}
|
||||
getFormData={this.getFormData}
|
||||
handleSuccess={this.handleCreateSuccess}
|
||||
@ -78,6 +80,7 @@ let CreateContractForm = React.createClass({
|
||||
<Property
|
||||
label="Contract file">
|
||||
<ReactS3FineUploader
|
||||
ref='uploader'
|
||||
keyRoutine={{
|
||||
url: AppConstants.serverUrl + 's3/key/',
|
||||
fileClass: 'contract'
|
||||
@ -118,7 +121,7 @@ let CreateContractForm = React.createClass({
|
||||
required/>
|
||||
</Property>
|
||||
<Property
|
||||
name="public"
|
||||
name="is_public"
|
||||
className="ascribe-settings-property-collapsible-toggle"
|
||||
style={{paddingBottom: 0}}>
|
||||
<InputCheckbox>
|
||||
|
@ -69,10 +69,11 @@ let LoanForm = React.createClass({
|
||||
},
|
||||
|
||||
handleOnChange(event) {
|
||||
let potentialEmail = event.target.value;
|
||||
|
||||
if(potentialEmail.match(/.*@.*/)) {
|
||||
ContractActions.fetchContract(potentialEmail);
|
||||
// event.target.value is the submitted email of the loanee
|
||||
if(event && event.target && event.target.value && event.target.value.match(/.*@.*/)) {
|
||||
ContractActions.fetchContract(event.target.value);
|
||||
} else {
|
||||
ContractActions.flushContract();
|
||||
}
|
||||
},
|
||||
|
||||
@ -143,6 +144,7 @@ let LoanForm = React.createClass({
|
||||
ref='form'
|
||||
url={this.props.url}
|
||||
getFormData={this.getFormData}
|
||||
onReset={this.handleOnChange}
|
||||
handleSuccess={this.props.handleSuccess}
|
||||
buttons={this.getButtons()}
|
||||
spinner={
|
||||
|
@ -7,16 +7,13 @@ import UserActions from '../../actions/user_actions';
|
||||
|
||||
import Form from './form';
|
||||
import Property from './property';
|
||||
import InputFineUploader from './input_fineuploader';
|
||||
|
||||
import ReactS3FineUploader from '../ascribe_uploader/react_s3_fine_uploader';
|
||||
|
||||
import AppConstants from '../../constants/application_constants';
|
||||
import ApiUrls from '../../constants/api_urls';
|
||||
|
||||
import { getCookie } from '../../utils/fetch_api_utils';
|
||||
import { getLangText } from '../../utils/lang_utils';
|
||||
import { mergeOptions } from '../../utils/general_utils';
|
||||
import { formSubmissionValidation } from '../ascribe_uploader/react_s3_fine_uploader_utils';
|
||||
import { isReadyForFormSubmission } from '../ascribe_uploader/react_s3_fine_uploader_utils';
|
||||
|
||||
|
||||
let RegisterPieceForm = React.createClass({
|
||||
@ -45,7 +42,6 @@ let RegisterPieceForm = React.createClass({
|
||||
getInitialState(){
|
||||
return mergeOptions(
|
||||
{
|
||||
digitalWorkKey: null,
|
||||
isUploadReady: false
|
||||
},
|
||||
UserStore.getState()
|
||||
@ -65,18 +61,6 @@ let RegisterPieceForm = React.createClass({
|
||||
this.setState(state);
|
||||
},
|
||||
|
||||
getFormData(){
|
||||
return {
|
||||
digital_work_key: this.state.digitalWorkKey
|
||||
};
|
||||
},
|
||||
|
||||
submitKey(key){
|
||||
this.setState({
|
||||
digitalWorkKey: key
|
||||
});
|
||||
},
|
||||
|
||||
setIsUploadReady(isReady) {
|
||||
this.setState({
|
||||
isUploadReady: isReady
|
||||
@ -94,14 +78,15 @@ let RegisterPieceForm = React.createClass({
|
||||
className="ascribe-form-bordered"
|
||||
ref='form'
|
||||
url={ApiUrls.pieces_list}
|
||||
getFormData={this.getFormData}
|
||||
handleSuccess={this.props.handleSuccess}
|
||||
buttons={<button
|
||||
type="submit"
|
||||
className="btn ascribe-btn ascribe-btn-login"
|
||||
disabled={!this.state.isUploadReady || this.props.disabled}>
|
||||
{this.props.submitMessage}
|
||||
</button>}
|
||||
buttons={
|
||||
<button
|
||||
type="submit"
|
||||
className="btn ascribe-btn ascribe-btn-login"
|
||||
disabled={!this.state.isUploadReady || this.props.disabled}>
|
||||
{this.props.submitMessage}
|
||||
</button>
|
||||
}
|
||||
spinner={
|
||||
<span className="btn ascribe-btn ascribe-btn-login ascribe-btn-login-spinner">
|
||||
<img src="https://s3-us-west-2.amazonaws.com/ascribe0/media/thumbnails/ascribe_animated_medium.gif" />
|
||||
@ -111,11 +96,11 @@ let RegisterPieceForm = React.createClass({
|
||||
<h3>{this.props.headerMessage}</h3>
|
||||
</div>
|
||||
<Property
|
||||
name="digital_work_key"
|
||||
ignoreFocus={true}>
|
||||
<FileUploader
|
||||
submitKey={this.submitKey}
|
||||
<InputFineUploader
|
||||
setIsUploadReady={this.setIsUploadReady}
|
||||
isReadyForFormSubmission={formSubmissionValidation.atLeastOneUploadedFile}
|
||||
isReadyForFormSubmission={isReadyForFormSubmission}
|
||||
isFineUploaderActive={this.props.isFineUploaderActive}
|
||||
onLoggedOut={this.props.onLoggedOut}
|
||||
editable={this.props.isFineUploaderEditable}
|
||||
@ -152,73 +137,4 @@ let RegisterPieceForm = React.createClass({
|
||||
}
|
||||
});
|
||||
|
||||
let FileUploader = React.createClass({
|
||||
propTypes: {
|
||||
setIsUploadReady: React.PropTypes.func,
|
||||
submitKey: React.PropTypes.func,
|
||||
isReadyForFormSubmission: React.PropTypes.func,
|
||||
onClick: React.PropTypes.func,
|
||||
|
||||
// isFineUploaderActive is used to lock react fine uploader in case
|
||||
// a user is actually not logged in already to prevent him from droping files
|
||||
// before login in
|
||||
isFineUploaderActive: React.PropTypes.bool,
|
||||
onLoggedOut: React.PropTypes.func,
|
||||
editable: React.PropTypes.bool,
|
||||
enableLocalHashing: React.PropTypes.bool,
|
||||
|
||||
// provided by Property
|
||||
disabled: React.PropTypes.bool
|
||||
},
|
||||
|
||||
render() {
|
||||
|
||||
let editable = this.props.isFineUploaderActive;
|
||||
|
||||
// if disabled is actually set by property, we want to override
|
||||
// isFineUploaderActive
|
||||
if(typeof this.props.disabled !== 'undefined') {
|
||||
editable = !this.props.disabled;
|
||||
}
|
||||
|
||||
|
||||
return (
|
||||
<ReactS3FineUploader
|
||||
onClick={this.props.onClick}
|
||||
keyRoutine={{
|
||||
url: AppConstants.serverUrl + 's3/key/',
|
||||
fileClass: 'digitalwork'
|
||||
}}
|
||||
createBlobRoutine={{
|
||||
url: ApiUrls.blob_digitalworks
|
||||
}}
|
||||
validation={{
|
||||
itemLimit: 100000,
|
||||
sizeLimit: '25000000000'
|
||||
}}
|
||||
submitKey={this.props.submitKey}
|
||||
setIsUploadReady={this.props.setIsUploadReady}
|
||||
isReadyForFormSubmission={this.props.isReadyForFormSubmission}
|
||||
areAssetsDownloadable={false}
|
||||
areAssetsEditable={editable}
|
||||
signature={{
|
||||
endpoint: AppConstants.serverUrl + 's3/signature/',
|
||||
customHeaders: {
|
||||
'X-CSRFToken': getCookie(AppConstants.csrftoken)
|
||||
}
|
||||
}}
|
||||
deleteFile={{
|
||||
enabled: true,
|
||||
method: 'DELETE',
|
||||
endpoint: AppConstants.serverUrl + 's3/delete',
|
||||
customHeaders: {
|
||||
'X-CSRFToken': getCookie(AppConstants.csrftoken)
|
||||
}
|
||||
}}
|
||||
onInactive={this.props.onLoggedOut}
|
||||
enableLocalHashing={this.props.enableLocalHashing} />
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
export default RegisterPieceForm;
|
||||
|
@ -6,7 +6,7 @@ import AclButton from './../ascribe_buttons/acl_button';
|
||||
import ActionPanel from '../ascribe_panel/action_panel';
|
||||
import Form from './form';
|
||||
|
||||
import PieceListActions from '../../actions/piece_list_actions';
|
||||
import NotificationActions from '../../actions/notification_actions';
|
||||
|
||||
import GlobalNotificationModel from '../../models/global_notification_model';
|
||||
import GlobalNotificationActions from '../../actions/global_notification_actions';
|
||||
@ -22,8 +22,7 @@ let RequestActionForm = React.createClass({
|
||||
React.PropTypes.object,
|
||||
React.PropTypes.array
|
||||
]).isRequired,
|
||||
requestAction: React.PropTypes.string,
|
||||
requestUser: React.PropTypes.string,
|
||||
notifications: React.PropTypes.object,
|
||||
currentUser: React.PropTypes.object,
|
||||
handleSuccess: React.PropTypes.func
|
||||
},
|
||||
@ -35,19 +34,19 @@ let RequestActionForm = React.createClass({
|
||||
getUrls() {
|
||||
let urls = {};
|
||||
|
||||
if (this.props.requestAction === 'consign'){
|
||||
if (this.props.notifications.action === 'consign'){
|
||||
urls.accept = ApiUrls.ownership_consigns_confirm;
|
||||
urls.deny = ApiUrls.ownership_consigns_deny;
|
||||
} else if (this.props.requestAction === 'unconsign'){
|
||||
} else if (this.props.notifications.action === 'unconsign'){
|
||||
urls.accept = ApiUrls.ownership_unconsigns;
|
||||
urls.deny = ApiUrls.ownership_unconsigns_deny;
|
||||
} else if (this.props.requestAction === 'loan' && !this.isPiece()){
|
||||
} else if (this.props.notifications.action === 'loan' && !this.isPiece()){
|
||||
urls.accept = ApiUrls.ownership_loans_confirm;
|
||||
urls.deny = ApiUrls.ownership_loans_deny;
|
||||
} else if (this.props.requestAction === 'loan' && this.isPiece()){
|
||||
} else if (this.props.notifications.action === 'loan' && this.isPiece()){
|
||||
urls.accept = ApiUrls.ownership_loans_pieces_confirm;
|
||||
urls.deny = ApiUrls.ownership_loans_pieces_deny;
|
||||
} else if (this.props.requestAction === 'loan_request' && this.isPiece()){
|
||||
} else if (this.props.notifications.action === 'loan_request' && this.isPiece()){
|
||||
urls.accept = ApiUrls.ownership_loans_pieces_request_confirm;
|
||||
urls.deny = ApiUrls.ownership_loans_pieces_request_deny;
|
||||
}
|
||||
@ -70,8 +69,8 @@ let RequestActionForm = React.createClass({
|
||||
return () => {
|
||||
let message = getLangText('You have successfully') + ' ' + option + ' the ' + action + ' request ' + getLangText('from') + ' ' + owner;
|
||||
|
||||
let notification = new GlobalNotificationModel(message, 'success');
|
||||
GlobalNotificationActions.appendGlobalNotification(notification);
|
||||
let notifications = new GlobalNotificationModel(message, 'success');
|
||||
GlobalNotificationActions.appendGlobalNotification(notifications);
|
||||
|
||||
this.handleSuccess();
|
||||
|
||||
@ -79,27 +78,27 @@ let RequestActionForm = React.createClass({
|
||||
},
|
||||
|
||||
handleSuccess() {
|
||||
PieceListActions.fetchPieceRequestActions();
|
||||
if (this.isPiece()){
|
||||
NotificationActions.fetchPieceListNotifications();
|
||||
}
|
||||
else {
|
||||
NotificationActions.fetchEditionListNotifications();
|
||||
}
|
||||
if(this.props.handleSuccess) {
|
||||
this.props.handleSuccess();
|
||||
}
|
||||
},
|
||||
|
||||
getContent() {
|
||||
let pieceOrEditionStr = this.isPiece() ? getLangText('this work%s', '.') : getLangText('this edition%s', '.');
|
||||
let message = this.props.requestUser + ' ' + getLangText('requests you') + ' ' + this.props.requestAction + ' ' + pieceOrEditionStr;
|
||||
if (this.props.requestAction === 'loan_request'){
|
||||
message = this.props.requestUser + ' ' + getLangText('requests you to loan') + ' ' + pieceOrEditionStr;
|
||||
}
|
||||
return (
|
||||
<span>
|
||||
{message}
|
||||
{this.props.notifications.action_str + ' by ' + this.props.notifications.by}
|
||||
</span>
|
||||
);
|
||||
},
|
||||
|
||||
getAcceptButtonForm(urls) {
|
||||
if(this.props.requestAction === 'unconsign') {
|
||||
if(this.props.notifications.action === 'unconsign') {
|
||||
return (
|
||||
<AclButton
|
||||
availableAcls={{'acl_unconsign': true}}
|
||||
@ -109,7 +108,7 @@ let RequestActionForm = React.createClass({
|
||||
currentUser={this.props.currentUser}
|
||||
handleSuccess={this.handleSuccess} />
|
||||
);
|
||||
} else if(this.props.requestAction === 'loan_request') {
|
||||
} else if(this.props.notifications.action === 'loan_request') {
|
||||
return (
|
||||
<AclButton
|
||||
availableAcls={{'acl_loan_request': true}}
|
||||
@ -126,7 +125,7 @@ let RequestActionForm = React.createClass({
|
||||
url={urls.accept}
|
||||
getFormData={this.getFormData}
|
||||
handleSuccess={
|
||||
this.showNotification(getLangText('accepted'), this.props.requestAction, this.props.requestUser)
|
||||
this.showNotification(getLangText('accepted'), this.props.notifications.action, this.props.notifications.by)
|
||||
}
|
||||
isInline={true}
|
||||
className='inline pull-right'>
|
||||
@ -151,7 +150,7 @@ let RequestActionForm = React.createClass({
|
||||
isInline={true}
|
||||
getFormData={this.getFormData}
|
||||
handleSuccess={
|
||||
this.showNotification(getLangText('denied'), this.props.requestAction, this.props.requestUser)
|
||||
this.showNotification(getLangText('denied'), this.props.notifications.action, this.props.notifications.by)
|
||||
}
|
||||
className='inline pull-right'>
|
||||
<button
|
||||
|
@ -18,7 +18,8 @@ let InputDate = React.createClass({
|
||||
|
||||
getInitialState() {
|
||||
return {
|
||||
value: null
|
||||
value: null,
|
||||
value_moment: null
|
||||
};
|
||||
},
|
||||
|
||||
@ -45,6 +46,10 @@ let InputDate = React.createClass({
|
||||
});
|
||||
},
|
||||
|
||||
reset() {
|
||||
this.setState(this.getInitialState());
|
||||
},
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
|
95
js/components/ascribe_forms/input_fineuploader.js
Normal file
95
js/components/ascribe_forms/input_fineuploader.js
Normal file
@ -0,0 +1,95 @@
|
||||
'use strict';
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import ReactS3FineUploader from '../ascribe_uploader/react_s3_fine_uploader';
|
||||
|
||||
import AppConstants from '../../constants/application_constants';
|
||||
import ApiUrls from '../../constants/api_urls';
|
||||
|
||||
import { getCookie } from '../../utils/fetch_api_utils';
|
||||
|
||||
let InputFileUploader = React.createClass({
|
||||
propTypes: {
|
||||
setIsUploadReady: React.PropTypes.func,
|
||||
isReadyForFormSubmission: React.PropTypes.func,
|
||||
onClick: React.PropTypes.func,
|
||||
|
||||
// isFineUploaderActive is used to lock react fine uploader in case
|
||||
// a user is actually not logged in already to prevent him from droping files
|
||||
// before login in
|
||||
isFineUploaderActive: React.PropTypes.bool,
|
||||
onLoggedOut: React.PropTypes.func,
|
||||
editable: React.PropTypes.bool,
|
||||
enableLocalHashing: React.PropTypes.bool,
|
||||
|
||||
// provided by Property
|
||||
disabled: React.PropTypes.bool
|
||||
},
|
||||
|
||||
getInitialState() {
|
||||
return {
|
||||
value: null
|
||||
};
|
||||
},
|
||||
|
||||
submitKey(key){
|
||||
this.setState({
|
||||
value: key
|
||||
});
|
||||
},
|
||||
|
||||
reset() {
|
||||
this.refs.fineuploader.reset();
|
||||
},
|
||||
|
||||
render() {
|
||||
let editable = this.props.isFineUploaderActive;
|
||||
|
||||
// if disabled is actually set by property, we want to override
|
||||
// isFineUploaderActive
|
||||
if(typeof this.props.disabled !== 'undefined') {
|
||||
editable = !this.props.disabled;
|
||||
}
|
||||
|
||||
return (
|
||||
<ReactS3FineUploader
|
||||
ref="fineuploader"
|
||||
onClick={this.props.onClick}
|
||||
keyRoutine={{
|
||||
url: AppConstants.serverUrl + 's3/key/',
|
||||
fileClass: 'digitalwork'
|
||||
}}
|
||||
createBlobRoutine={{
|
||||
url: ApiUrls.blob_digitalworks
|
||||
}}
|
||||
submitKey={this.submitKey}
|
||||
validation={{
|
||||
itemLimit: 100000,
|
||||
sizeLimit: '25000000000'
|
||||
}}
|
||||
setIsUploadReady={this.props.setIsUploadReady}
|
||||
isReadyForFormSubmission={this.props.isReadyForFormSubmission}
|
||||
areAssetsDownloadable={false}
|
||||
areAssetsEditable={editable}
|
||||
signature={{
|
||||
endpoint: AppConstants.serverUrl + 's3/signature/',
|
||||
customHeaders: {
|
||||
'X-CSRFToken': getCookie(AppConstants.csrftoken)
|
||||
}
|
||||
}}
|
||||
deleteFile={{
|
||||
enabled: true,
|
||||
method: 'DELETE',
|
||||
endpoint: AppConstants.serverUrl + 's3/delete',
|
||||
customHeaders: {
|
||||
'X-CSRFToken': getCookie(AppConstants.csrftoken)
|
||||
}
|
||||
}}
|
||||
onInactive={this.props.onLoggedOut}
|
||||
enableLocalHashing={this.props.enableLocalHashing} />
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
export default InputFileUploader;
|
@ -4,6 +4,7 @@ import React from 'react';
|
||||
|
||||
import TextareaAutosize from 'react-textarea-autosize';
|
||||
|
||||
|
||||
let InputTextAreaToggable = React.createClass({
|
||||
propTypes: {
|
||||
editable: React.PropTypes.bool.isRequired,
|
||||
@ -17,14 +18,17 @@ let InputTextAreaToggable = React.createClass({
|
||||
value: this.props.defaultValue
|
||||
};
|
||||
},
|
||||
|
||||
handleChange(event) {
|
||||
this.setState({value: event.target.value});
|
||||
this.props.onChange(event);
|
||||
},
|
||||
|
||||
render() {
|
||||
let className = 'form-control ascribe-textarea';
|
||||
let textarea = null;
|
||||
if (this.props.editable){
|
||||
|
||||
if(this.props.editable) {
|
||||
className = className + ' ascribe-textarea-editable';
|
||||
textarea = (
|
||||
<TextareaAutosize
|
||||
@ -37,10 +41,10 @@ let InputTextAreaToggable = React.createClass({
|
||||
onBlur={this.props.onBlur}
|
||||
placeholder={this.props.placeholder} />
|
||||
);
|
||||
}
|
||||
else{
|
||||
} else {
|
||||
textarea = <pre className="ascribe-pre">{this.state.value}</pre>;
|
||||
}
|
||||
|
||||
return textarea;
|
||||
}
|
||||
});
|
||||
|
@ -12,20 +12,19 @@ let ListRequestActions = React.createClass({
|
||||
]).isRequired,
|
||||
currentUser: React.PropTypes.object.isRequired,
|
||||
handleSuccess: React.PropTypes.func.isRequired,
|
||||
requestActions: React.PropTypes.array.isRequired
|
||||
notifications: React.PropTypes.array.isRequired
|
||||
},
|
||||
|
||||
render () {
|
||||
if (this.props.requestActions &&
|
||||
this.props.requestActions.length > 0) {
|
||||
if (this.props.notifications &&
|
||||
this.props.notifications.length > 0) {
|
||||
return (
|
||||
<div>
|
||||
{this.props.requestActions.map((requestAction) =>
|
||||
{this.props.notifications.map((notification) =>
|
||||
<RequestActionForm
|
||||
currentUser={this.props.currentUser}
|
||||
pieceOrEditions={ this.props.pieceOrEditions }
|
||||
requestAction={requestAction.action}
|
||||
requestUser={requestAction.by}
|
||||
notifications={notification}
|
||||
handleSuccess={this.props.handleSuccess}/>)}
|
||||
</div>
|
||||
);
|
||||
|
@ -29,8 +29,11 @@ let Property = React.createClass({
|
||||
handleChange: React.PropTypes.func,
|
||||
ignoreFocus: React.PropTypes.bool,
|
||||
className: React.PropTypes.string,
|
||||
|
||||
onClick: React.PropTypes.func,
|
||||
onChange: React.PropTypes.func,
|
||||
onBlur: React.PropTypes.func,
|
||||
|
||||
children: React.PropTypes.oneOfType([
|
||||
React.PropTypes.arrayOf(React.PropTypes.element),
|
||||
React.PropTypes.element
|
||||
@ -90,18 +93,28 @@ let Property = React.createClass({
|
||||
// maybe do reset by reload instead of front end state?
|
||||
this.setState({value: this.state.initialValue});
|
||||
|
||||
// resets the value of a custom react component input
|
||||
this.refs.input.state.value = this.state.initialValue;
|
||||
if (this.refs.input.state && this.refs.input.state.value) {
|
||||
// resets the value of a custom react component input
|
||||
this.refs.input.state.value = this.state.initialValue;
|
||||
}
|
||||
|
||||
// resets the value of a plain HTML5 input
|
||||
this.refs.input.getDOMNode().value = this.state.initialValue;
|
||||
|
||||
// For some inputs, reseting state.value is not enough to visually reset the
|
||||
// component.
|
||||
//
|
||||
// So if the input actually needs a visual reset, it needs to implement
|
||||
// a dedicated reset method.
|
||||
if(this.refs.input.reset && typeof this.refs.input.reset === 'function') {
|
||||
this.refs.input.reset();
|
||||
}
|
||||
},
|
||||
|
||||
handleChange(event) {
|
||||
|
||||
this.props.handleChange(event);
|
||||
if ('onChange' in this.props) {
|
||||
if (this.props.onChange && typeof this.props.onChange === 'function') {
|
||||
this.props.onChange(event);
|
||||
}
|
||||
|
||||
@ -117,7 +130,7 @@ let Property = React.createClass({
|
||||
|
||||
// if onClick is defined from the outside,
|
||||
// just call it
|
||||
if(this.props.onClick) {
|
||||
if(this.props.onClick && typeof this.props.onClick === 'function') {
|
||||
this.props.onClick();
|
||||
}
|
||||
|
||||
@ -132,7 +145,7 @@ let Property = React.createClass({
|
||||
isFocused: false
|
||||
});
|
||||
|
||||
if(this.props.onBlur) {
|
||||
if(this.props.onBlur && typeof this.props.onBlur === 'function') {
|
||||
this.props.onBlur(event);
|
||||
}
|
||||
},
|
||||
@ -190,6 +203,7 @@ let Property = React.createClass({
|
||||
},
|
||||
|
||||
render() {
|
||||
let footer = null;
|
||||
let tooltip = <span/>;
|
||||
let style = this.props.style ? mergeOptions({}, this.props.style) : {};
|
||||
|
||||
@ -199,7 +213,7 @@ let Property = React.createClass({
|
||||
{this.props.tooltip}
|
||||
</Tooltip>);
|
||||
}
|
||||
let footer = null;
|
||||
|
||||
if(this.props.footer){
|
||||
footer = (
|
||||
<div className="ascribe-property-footer">
|
||||
|
@ -42,6 +42,13 @@ let PropertyCollapsile = React.createClass({
|
||||
}
|
||||
},
|
||||
|
||||
reset() {
|
||||
// If the child input is a native HTML element, it will be reset automatically
|
||||
// by the DOM.
|
||||
// However, we need to collapse this component again.
|
||||
this.setState(this.getInitialState());
|
||||
},
|
||||
|
||||
render() {
|
||||
let tooltip = <span/>;
|
||||
if (this.props.tooltip){
|
||||
|
@ -28,12 +28,20 @@ let Other = React.createClass({
|
||||
},
|
||||
|
||||
render() {
|
||||
let ext = this.props.url.split('.').pop();
|
||||
let filename = this.props.url.split('/').pop();
|
||||
let tokens = filename.split('.');
|
||||
let preview;
|
||||
|
||||
if (tokens.length > 1) {
|
||||
preview = '.' + tokens.pop();
|
||||
} else {
|
||||
preview = 'file';
|
||||
}
|
||||
|
||||
return (
|
||||
<Panel className="media-other">
|
||||
<p className="text-center">
|
||||
.{ext}
|
||||
{preview}
|
||||
</p>
|
||||
</Panel>
|
||||
);
|
||||
|
@ -1,82 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import PrizeListActions from '../../actions/prize_list_actions';
|
||||
import PrizeListStore from '../../stores/prize_list_store';
|
||||
|
||||
import Table from '../ascribe_table/table';
|
||||
import TableItem from '../ascribe_table/table_item';
|
||||
import TableItemText from '../ascribe_table/table_item_text';
|
||||
|
||||
import { ColumnModel} from '../ascribe_table/models/table_models';
|
||||
import { getLangText } from '../../utils/lang_utils';
|
||||
|
||||
let PrizesDashboard = React.createClass({
|
||||
|
||||
getInitialState() {
|
||||
return PrizeListStore.getState();
|
||||
},
|
||||
|
||||
componentDidMount() {
|
||||
PrizeListStore.listen(this.onChange);
|
||||
PrizeListActions.fetchPrizeList();
|
||||
},
|
||||
|
||||
componentWillUnmount() {
|
||||
PrizeListStore.unlisten(this.onChange);
|
||||
},
|
||||
|
||||
onChange(state) {
|
||||
this.setState(state);
|
||||
},
|
||||
|
||||
getColumnList() {
|
||||
return [
|
||||
new ColumnModel(
|
||||
(item) => {
|
||||
return {
|
||||
'content': item.name
|
||||
}; },
|
||||
'name',
|
||||
getLangText('Name'),
|
||||
TableItemText,
|
||||
6,
|
||||
false,
|
||||
null
|
||||
),
|
||||
new ColumnModel(
|
||||
(item) => {
|
||||
return {
|
||||
'content': item.domain
|
||||
}; },
|
||||
'domain',
|
||||
getLangText('Domain'),
|
||||
TableItemText,
|
||||
1,
|
||||
false,
|
||||
null
|
||||
)
|
||||
];
|
||||
},
|
||||
|
||||
render() {
|
||||
return (
|
||||
<Table
|
||||
responsive
|
||||
className="ascribe-table"
|
||||
columnList={this.getColumnList()}
|
||||
itemList={this.state.prizeList}>
|
||||
{this.state.prizeList.map((item, i) => {
|
||||
return (
|
||||
<TableItem
|
||||
className="ascribe-table-item-selectable"
|
||||
key={i}/>
|
||||
);
|
||||
})}
|
||||
</Table>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
export default PrizesDashboard;
|
@ -26,7 +26,7 @@ let ContractSettings = React.createClass({
|
||||
|
||||
componentDidMount() {
|
||||
ContractListStore.listen(this.onChange);
|
||||
ContractListActions.fetchContractList();
|
||||
ContractListActions.fetchContractList({is_active: 'True'});
|
||||
},
|
||||
|
||||
componentWillUnmount() {
|
||||
@ -39,32 +39,42 @@ let ContractSettings = React.createClass({
|
||||
|
||||
makeContractPublic(contract) {
|
||||
return () => {
|
||||
ContractListActions.makeContractPublic(contract)
|
||||
.then(() => ContractListActions.fetchContractList())
|
||||
.catch((error) => {
|
||||
let notification = new GlobalNotificationModel(error, 'success', 10000);
|
||||
contract.is_public = true;
|
||||
ContractListActions.changeContract(contract)
|
||||
.then(() => {
|
||||
ContractListActions.fetchContractList({is_active: 'True'});
|
||||
let notification = getLangText('Contract %s is now public', contract.name);
|
||||
notification = new GlobalNotificationModel(notification, 'success', 4000);
|
||||
GlobalNotificationActions.appendGlobalNotification(notification);
|
||||
});
|
||||
})
|
||||
.catch((err) => {
|
||||
let notification = new GlobalNotificationModel(err, 'danger', 10000);
|
||||
GlobalNotificationActions.appendGlobalNotification(notification);
|
||||
});
|
||||
};
|
||||
},
|
||||
|
||||
removeContract(contract) {
|
||||
return () => {
|
||||
ContractListActions.removeContract(contract.id)
|
||||
.then(( ) => ContractListActions.fetchContractList())
|
||||
.catch((error) => {
|
||||
let notification = new GlobalNotificationModel(error, 'danger', 10000);
|
||||
.then((response) => {
|
||||
ContractListActions.fetchContractList({is_active: 'True'});
|
||||
let notification = new GlobalNotificationModel(response.notification, 'success', 4000);
|
||||
GlobalNotificationActions.appendGlobalNotification(notification);
|
||||
});
|
||||
})
|
||||
.catch((err) => {
|
||||
let notification = new GlobalNotificationModel(err, 'danger', 10000);
|
||||
GlobalNotificationActions.appendGlobalNotification(notification);
|
||||
});
|
||||
};
|
||||
},
|
||||
|
||||
getPublicContracts(){
|
||||
return this.state.contractList.filter((contract) => contract.public);
|
||||
return this.state.contractList.filter((contract) => contract.is_public);
|
||||
},
|
||||
|
||||
getPrivateContracts(){
|
||||
return this.state.contractList.filter((contract) => !contract.public);
|
||||
return this.state.contractList.filter((contract) => !contract.is_public);
|
||||
},
|
||||
|
||||
render() {
|
||||
@ -95,8 +105,9 @@ let ContractSettings = React.createClass({
|
||||
<button className="btn btn-default btn-sm margin-left-2px">
|
||||
UPDATE
|
||||
</button>
|
||||
<button className="btn btn-default btn-sm margin-left-2px"
|
||||
onClick={this.removeContract(contract)}>
|
||||
<button
|
||||
className="btn btn-default btn-sm margin-left-2px"
|
||||
onClick={this.removeContract(contract)}>
|
||||
REMOVE
|
||||
</button>
|
||||
</div>
|
||||
@ -121,12 +132,14 @@ let ContractSettings = React.createClass({
|
||||
<button className="btn btn-default btn-sm margin-left-2px">
|
||||
UPDATE
|
||||
</button>
|
||||
<button className="btn btn-default btn-sm margin-left-2px"
|
||||
onClick={this.removeContract(contract)}>
|
||||
<button
|
||||
className="btn btn-default btn-sm margin-left-2px"
|
||||
onClick={this.removeContract(contract)}>
|
||||
REMOVE
|
||||
</button>
|
||||
<button className="btn btn-default btn-sm margin-left-2px"
|
||||
onClick={this.makeContractPublic(contract)}>
|
||||
<button
|
||||
className="btn btn-default btn-sm margin-left-2px"
|
||||
onClick={this.makeContractPublic(contract)}>
|
||||
MAKE PUBLIC
|
||||
</button>
|
||||
</div>
|
||||
|
@ -4,13 +4,12 @@ import React from 'react';
|
||||
import Router from 'react-router';
|
||||
import ReactAddons from 'react/addons';
|
||||
|
||||
import Col from 'react-bootstrap/lib/Col';
|
||||
|
||||
import SlidesContainerBreadcrumbs from './slides_container_breadcrumbs';
|
||||
|
||||
let State = Router.State;
|
||||
let Navigation = Router.Navigation;
|
||||
|
||||
|
||||
let SlidesContainer = React.createClass({
|
||||
propTypes: {
|
||||
children: React.PropTypes.arrayOf(React.PropTypes.element),
|
||||
@ -30,12 +29,15 @@ let SlidesContainer = React.createClass({
|
||||
let slideNum = -1;
|
||||
let startFrom = -1;
|
||||
|
||||
// We can actually need to check if slide_num is present as a key in queryParams.
|
||||
// We do not really care about its value though...
|
||||
if(queryParams && 'slide_num' in queryParams) {
|
||||
slideNum = parseInt(queryParams.slide_num, 10);
|
||||
}
|
||||
// if slide_num is not set, this will be done in componentDidMount
|
||||
|
||||
// the query param 'start_from' removes all slide children before the respective number
|
||||
// Also, we use the 'in' keyword for the same reason as above in 'slide_num'
|
||||
if(queryParams && 'start_from' in queryParams) {
|
||||
startFrom = parseInt(queryParams.start_from, 10);
|
||||
}
|
||||
@ -51,6 +53,9 @@ let SlidesContainer = React.createClass({
|
||||
componentDidMount() {
|
||||
// check if slide_num was defined, and if not then default to 0
|
||||
let queryParams = this.getQuery();
|
||||
|
||||
// We use 'in' to check if the key is present in the user's browser url bar,
|
||||
// we do not really care about its value at this point
|
||||
if(!('slide_num' in queryParams)) {
|
||||
|
||||
// we're first requiring all the other possible queryParams and then set
|
||||
@ -241,6 +246,16 @@ let SlidesContainer = React.createClass({
|
||||
},
|
||||
|
||||
render() {
|
||||
let spacing = this.state.containerWidth * this.state.slideNum;
|
||||
let translateXValue = 'translateX(' + (-1) * spacing + 'px)';
|
||||
|
||||
/*
|
||||
According to the react documentation,
|
||||
all browser vendor prefixes need to be upper cases in the beginning except for
|
||||
the Microsoft one *bigfuckingsurprise*
|
||||
https://facebook.github.io/react/tips/inline-styles.html
|
||||
*/
|
||||
|
||||
return (
|
||||
<div
|
||||
className="container ascribe-sliding-container-wrapper"
|
||||
@ -250,7 +265,11 @@ let SlidesContainer = React.createClass({
|
||||
className="container ascribe-sliding-container"
|
||||
style={{
|
||||
width: this.state.containerWidth * this.customChildrenCount(),
|
||||
transform: 'translateX(' + (-1) * this.state.containerWidth * this.state.slideNum + 'px)'
|
||||
transform: translateXValue,
|
||||
WebkitTransform: translateXValue,
|
||||
MozTransform: translateXValue,
|
||||
OTransform: translateXValue,
|
||||
mstransform: translateXValue
|
||||
}}>
|
||||
<div className="row">
|
||||
{this.renderChildren()}
|
||||
|
@ -6,15 +6,15 @@ import React from 'react';
|
||||
let TableItemAclFiltered = React.createClass({
|
||||
propTypes: {
|
||||
content: React.PropTypes.object,
|
||||
requestAction: React.PropTypes.string
|
||||
notifications: React.PropTypes.string
|
||||
},
|
||||
|
||||
render() {
|
||||
var availableAcls = ['acl_consign', 'acl_loan', 'acl_transfer', 'acl_view', 'acl_share', 'acl_unshare', 'acl_delete'];
|
||||
if (this.props.requestAction && this.props.requestAction.length > 0){
|
||||
if (this.props.notifications && this.props.notifications.length > 0){
|
||||
return (
|
||||
<span>
|
||||
{this.props.requestAction[0].action + ' request pending'}
|
||||
{this.props.notifications[0].action_str}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
@ -20,7 +20,6 @@ import AppConstants from '../../constants/application_constants';
|
||||
import { computeHashOfFile } from '../../utils/file_utils';
|
||||
|
||||
var ReactS3FineUploader = React.createClass({
|
||||
|
||||
propTypes: {
|
||||
keyRoutine: React.PropTypes.shape({
|
||||
url: React.PropTypes.string,
|
||||
@ -125,6 +124,7 @@ var ReactS3FineUploader = React.createClass({
|
||||
bucket: 'ascribe0'
|
||||
},
|
||||
request: {
|
||||
//endpoint: 'https://www.ascribe.io.global.prod.fastly.net',
|
||||
endpoint: 'https://ascribe0.s3.amazonaws.com',
|
||||
accessKey: 'AKIAIVCZJ33WSCBQ3QDA'
|
||||
},
|
||||
@ -235,6 +235,21 @@ var ReactS3FineUploader = React.createClass({
|
||||
};
|
||||
},
|
||||
|
||||
// Resets the whole react fineuploader component to its initial state
|
||||
reset() {
|
||||
// Cancel all currently ongoing uploads
|
||||
this.state.uploader.cancelAll();
|
||||
|
||||
// and reset component in general
|
||||
this.state.uploader.reset();
|
||||
|
||||
// proclaim that upload is not ready
|
||||
this.props.setIsUploadReady(false);
|
||||
|
||||
// reset internal data structures of component
|
||||
this.setState(this.getInitialState());
|
||||
},
|
||||
|
||||
requestKey(fileId) {
|
||||
let filename = this.state.uploader.getName(fileId);
|
||||
let uuid = this.state.uploader.getUuid(fileId);
|
||||
@ -356,64 +371,59 @@ var ReactS3FineUploader = React.createClass({
|
||||
|
||||
onComplete(id, name, res, xhr) {
|
||||
// there has been an issue with the server's connection
|
||||
if(xhr.status === 0) {
|
||||
|
||||
console.logGlobal(new Error('Complete was called but there wasn\t a success'), false, {
|
||||
if((xhr && xhr.status === 0) || res.error) {
|
||||
console.logGlobal(new Error(res.error || 'Complete was called but there wasn\t a success'), false, {
|
||||
files: this.state.filesToUpload,
|
||||
chunks: this.state.chunks
|
||||
});
|
||||
} else {
|
||||
let files = this.state.filesToUpload;
|
||||
|
||||
return;
|
||||
}
|
||||
// Set the state of the completed file to 'upload successful' in order to
|
||||
// remove it from the GUI
|
||||
files[id].status = 'upload successful';
|
||||
files[id].key = this.state.uploader.getKey(id);
|
||||
|
||||
let files = this.state.filesToUpload;
|
||||
|
||||
// Set the state of the completed file to 'upload successful' in order to
|
||||
// remove it from the GUI
|
||||
files[id].status = 'upload successful';
|
||||
files[id].key = this.state.uploader.getKey(id);
|
||||
|
||||
let newState = React.addons.update(this.state, {
|
||||
filesToUpload: { $set: files }
|
||||
});
|
||||
|
||||
this.setState(newState);
|
||||
|
||||
// Only after the blob has been created server-side, we can make the form submittable.
|
||||
this.createBlob(files[id])
|
||||
.then(() => {
|
||||
// since the form validation props isReadyForFormSubmission, setIsUploadReady and submitKey
|
||||
// are optional, we'll only trigger them when they're actually defined
|
||||
if(this.props.submitKey) {
|
||||
this.props.submitKey(files[id].key);
|
||||
} else {
|
||||
console.warn('You didn\'t define submitKey in as a prop in react-s3-fine-uploader');
|
||||
}
|
||||
|
||||
// for explanation, check comment of if statement above
|
||||
if(this.props.isReadyForFormSubmission && this.props.setIsUploadReady) {
|
||||
// also, lets check if after the completion of this upload,
|
||||
// the form is ready for submission or not
|
||||
if(this.props.isReadyForFormSubmission(this.state.filesToUpload)) {
|
||||
// if so, set uploadstatus to true
|
||||
this.props.setIsUploadReady(true);
|
||||
} else {
|
||||
this.props.setIsUploadReady(false);
|
||||
}
|
||||
} else {
|
||||
console.warn('You didn\'t define the functions isReadyForFormSubmission and/or setIsUploadReady in as a prop in react-s3-fine-uploader');
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
console.logGlobal(err, false, {
|
||||
files: this.state.filesToUpload,
|
||||
chunks: this.state.chunks
|
||||
});
|
||||
let notification = new GlobalNotificationModel(err.message, 'danger', 5000);
|
||||
GlobalNotificationActions.appendGlobalNotification(notification);
|
||||
let newState = React.addons.update(this.state, {
|
||||
filesToUpload: { $set: files }
|
||||
});
|
||||
|
||||
|
||||
this.setState(newState);
|
||||
|
||||
// Only after the blob has been created server-side, we can make the form submittable.
|
||||
this.createBlob(files[id])
|
||||
.then(() => {
|
||||
// since the form validation props isReadyForFormSubmission, setIsUploadReady and submitKey
|
||||
// are optional, we'll only trigger them when they're actually defined
|
||||
if(this.props.submitKey) {
|
||||
this.props.submitKey(files[id].key);
|
||||
} else {
|
||||
console.warn('You didn\'t define submitKey in as a prop in react-s3-fine-uploader');
|
||||
}
|
||||
|
||||
// for explanation, check comment of if statement above
|
||||
if(this.props.isReadyForFormSubmission && this.props.setIsUploadReady) {
|
||||
// also, lets check if after the completion of this upload,
|
||||
// the form is ready for submission or not
|
||||
if(this.props.isReadyForFormSubmission(this.state.filesToUpload)) {
|
||||
// if so, set uploadstatus to true
|
||||
this.props.setIsUploadReady(true);
|
||||
} else {
|
||||
this.props.setIsUploadReady(false);
|
||||
}
|
||||
} else {
|
||||
console.warn('You didn\'t define the functions isReadyForFormSubmission and/or setIsUploadReady in as a prop in react-s3-fine-uploader');
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
console.logGlobal(err, false, {
|
||||
files: this.state.filesToUpload,
|
||||
chunks: this.state.chunks
|
||||
});
|
||||
let notification = new GlobalNotificationModel(err.message, 'danger', 5000);
|
||||
GlobalNotificationActions.appendGlobalNotification(notification);
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
onError(id, name, errorReason) {
|
||||
@ -442,7 +452,6 @@ var ReactS3FineUploader = React.createClass({
|
||||
},
|
||||
|
||||
onCancel(id) {
|
||||
|
||||
// when a upload is canceled, we need to update this components file array
|
||||
this.setStatusOfFile(id, 'canceled');
|
||||
|
||||
@ -464,7 +473,6 @@ var ReactS3FineUploader = React.createClass({
|
||||
},
|
||||
|
||||
onProgress(id, name, uploadedBytes, totalBytes) {
|
||||
|
||||
let newState = React.addons.update(this.state, {
|
||||
filesToUpload: { [id]: {
|
||||
progress: { $set: (uploadedBytes / totalBytes) * 100} }
|
||||
|
36
js/components/contract_notification.js
Normal file
36
js/components/contract_notification.js
Normal file
@ -0,0 +1,36 @@
|
||||
'use strict';
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import NotificationStore from '../stores/notification_store';
|
||||
|
||||
import { mergeOptions } from '../utils/general_utils';
|
||||
|
||||
let ContractNotification = React.createClass({
|
||||
getInitialState() {
|
||||
return mergeOptions(
|
||||
NotificationStore.getState()
|
||||
);
|
||||
},
|
||||
|
||||
componentDidMount() {
|
||||
NotificationStore.listen(this.onChange);
|
||||
},
|
||||
|
||||
componentWillUnmount() {
|
||||
NotificationStore.unlisten(this.onChange);
|
||||
},
|
||||
|
||||
onChange(state) {
|
||||
this.setState(state);
|
||||
},
|
||||
|
||||
render() {
|
||||
|
||||
return (
|
||||
null
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
export default ContractNotification;
|
@ -96,6 +96,31 @@ let Header = React.createClass({
|
||||
}
|
||||
},
|
||||
|
||||
onMenuItemClick(event) {
|
||||
/*
|
||||
This is a hack to make the dropdown close after clicking on an item
|
||||
The function just need to be defined
|
||||
|
||||
from https://github.com/react-bootstrap/react-bootstrap/issues/368:
|
||||
|
||||
@jvillasante - Have you tried to use onSelect with the DropdownButton?
|
||||
I don't have a working example that is exactly like yours,
|
||||
but I just noticed that the Dropdown closes when I've attached an event handler to OnSelect:
|
||||
|
||||
<DropdownButton eventKey={3} title="Admin" onSelect={ this.OnSelected } >
|
||||
|
||||
onSelected: function(e) {
|
||||
// doesn't need to have functionality (necessarily) ... just wired up
|
||||
}
|
||||
Internally, a call to DropdownButton.setDropDownState(false) is made which will hide the dropdown menu.
|
||||
So, you should be able to call that directly on the DropdownButton instance as well if needed.
|
||||
|
||||
NOW, THAT DIDN'T WORK - the onSelect routine isnt triggered in all cases
|
||||
Hence, we do this manually
|
||||
*/
|
||||
this.refs.dropdownbutton.setDropdownState(false);
|
||||
},
|
||||
|
||||
render() {
|
||||
let account;
|
||||
let signup;
|
||||
@ -103,9 +128,15 @@ let Header = React.createClass({
|
||||
if (this.state.currentUser.username){
|
||||
account = (
|
||||
<DropdownButton
|
||||
ref='dropdownbutton'
|
||||
eventKey="1"
|
||||
title={this.state.currentUser.username}>
|
||||
<MenuItemLink eventKey="2" to="settings">{getLangText('Account Settings')}</MenuItemLink>
|
||||
<MenuItemLink
|
||||
eventKey="2"
|
||||
to="settings"
|
||||
onClick={this.onMenuItemClick}>
|
||||
{getLangText('Account Settings')}
|
||||
</MenuItemLink>
|
||||
<MenuItem divider />
|
||||
<MenuItemLink eventKey="3" to="logout">{getLangText('Log out')}</MenuItemLink>
|
||||
</DropdownButton>
|
||||
|
@ -8,7 +8,8 @@ import MenuItem from 'react-bootstrap/lib/MenuItem';
|
||||
|
||||
import Nav from 'react-bootstrap/lib/Nav';
|
||||
|
||||
import PieceListStore from '../stores/piece_list_store';
|
||||
import NotificationActions from '../actions/notification_actions';
|
||||
import NotificationStore from '../stores/notification_store';
|
||||
|
||||
import { mergeOptions } from '../utils/general_utils';
|
||||
import { getLangText } from '../utils/lang_utils';
|
||||
@ -20,23 +21,25 @@ let HeaderNotifications = React.createClass({
|
||||
|
||||
getInitialState() {
|
||||
return mergeOptions(
|
||||
PieceListStore.getState()
|
||||
NotificationStore.getState()
|
||||
);
|
||||
},
|
||||
|
||||
componentDidMount() {
|
||||
PieceListStore.listen(this.onChange);
|
||||
NotificationStore.listen(this.onChange);
|
||||
NotificationActions.fetchPieceListNotifications();
|
||||
NotificationActions.fetchEditionListNotifications();
|
||||
},
|
||||
|
||||
componentWillUnmount() {
|
||||
PieceListStore.unlisten(this.onChange);
|
||||
NotificationStore.unlisten(this.onChange);
|
||||
},
|
||||
|
||||
onChange(state) {
|
||||
this.setState(state);
|
||||
},
|
||||
|
||||
onSelected(event) {
|
||||
onMenuItemClick(event) {
|
||||
/*
|
||||
This is a hack to make the dropdown close after clicking on an item
|
||||
The function just need to be defined
|
||||
@ -54,32 +57,87 @@ let HeaderNotifications = React.createClass({
|
||||
}
|
||||
Internally, a call to DropdownButton.setDropDownState(false) is made which will hide the dropdown menu.
|
||||
So, you should be able to call that directly on the DropdownButton instance as well if needed.
|
||||
|
||||
NOW, THAT DIDN'T WORK - the onSelect routine isnt triggered in all cases
|
||||
Hence, we do this manually
|
||||
*/
|
||||
this.refs.dropdownbutton.setDropdownState(false);
|
||||
},
|
||||
|
||||
getPieceNotifications(){
|
||||
if (this.state.pieceListNotifications && this.state.pieceListNotifications.length > 0) {
|
||||
return (
|
||||
<div>
|
||||
<div className="notification-header">
|
||||
Artworks ({this.state.pieceListNotifications.length})
|
||||
</div>
|
||||
{this.state.pieceListNotifications.map((pieceNotification, i) => {
|
||||
return (
|
||||
<MenuItem eventKey={i + 2}>
|
||||
<NotificationListItem
|
||||
ref={i}
|
||||
notification={pieceNotification.notification}
|
||||
pieceOrEdition={pieceNotification.piece}
|
||||
onClick={this.onMenuItemClick}/>
|
||||
</MenuItem>
|
||||
);
|
||||
}
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
return null;
|
||||
},
|
||||
|
||||
getEditionNotifications(){
|
||||
if (this.state.editionListNotifications && this.state.editionListNotifications.length > 0) {
|
||||
return (
|
||||
<div>
|
||||
<div className="notification-header">
|
||||
Editions ({this.state.editionListNotifications.length})
|
||||
</div>
|
||||
{this.state.editionListNotifications.map((editionNotification, i) => {
|
||||
return (
|
||||
<MenuItem eventKey={i + 2}>
|
||||
<NotificationListItem
|
||||
ref={'edition' + i}
|
||||
notification={editionNotification.notification}
|
||||
pieceOrEdition={editionNotification.edition}
|
||||
onClick={this.onMenuItemClick}/>
|
||||
</MenuItem>
|
||||
);
|
||||
}
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
return null;
|
||||
},
|
||||
|
||||
render() {
|
||||
if (this.state.requestActions && this.state.requestActions.length > 0) {
|
||||
if ((this.state.pieceListNotifications && this.state.pieceListNotifications.length > 0) ||
|
||||
(this.state.editionListNotifications && this.state.editionListNotifications.length > 0)){
|
||||
let numNotifications = 0;
|
||||
if (this.state.pieceListNotifications && this.state.pieceListNotifications.length > 0) {
|
||||
numNotifications += this.state.pieceListNotifications.length;
|
||||
}
|
||||
if (this.state.editionListNotifications && this.state.editionListNotifications.length > 0) {
|
||||
numNotifications += this.state.editionListNotifications.length;
|
||||
}
|
||||
return (
|
||||
<Nav navbar right>
|
||||
<DropdownButton
|
||||
ref='dropdownbutton'
|
||||
eventKey="1"
|
||||
title={
|
||||
<span>
|
||||
<Glyphicon glyph='envelope' color="green"/>
|
||||
<span className="notification-amount">({this.state.requestActions.length})</span>
|
||||
<span className="notification-amount">({numNotifications})</span>
|
||||
</span>
|
||||
}
|
||||
className="notification-menu"
|
||||
onSelect={this.onSelected}>
|
||||
{this.state.requestActions.map((pieceOrEdition, i) => {
|
||||
return (
|
||||
<MenuItem eventKey={i + 2}>
|
||||
<NotificationListItem
|
||||
ref={i}
|
||||
pieceOrEdition={pieceOrEdition}/>
|
||||
</MenuItem>);
|
||||
}
|
||||
)}
|
||||
className="notification-menu">
|
||||
{this.getPieceNotifications()}
|
||||
{this.getEditionNotifications()}
|
||||
</DropdownButton>
|
||||
</Nav>
|
||||
);
|
||||
@ -90,33 +148,55 @@ let HeaderNotifications = React.createClass({
|
||||
|
||||
let NotificationListItem = React.createClass({
|
||||
propTypes: {
|
||||
pieceOrEdition: React.PropTypes.object
|
||||
notification: React.PropTypes.array,
|
||||
pieceOrEdition: React.PropTypes.object,
|
||||
onClick: React.PropTypes.func
|
||||
},
|
||||
|
||||
isPiece() {
|
||||
return !(this.props.pieceOrEdition && this.props.pieceOrEdition.parent);
|
||||
},
|
||||
|
||||
getLinkData() {
|
||||
|
||||
if(this.props.pieceOrEdition && this.props.pieceOrEdition.parent) {
|
||||
return {
|
||||
to: 'edition',
|
||||
params: {
|
||||
editionId: this.props.pieceOrEdition.bitcoin_id
|
||||
}
|
||||
};
|
||||
} else {
|
||||
if (this.isPiece()) {
|
||||
return {
|
||||
to: 'piece',
|
||||
params: {
|
||||
pieceId: this.props.pieceOrEdition.id
|
||||
}
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
to: 'edition',
|
||||
params: {
|
||||
editionId: this.props.pieceOrEdition.bitcoin_id
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
onClick(event){
|
||||
this.props.onClick(event);
|
||||
},
|
||||
|
||||
getNotificationText(){
|
||||
let numNotifications = null;
|
||||
if (this.props.notification.length > 1){
|
||||
numNotifications = <div>+ {this.props.notification.length - 1} more...</div>;
|
||||
}
|
||||
return (
|
||||
<div className="notification-action">
|
||||
{this.props.notification[0].action_str}
|
||||
{numNotifications}
|
||||
</div>);
|
||||
},
|
||||
|
||||
render() {
|
||||
if (this.props.pieceOrEdition) {
|
||||
return (
|
||||
<Link {...this.getLinkData()}>
|
||||
<Link {...this.getLinkData()} onClick={this.onClick}>
|
||||
<div className="row notification-wrapper">
|
||||
<div className="col-xs-4 clear-paddings">
|
||||
<div className="thumbnail-wrapper">
|
||||
@ -126,13 +206,7 @@ let NotificationListItem = React.createClass({
|
||||
<div className="col-xs-8 notification-list-item-header">
|
||||
<h1>{this.props.pieceOrEdition.title}</h1>
|
||||
<div className="sub-header">by {this.props.pieceOrEdition.artist_name}</div>
|
||||
<div className="notification-action">
|
||||
{
|
||||
this.props.pieceOrEdition.request_action.map((requestAction) => {
|
||||
return 'Pending ' + requestAction.action + ' request';
|
||||
})
|
||||
}
|
||||
</div>
|
||||
{this.getNotificationText()}
|
||||
</div>
|
||||
</div>
|
||||
</Link>);
|
||||
|
@ -65,8 +65,7 @@ let PieceList = React.createClass({
|
||||
let orderBy = this.props.orderBy ? this.props.orderBy : this.state.orderBy;
|
||||
if (this.state.pieceList.length === 0 || this.state.page !== page){
|
||||
PieceListActions.fetchPieceList(page, this.state.pageSize, this.state.search,
|
||||
orderBy, this.state.orderAsc, this.state.filterBy)
|
||||
.then(() => PieceListActions.fetchPieceRequestActions());
|
||||
orderBy, this.state.orderAsc, this.state.filterBy);
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -88,15 +88,37 @@ let PieceContainer = React.createClass({
|
||||
PieceActions.fetchOne(this.props.params.pieceId);
|
||||
},
|
||||
|
||||
getActions() {
|
||||
if (this.state.piece &&
|
||||
this.state.piece.notifications &&
|
||||
this.state.piece.notifications.length > 0) {
|
||||
return (
|
||||
<ListRequestActions
|
||||
pieceOrEditions={this.state.piece}
|
||||
currentUser={this.state.currentUser}
|
||||
handleSuccess={this.loadPiece}
|
||||
notifications={this.state.piece.notifications}/>);
|
||||
}
|
||||
},
|
||||
|
||||
render() {
|
||||
if('title' in this.state.piece) {
|
||||
if(this.state.piece && this.state.piece.title) {
|
||||
/*
|
||||
|
||||
This really needs a refactor!
|
||||
|
||||
- Tim
|
||||
|
||||
*/
|
||||
// Only show the artist name if you are the participant or if you are a judge and the piece is shortlisted
|
||||
let artistName = ((this.state.currentUser.is_jury && !this.state.currentUser.is_judge) ||
|
||||
(this.state.currentUser.is_judge && !this.state.piece.selected )) ?
|
||||
<span className="glyphicon glyphicon-eye-close" aria-hidden="true"/> : this.state.piece.artist_name;
|
||||
|
||||
// Only show the artist email if you are a judge and the piece is shortlisted
|
||||
let artistEmail = (this.state.currentUser.is_judge && this.state.piece.selected ) ?
|
||||
<DetailProperty label={getLangText('REGISTREE')} value={ this.state.piece.user_registered } /> : null;
|
||||
|
||||
return (
|
||||
<Piece
|
||||
piece={this.state.piece}
|
||||
@ -111,11 +133,7 @@ let PieceContainer = React.createClass({
|
||||
<DetailProperty label={getLangText('BY')} value={artistName} />
|
||||
<DetailProperty label={getLangText('DATE')} value={ this.state.piece.date_created.slice(0, 4) } />
|
||||
{artistEmail}
|
||||
<ListRequestActions
|
||||
pieceOrEditions={this.state.piece}
|
||||
currentUser={this.state.currentUser}
|
||||
handleSuccess={this.loadPiece}
|
||||
requestActions={this.state.piece.request_action}/>
|
||||
{this.getActions()}
|
||||
<hr/>
|
||||
</div>
|
||||
}
|
||||
|
@ -68,7 +68,7 @@ let CylandPieceContainer = React.createClass({
|
||||
},
|
||||
|
||||
render() {
|
||||
if('title' in this.state.piece) {
|
||||
if(this.state.piece && this.state.piece.title) {
|
||||
return (
|
||||
<Piece
|
||||
piece={this.state.piece}
|
||||
|
@ -72,6 +72,9 @@ let CylandRegisterPiece = React.createClass({
|
||||
// we may need to enter the process at step 1 or 2.
|
||||
// If this is the case, we'll need the piece number to complete submission.
|
||||
// It is encoded in the URL as a queryParam and we're checking for it here.
|
||||
//
|
||||
// We're using 'in' here as we want to know if 'piece_id' is present in the url,
|
||||
// we don't care about the value.
|
||||
if(queryParams && 'piece_id' in queryParams) {
|
||||
PieceActions.fetchOne(queryParams.piece_id);
|
||||
}
|
||||
|
@ -58,7 +58,7 @@ let IkonotvSubmitButton = React.createClass({
|
||||
<InputCheckbox>
|
||||
<span>
|
||||
{' ' + getLangText('I agree to the Terms of Service of IkonoTV Archive') + ' '}
|
||||
(<a href="https://d1qjsxua1o9x03.cloudfront.net/live/743394beff4b1282ba735e5e3723ed74/contract/bbc92f1d-4504-49f8-818c-8dd7113c6e06.pdf" target="_blank" style={{fontSize: '0.9em', color: 'rgba(0,0,0,0.7)'}}>
|
||||
(<a href="https://s3-us-west-2.amazonaws.com/ascribe0/whitelabel/ikonotv/ikono-tos.pdf" target="_blank" style={{fontSize: '0.9em', color: 'rgba(0,0,0,0.7)'}}>
|
||||
{getLangText('read')}
|
||||
</a>)
|
||||
</span>
|
||||
|
@ -89,15 +89,14 @@ let IkonotvPieceContainer = React.createClass({
|
||||
|
||||
getActions(){
|
||||
if (this.state.piece &&
|
||||
this.state.piece.request_action &&
|
||||
this.state.piece.request_action.length > 0) {
|
||||
this.state.piece.notifications &&
|
||||
this.state.piece.notifications.length > 0) {
|
||||
return (
|
||||
<ListRequestActions
|
||||
pieceOrEditions={this.state.piece}
|
||||
currentUser={this.state.currentUser}
|
||||
handleSuccess={this.loadPiece}
|
||||
requestActions={this.state.piece.request_action}/>
|
||||
);
|
||||
notifications={this.state.piece.notifications}/>);
|
||||
}
|
||||
else {
|
||||
|
||||
@ -133,7 +132,7 @@ let IkonotvPieceContainer = React.createClass({
|
||||
},
|
||||
|
||||
render() {
|
||||
if('title' in this.state.piece) {
|
||||
if(this.state.piece && this.state.piece.title) {
|
||||
return (
|
||||
<Piece
|
||||
piece={this.state.piece}
|
||||
|
@ -0,0 +1,180 @@
|
||||
'use strict';
|
||||
|
||||
import React from 'react';
|
||||
import Router from 'react-router';
|
||||
|
||||
import Glyphicon from 'react-bootstrap/lib/Glyphicon';
|
||||
import Button from 'react-bootstrap/lib/Button';
|
||||
|
||||
import Form from '../../../../ascribe_forms/form';
|
||||
import Property from '../../../../ascribe_forms/property';
|
||||
import InputCheckbox from '../../../../ascribe_forms/input_checkbox';
|
||||
|
||||
import NotificationActions from '../../../../../actions/notification_actions';
|
||||
import NotificationStore from '../../../../../stores/notification_store';
|
||||
import WhitelabelStore from '../../../../../stores/whitelabel_store';
|
||||
|
||||
import GlobalNotificationModel from '../../../../../models/global_notification_model';
|
||||
import GlobalNotificationActions from '../../../../../actions/global_notification_actions';
|
||||
|
||||
import apiUrls from '../../../../../constants/api_urls';
|
||||
|
||||
import requests from '../../../../../utils/requests';
|
||||
import { getLangText } from '../../../../../utils/lang_utils';
|
||||
import { mergeOptions } from '../../../../../utils/general_utils';
|
||||
|
||||
let Navigation = Router.Navigation;
|
||||
|
||||
let IkonotvContractNotifications = React.createClass({
|
||||
|
||||
mixins: [Navigation],
|
||||
|
||||
getInitialState() {
|
||||
return mergeOptions(
|
||||
NotificationStore.getState(),
|
||||
WhitelabelStore.getState()
|
||||
);
|
||||
},
|
||||
|
||||
componentDidMount() {
|
||||
NotificationStore.listen(this.onChange);
|
||||
WhitelabelStore.listen(this.onChange);
|
||||
if (this.state.contractAgreementListNotifications === null){
|
||||
NotificationActions.fetchContractAgreementListNotifications();
|
||||
}
|
||||
},
|
||||
|
||||
componentWillUnmount() {
|
||||
NotificationStore.unlisten(this.onChange);
|
||||
WhitelabelStore.unlisten(this.onChange);
|
||||
},
|
||||
|
||||
onChange(state) {
|
||||
this.setState(state);
|
||||
},
|
||||
|
||||
getContract(){
|
||||
let notifications = this.state.contractAgreementListNotifications[0];
|
||||
let blob = notifications.contract_agreement.contract.blob;
|
||||
if (blob.mime === 'pdf') {
|
||||
return (
|
||||
<div className='notification-contract-pdf'>
|
||||
<embed src={blob.url_safe} alt="pdf"
|
||||
pluginspage="http://www.adobe.com/products/acrobat/readstep2.html"/>
|
||||
<div className='notification-contract-pdf-download'>
|
||||
<a href={blob.url_safe} target="_blank">
|
||||
<Glyphicon glyph='download-alt'/>
|
||||
<span style={{padding: '0.3em'}}>
|
||||
Download PDF version
|
||||
</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<div className='notification-contract-download'>
|
||||
<a href={blob.url_safe} target="_blank">
|
||||
<Glyphicon glyph='download-alt'/>
|
||||
<span style={{padding: '0.3em'}}>
|
||||
Download contract
|
||||
</span>
|
||||
</a>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
|
||||
getAppendix() {
|
||||
let notifications = this.state.contractAgreementListNotifications[0];
|
||||
let appendix = notifications.contract_agreement.appendix;
|
||||
if (appendix) {
|
||||
return (<div>
|
||||
<h1>{getLangText('Appendix')}</h1>
|
||||
<pre>
|
||||
{appendix.default}
|
||||
</pre>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
return null;
|
||||
},
|
||||
|
||||
handleConfirmSuccess() {
|
||||
let notification = new GlobalNotificationModel(getLangText('You have accepted the conditions'), 'success', 10000);
|
||||
GlobalNotificationActions.appendGlobalNotification(notification);
|
||||
this.transitionTo('pieces');
|
||||
},
|
||||
|
||||
handleDeny() {
|
||||
let contractAgreement = this.state.contractAgreementListNotifications[0].contract_agreement;
|
||||
requests.put(apiUrls.ownership_contract_agreements_deny, {contract_agreement_id: contractAgreement.id}).then(
|
||||
() => this.handleDenySuccess()
|
||||
);
|
||||
},
|
||||
|
||||
handleDenySuccess() {
|
||||
let notification = new GlobalNotificationModel(getLangText('You have denied the conditions'), 'success', 10000);
|
||||
GlobalNotificationActions.appendGlobalNotification(notification);
|
||||
this.transitionTo('pieces');
|
||||
},
|
||||
|
||||
render() {
|
||||
|
||||
if (this.state.contractAgreementListNotifications &&
|
||||
this.state.contractAgreementListNotifications.length > 0) {
|
||||
let contractAgreement = this.state.contractAgreementListNotifications[0].contract_agreement;
|
||||
return (
|
||||
<div className='container'>
|
||||
<div className='notification-contract-wrapper'>
|
||||
<div className='notification-contract-logo'>
|
||||
<img src={this.state.whitelabel.logo}/>
|
||||
<div className='notification-contract-header'>
|
||||
{getLangText('Production Contract')}
|
||||
</div>
|
||||
</div>
|
||||
{this.getContract()}
|
||||
|
||||
<div className='notification-contract-footer'>
|
||||
{this.getAppendix}
|
||||
<h1>{getLangText('Are you a member of any copyright societies?')}</h1>
|
||||
<p>
|
||||
ARS, DACS, Bildkunst, Pictoright, SODRAC, Copyright Agency/Viscopy, SAVA, Bildrecht GmbH,
|
||||
SABAM, AUTVIS, CREAIMAGEN, SONECA, Copydan, EAU, Kuvasto, GCA, HUNGART, IVARO, SIAE, JASPAR-SPDA,
|
||||
AKKA/LAA, LATGA-A, SOMAAP, ARTEGESTION, CARIER, BONO, APSAV, SPA, GESTOR, VISaRTA, RAO, LITA,
|
||||
DALRO, VeGaP, BUS, ProLitteris, AGADU, AUTORARTE, BUBEDRA, BBDA, BCDA, BURIDA, ADAVIS, BSDA
|
||||
</p>
|
||||
<Form
|
||||
ref='form'
|
||||
url={requests.prepareUrl(apiUrls.ownership_contract_agreements_confirm, {contract_agreement_id: contractAgreement.id})}
|
||||
handleSuccess={this.handleConfirmSuccess}
|
||||
method='put'
|
||||
buttons={
|
||||
<p style={{marginTop: '1em'}}>
|
||||
<Button type="submit">{getLangText('I agree with the conditions')}</Button>
|
||||
<Button bsStyle="danger" className="btn-delete" bsSize="medium" onClick={this.handleDeny}>
|
||||
{getLangText('I disagree')}
|
||||
</Button>
|
||||
</p>
|
||||
}>
|
||||
<Property
|
||||
name="terms"
|
||||
className="ascribe-settings-property-collapsible-toggle"
|
||||
style={{paddingBottom: 0}}>
|
||||
<InputCheckbox>
|
||||
<span>
|
||||
{' ' + getLangText('Yes') }
|
||||
</span>
|
||||
</InputCheckbox>
|
||||
</Property>
|
||||
|
||||
</Form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
});
|
||||
|
||||
export default IkonotvContractNotifications;
|
@ -22,6 +22,7 @@ import CylandPieceList from './components/cyland/cyland_piece_list';
|
||||
import IkonotvPieceList from './components/ikonotv/ikonotv_piece_list';
|
||||
import IkonotvRequestLoan from './components/ikonotv/ikonotv_request_loan';
|
||||
import IkonotvPieceContainer from './components/ikonotv/ascribe_detail/ikonotv_piece_container';
|
||||
import IkonotvContractNotifications from './components/ikonotv/ikonotv_contract_notifications';
|
||||
|
||||
import CCRegisterPiece from './components/cc/cc_register_piece';
|
||||
|
||||
@ -77,6 +78,7 @@ let ROUTES = {
|
||||
<Route name="piece" path="pieces/:pieceId" handler={IkonotvPieceContainer} />
|
||||
<Route name="edition" path="editions/:editionId" handler={EditionContainer} />
|
||||
<Route name="settings" path="settings" handler={SettingsContainer} />
|
||||
<Route name="contract_notifications" path="contract_notifications" handler={IkonotvContractNotifications} />
|
||||
</Route>
|
||||
)
|
||||
};
|
||||
|
@ -27,7 +27,14 @@ let ApiUrls = {
|
||||
'note_private_piece': AppConstants.apiEndpoint + 'note/private/pieces/',
|
||||
'note_public_edition': AppConstants.apiEndpoint + 'note/public/editions/',
|
||||
'note_public_piece': AppConstants.apiEndpoint + 'note/public/pieces/',
|
||||
'notification_piecelist': AppConstants.apiEndpoint + 'notifications/pieces/',
|
||||
'notification_piece': AppConstants.apiEndpoint + 'notifications/pieces/${piece_id}/',
|
||||
'notification_editionlist': AppConstants.apiEndpoint + 'notifications/editions/',
|
||||
'notification_edition': AppConstants.apiEndpoint + 'notifications/editions/${edition_id}/',
|
||||
'notification_contractagreementlist': AppConstants.apiEndpoint + 'notifications/contract_agreements/',
|
||||
'ownership_contract_agreements': AppConstants.apiEndpoint + 'ownership/contract_agreements/',
|
||||
'ownership_contract_agreements_confirm': AppConstants.apiEndpoint + 'ownership/contract_agreements/${contract_agreement_id}/accept/',
|
||||
'ownership_contract_agreements_deny': AppConstants.apiEndpoint + 'ownership/contract_agreements/${contract_agreement_id}/reject/',
|
||||
'ownership_consigns': AppConstants.apiEndpoint + 'ownership/consigns/',
|
||||
'ownership_consigns_confirm': AppConstants.apiEndpoint + 'ownership/consigns/confirm/',
|
||||
'ownership_consigns_deny': AppConstants.apiEndpoint + 'ownership/consigns/deny/',
|
||||
@ -53,7 +60,6 @@ let ApiUrls = {
|
||||
'piece_extradata': AppConstants.apiEndpoint + 'pieces/${piece_id}/extradata/',
|
||||
'piece_first_edition_id': AppConstants.apiEndpoint + 'pieces/${piece_id}/edition_index/',
|
||||
'pieces_list': AppConstants.apiEndpoint + 'pieces/',
|
||||
'pieces_list_request_actions': AppConstants.apiEndpoint + 'pieces/request_actions/',
|
||||
'piece_remove_from_collection': AppConstants.apiEndpoint + 'ownership/shares/pieces/${piece_id}/',
|
||||
'user': AppConstants.apiEndpoint + 'users/',
|
||||
'users_login': AppConstants.apiEndpoint + 'users/login/',
|
||||
|
29
js/fetchers/notification_fetcher.js
Normal file
29
js/fetchers/notification_fetcher.js
Normal file
@ -0,0 +1,29 @@
|
||||
'use strict';
|
||||
|
||||
import requests from '../utils/requests';
|
||||
|
||||
|
||||
let NotificationFetcher = {
|
||||
|
||||
fetchPieceListNotifications() {
|
||||
return requests.get('notification_piecelist');
|
||||
},
|
||||
|
||||
fetchPieceNotifications(pieceId) {
|
||||
return requests.get('notification_piece', {'piece_id': pieceId});
|
||||
},
|
||||
|
||||
fetchEditionListNotifications() {
|
||||
return requests.get('notification_editionlist');
|
||||
},
|
||||
|
||||
fetchEditionNotifications(editionId) {
|
||||
return requests.get('notification_edition', {'edition_id': editionId});
|
||||
},
|
||||
|
||||
fetchContractAgreementListNotifications() {
|
||||
return requests.get('notification_contractagreementlist');
|
||||
}
|
||||
};
|
||||
|
||||
export default NotificationFetcher;
|
@ -15,8 +15,8 @@ let OwnershipFetcher = {
|
||||
/**
|
||||
* Fetch the contracts of the logged-in user from the API.
|
||||
*/
|
||||
fetchContractList(){
|
||||
return requests.get(ApiUrls.ownership_contract_list);
|
||||
fetchContractList(isActive){
|
||||
return requests.get(ApiUrls.ownership_contract_list, isActive);
|
||||
},
|
||||
|
||||
fetchLoanPieceRequestList(){
|
||||
@ -24,11 +24,11 @@ let OwnershipFetcher = {
|
||||
},
|
||||
|
||||
makeContractPublic(contractObj){
|
||||
return requests.put('ownership_csontract', { body: contractObj, contract_id: contractObj.id });
|
||||
return requests.put(ApiUrls.ownership_contract, { body: contractObj, contract_id: contractObj.id });
|
||||
},
|
||||
|
||||
deleteContract(contractObjId){
|
||||
return requests.delete('ownership_contract', {contract_id: contractObjId});
|
||||
return requests.delete(ApiUrls.ownership_contract, {contract_id: contractObjId});
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -21,8 +21,7 @@ import SettingsContainer from './components/ascribe_settings/settings_container'
|
||||
import CoaVerifyContainer from './components/coa_verify_container';
|
||||
|
||||
import RegisterPiece from './components/register_piece';
|
||||
|
||||
import PrizesDashboard from './components/ascribe_prizes_dashboard/prizes_dashboard';
|
||||
import ContractNotification from './components/contract_notification';
|
||||
|
||||
import AppConstants from './constants/application_constants';
|
||||
|
||||
@ -45,7 +44,7 @@ const COMMON_ROUTES = (
|
||||
<Route name="password_reset" path="password_reset" handler={PasswordResetContainer} />
|
||||
<Route name="settings" path="settings" handler={SettingsContainer} />
|
||||
<Route name="coa_verify" path="verify" handler={CoaVerifyContainer} />
|
||||
<Route name="prizes" path="prizes" handler={PrizesDashboard} />
|
||||
<Route name="contract_notifications" path="verify" handler={ContractNotification} />
|
||||
</Route>
|
||||
);
|
||||
|
||||
|
41
js/stores/notification_store.js
Normal file
41
js/stores/notification_store.js
Normal file
@ -0,0 +1,41 @@
|
||||
'use strict';
|
||||
|
||||
import React from 'react';
|
||||
import alt from '../alt';
|
||||
|
||||
import NotificationActions from '../actions/notification_actions';
|
||||
|
||||
|
||||
class NotificationStore {
|
||||
constructor() {
|
||||
this.pieceListNotifications = {};
|
||||
this.editionListNotifications = {};
|
||||
this.contractAgreementListNotifications = null;
|
||||
this.editionNotifications = null;
|
||||
this.pieceNotifications = null;
|
||||
this.bindActions(NotificationActions);
|
||||
}
|
||||
|
||||
onUpdatePieceListNotifications(res) {
|
||||
this.pieceListNotifications = res.notifications;
|
||||
}
|
||||
|
||||
onUpdatePieceNotifications(res) {
|
||||
this.pieceNotifications = res.notification;
|
||||
}
|
||||
|
||||
onUpdateEditionListNotifications(res) {
|
||||
this.editionListNotifications = res.notifications;
|
||||
}
|
||||
|
||||
onUpdateEditionNotifications(res) {
|
||||
this.editionNotifications = res.notification;
|
||||
}
|
||||
|
||||
onUpdateContractAgreementListNotifications(res) {
|
||||
this.contractAgreementListNotifications = res.notifications;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default alt.createStore(NotificationStore, 'NotificationStore');
|
36
js/third_party/notifications.js
vendored
Normal file
36
js/third_party/notifications.js
vendored
Normal file
@ -0,0 +1,36 @@
|
||||
'use strict';
|
||||
|
||||
import alt from '../alt';
|
||||
import EventActions from '../actions/event_actions';
|
||||
|
||||
import NotificationActions from '../actions/notification_actions';
|
||||
|
||||
|
||||
class NotificationsHandler {
|
||||
|
||||
constructor() {
|
||||
this.bindActions(EventActions);
|
||||
this.loaded = false;
|
||||
}
|
||||
|
||||
onProfileDidLoad(profile) {
|
||||
if (this.loaded) {
|
||||
return;
|
||||
}
|
||||
let subdomain = window.location.host.split('.')[0];
|
||||
if (subdomain === 'ikonotv') {
|
||||
NotificationActions.fetchContractAgreementListNotifications().then(
|
||||
(res) => {
|
||||
if (res.notifications && res.notifications.length > 0) {
|
||||
this.loaded = true;
|
||||
console.log('Contractagreement notifications loaded');
|
||||
setTimeout(() => window.appRouter.transitionTo('contract_notifications'), 0);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
this.loaded = true;
|
||||
}
|
||||
}
|
||||
|
||||
export default alt.createStore(NotificationsHandler, 'NotificationsHandler');
|
@ -52,19 +52,6 @@ export function sumNumList(l) {
|
||||
return sum;
|
||||
}
|
||||
|
||||
export function excludePropFromObject(obj, propList){
|
||||
let clonedObj = mergeOptions({},obj);
|
||||
for (let item in propList){
|
||||
console.log(item);
|
||||
if (clonedObj[propList[item]]){
|
||||
console.log('deleting... ');
|
||||
delete clonedObj[propList[item]];
|
||||
}
|
||||
}
|
||||
console.log(clonedObj);
|
||||
return clonedObj;
|
||||
}
|
||||
|
||||
/*
|
||||
Taken from http://stackoverflow.com/a/4795914/1263876
|
||||
Behaves like C's format string function
|
||||
@ -207,4 +194,14 @@ function _mergeOptions(obj1, obj2) {
|
||||
*/
|
||||
export function escapeHTML(s) {
|
||||
return document.createElement('div').appendChild(document.createTextNode(s)).parentNode.innerHTML;
|
||||
}
|
||||
|
||||
export function excludePropFromObject(obj, propList){
|
||||
let clonedObj = mergeOptions({}, obj);
|
||||
for (let item in propList){
|
||||
if (clonedObj[propList[item]]){
|
||||
delete clonedObj[propList[item]];
|
||||
}
|
||||
}
|
||||
return clonedObj;
|
||||
}
|
@ -33,7 +33,7 @@ class Requests {
|
||||
// If this is the case, we can not try to parse it as JSON.
|
||||
if(responseText !== 'None') {
|
||||
let body = JSON.parse(responseText);
|
||||
|
||||
|
||||
if(body && body.errors) {
|
||||
let error = new Error('Form Error');
|
||||
error.json = body;
|
||||
@ -116,7 +116,6 @@ class Requests {
|
||||
merged.headers['X-CSRFToken'] = csrftoken;
|
||||
}
|
||||
merged.method = verb;
|
||||
|
||||
return fetch(url, merged)
|
||||
.then(this.unpackResponse)
|
||||
.catch(this.handleError);
|
||||
@ -139,8 +138,7 @@ class Requests {
|
||||
|
||||
_putOrPost(url, paramsAndBody, method){
|
||||
let paramsCopy = this._merge(paramsAndBody);
|
||||
let params = excludePropFromObject(paramsAndBody,['body']);
|
||||
|
||||
let params = excludePropFromObject(paramsAndBody, ['body']);
|
||||
let newUrl = this.prepareUrl(url, params);
|
||||
let body = null;
|
||||
if (paramsCopy && paramsCopy.body) {
|
||||
@ -157,6 +155,10 @@ class Requests {
|
||||
return this._putOrPost(url, params, 'put');
|
||||
}
|
||||
|
||||
patch(url, params){
|
||||
return this._putOrPost(url, params, 'patch');
|
||||
}
|
||||
|
||||
defaults(options) {
|
||||
this.httpOptions = options.http || {};
|
||||
this.urlMap = options.urlMap || {};
|
||||
|
@ -3,35 +3,40 @@
|
||||
|
||||
video,
|
||||
img {
|
||||
display: block;
|
||||
height: auto;
|
||||
margin: 0 auto;
|
||||
max-height: 640px;
|
||||
max-width: 100%;
|
||||
max-height: 640px;
|
||||
width: auto;
|
||||
height: auto;
|
||||
display: block;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.media-other {
|
||||
color: #cccccc;
|
||||
font-size: 500%;
|
||||
p {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
|
||||
.audiojs {
|
||||
background-image: none;
|
||||
margin: 50px auto;
|
||||
* {
|
||||
box-sizing: content-box;
|
||||
}
|
||||
background-image: none;
|
||||
}
|
||||
|
||||
.loaded {
|
||||
background-color: $ascribe-color-green;
|
||||
background-image: none;
|
||||
}
|
||||
.audiojs * {
|
||||
box-sizing: content-box;
|
||||
}
|
||||
|
||||
.progress {
|
||||
background-color: rgba(255, 255, 255, .8);
|
||||
background-image: none;
|
||||
}
|
||||
.audiojs .loaded {
|
||||
background-color: $ascribe-color-green;
|
||||
background-image: none;
|
||||
}
|
||||
.audiojs .progress {
|
||||
background-color: rgba(255,255,255,0.8);
|
||||
background-image: none;
|
||||
}
|
||||
|
||||
.video-js,
|
||||
@ -44,13 +49,12 @@
|
||||
}
|
||||
|
||||
.vjs-fullscreen {
|
||||
padding-top: 0;
|
||||
|
||||
video {
|
||||
max-height: 100%;
|
||||
}
|
||||
padding-top: 0px;
|
||||
}
|
||||
|
||||
.vjs-fullscreen video {
|
||||
max-height: 100%;
|
||||
}
|
||||
|
||||
.vjs-default-skin .vjs-play-progress,
|
||||
.vjs-default-skin .vjs-volume-level {
|
||||
@ -58,35 +62,41 @@
|
||||
}
|
||||
|
||||
.vjs-default-skin .vjs-big-play-button {
|
||||
background-color: rgba(0, 0, 0, .8);
|
||||
border: 0;
|
||||
-moz-border-radius: 6px;
|
||||
-o-border-radius: 6px;
|
||||
-webkit-border-radius: 6px;
|
||||
border-radius: 6px;
|
||||
-moz-box-shadow: none;
|
||||
-o-box-shadow: none;
|
||||
-webkit-box-shadow: none;
|
||||
-o-border-radius: 6px;
|
||||
-moz-border-radius: 6px;
|
||||
-webkit-border-radius: 6px;
|
||||
|
||||
box-shadow: none;
|
||||
-o-box-shadow: none;
|
||||
-moz-box-shadow: none;
|
||||
-webkit-box-shadow: none;
|
||||
|
||||
width: 100px;
|
||||
height: 60px;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
margin: -30px -50px;
|
||||
top: 50%;
|
||||
width: 100px;
|
||||
|
||||
border: none;
|
||||
|
||||
background-color: rgba(0,0,0,.8);
|
||||
}
|
||||
|
||||
.vjs-default-skin:hover .vjs-big-play-button,
|
||||
.vjs-default-skin .vjs-big-play-button:focus {
|
||||
background-color: rgba(0, 0, 0, .9);
|
||||
border-color: #fff;
|
||||
-moz-box-shadow: none;
|
||||
-o-box-shadow: none;
|
||||
-webkit-box-shadow: none;
|
||||
background-color: rgba(0,0,0,.9);
|
||||
|
||||
box-shadow: none;
|
||||
-moz-transition: all 0s;
|
||||
-o-transition: all 0s;
|
||||
-webkit-transition: all 0s;
|
||||
-o-box-shadow: none;
|
||||
-moz-box-shadow: none;
|
||||
-webkit-box-shadow: none;
|
||||
|
||||
transition: all 0s;
|
||||
-o-transition: all 0s;
|
||||
-moz-transition: all 0s;
|
||||
-webkit-transition: all 0s;
|
||||
}
|
||||
|
||||
.vjs-default-skin .vjs-big-play-button:before {
|
||||
@ -103,7 +113,7 @@
|
||||
}
|
||||
|
||||
.vjs-default-skin .vjs-control-bar {
|
||||
background-color: rgba(0, 0, 0, .7);
|
||||
background-color: rgba(0,0,0,.7);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2,13 +2,27 @@ $break-small: 764px;
|
||||
$break-medium: 991px;
|
||||
$break-medium: 1200px;
|
||||
|
||||
.notification-wrapper {
|
||||
.notification-header,.notification-wrapper {
|
||||
width: 350px;
|
||||
height:8em;
|
||||
padding: 0.3em;
|
||||
border-bottom: 1px solid #cccccc;
|
||||
margin: -3px -20px;
|
||||
}
|
||||
|
||||
.notification-header {
|
||||
border-bottom: 1px solid #cccccc;
|
||||
border-top: 1px solid #cccccc;
|
||||
padding: 0.3em 1em;
|
||||
background-color: #eeeeee;
|
||||
}
|
||||
|
||||
.notification-wrapper {
|
||||
height:8.4em;
|
||||
border-bottom: 1px solid #eeeeee;
|
||||
margin: -3px 0;
|
||||
padding: 0.5em;
|
||||
color: black;
|
||||
|
||||
&:hover{
|
||||
background-color: rgba(2, 182, 163, .05);
|
||||
}
|
||||
// ToDo: Include media queries for thumbnail
|
||||
.thumbnail-wrapper {
|
||||
width: 7.4em;
|
||||
@ -31,9 +45,15 @@ $break-medium: 1200px;
|
||||
margin-top: 0.3em;
|
||||
margin-bottom: 0.15em;
|
||||
font-size: 1.8em;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.sub-header{
|
||||
margin-bottom: 1em;
|
||||
margin-bottom: 0.6em;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.notification-action{
|
||||
color: $ascribe-color-green;
|
||||
@ -46,6 +66,10 @@ $break-medium: 1200px;
|
||||
li a {
|
||||
padding-top: 0;
|
||||
}
|
||||
border-top: 0;
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
max-height: 70vh;
|
||||
}
|
||||
}
|
||||
|
||||
|
62
sass/ascribe_notification_page.scss
Normal file
62
sass/ascribe_notification_page.scss
Normal file
@ -0,0 +1,62 @@
|
||||
|
||||
.notification-contract-download {
|
||||
|
||||
}
|
||||
|
||||
.notification-contract-wrapper{
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.notification-contract-logo {
|
||||
|
||||
img {
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
.notification-contract-header {
|
||||
font-size: 2em;
|
||||
text-transform: uppercase;
|
||||
margin-bottom: 0.8em;
|
||||
}
|
||||
}
|
||||
|
||||
.notification-contract-pdf, .notification-contract-footer {
|
||||
width: 100%;
|
||||
max-width: 750px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
.notification-contract-pdf {
|
||||
embed {
|
||||
border: 1px solid #cccccc;
|
||||
width: 100%;
|
||||
height: 60vh;
|
||||
margin-bottom: 0.4em;
|
||||
}
|
||||
.notification-contract-pdf-download {
|
||||
text-align: left;
|
||||
margin-left: 1em;
|
||||
}
|
||||
}
|
||||
|
||||
.notification-contract-footer {
|
||||
text-align: left;
|
||||
padding: 1em;
|
||||
> h1 {
|
||||
margin-top: 0.4em;
|
||||
font-size: 1.4em;
|
||||
}
|
||||
> p {
|
||||
font-size: 0.9em;
|
||||
color: #444444;
|
||||
}
|
||||
> pre {
|
||||
color: #444;
|
||||
cursor: default;
|
||||
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
|
||||
background-color: rgba(0, 0, 0, 0);
|
||||
border: 0;
|
||||
box-shadow: none;
|
||||
margin-bottom: 1em;
|
||||
padding-left: 0;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
0
sass/lib/buttons.scss
Normal file
0
sass/lib/buttons.scss
Normal file
@ -25,6 +25,7 @@ $BASE_URL: '<%= BASE_URL %>';
|
||||
@import 'ascribe_global_action';
|
||||
@import 'ascribe_global_notification';
|
||||
@import 'ascribe_notification_list';
|
||||
@import 'ascribe_notification_page';
|
||||
@import 'ascribe_piece_register';
|
||||
@import 'offset_right';
|
||||
@import 'ascribe_settings';
|
||||
@ -208,36 +209,20 @@ hr {
|
||||
border: 1px solid $ascribe-brand-danger;
|
||||
}
|
||||
}
|
||||
|
||||
.btn-ascribe,
|
||||
.btn-ascribe-inv {
|
||||
.btn-ascribe {
|
||||
border: 1px solid #444;
|
||||
border-radius: 0 !important;
|
||||
font-family: sans-serif !important;
|
||||
line-height: 2em;
|
||||
margin-left: 0 !important;
|
||||
margin-right: 1px;
|
||||
}
|
||||
|
||||
.btn-ascribe,
|
||||
.btn-ascribe-inv:active,
|
||||
.btn-ascribe-inv:hover {
|
||||
background-color: #fff;
|
||||
font-family: sans-serif !important;
|
||||
border-radius: 0 !important;
|
||||
color: #222 !important;
|
||||
}
|
||||
|
||||
.btn-ascribe:active,
|
||||
.btn-ascribe:hover,
|
||||
.btn-ascribe-inv {
|
||||
.btn-ascribe:active, .btn-ascribe:hover {
|
||||
color: #FFF !important;
|
||||
background-color: #444;
|
||||
color: #fff !important;
|
||||
}
|
||||
|
||||
.btn-ascribe-inv:disabled,
|
||||
.btn-ascribe-inv:focus {
|
||||
background-color: #BBB !important;
|
||||
border: 1px solid #444 !important;
|
||||
color: #444 !important;
|
||||
}
|
||||
|
||||
.btn-ascribe-sm {
|
||||
|
Loading…
Reference in New Issue
Block a user