diff --git a/.scss-lint.yml b/.scss-lint.yml
new file mode 100644
index 00000000..61b1c624
--- /dev/null
+++ b/.scss-lint.yml
@@ -0,0 +1,224 @@
+linters:
+ BangFormat:
+ enabled: true
+ space_before_bang: true
+ space_after_bang: false
+
+ BemDepth:
+ enabled: false
+ max_elements: 1
+
+ BorderZero:
+ enabled: true
+ convention: zero # or `none`
+
+ ColorKeyword:
+ enabled: true
+
+ ColorVariable:
+ enabled: true
+
+ Comment:
+ enabled: true
+
+ DebugStatement:
+ enabled: true
+
+ DeclarationOrder:
+ enabled: true
+
+ DisableLinterReason:
+ enabled: false
+
+ DuplicateProperty:
+ enabled: true
+
+ ElsePlacement:
+ enabled: true
+ style: same_line # or 'new_line'
+
+ EmptyLineBetweenBlocks:
+ enabled: true
+ ignore_single_line_blocks: true
+
+ EmptyRule:
+ enabled: true
+
+ ExtendDirective:
+ enabled: false
+
+ FinalNewline:
+ enabled: false
+ present: true
+
+ HexLength:
+ enabled: true
+ style: short # or 'long'
+
+ HexNotation:
+ enabled: true
+ style: lowercase # or 'uppercase'
+
+ HexValidation:
+ enabled: true
+
+ IdSelector:
+ enabled: true
+
+ ImportantRule:
+ enabled: true
+
+ ImportPath:
+ enabled: true
+ leading_underscore: false
+ filename_extension: false
+
+ Indentation:
+ enabled: true
+ allow_non_nested_indentation: false
+ character: space # or 'tab'
+ width: 4
+
+ LeadingZero:
+ enabled: true
+ style: exclude_zero # or 'include_zero'
+
+ MergeableSelector:
+ enabled: true
+ force_nesting: true
+
+ NameFormat:
+ enabled: true
+ allow_leading_underscore: true
+ convention: hyphenated_lowercase # or 'camel_case', or 'snake_case', or a regex pattern
+
+ NestingDepth:
+ enabled: true
+ max_depth: 3
+ ignore_parent_selectors: false
+
+ PlaceholderInExtend:
+ enabled: true
+
+ PropertyCount:
+ enabled: false
+ include_nested: false
+ max_properties: 10
+
+ PropertySortOrder:
+ enabled: false
+ ignore_unspecified: false
+ min_properties: 2
+ separate_groups: false
+
+ PropertySpelling:
+ enabled: true
+ extra_properties: []
+
+ PropertyUnits:
+ enabled: true
+ global: [
+ 'ch', 'em', 'ex', 'rem', # Font-relative lengths
+ 'cm', 'in', 'mm', 'pc', 'pt', 'px', 'q', # Absolute lengths
+ 'vh', 'vw', 'vmin', 'vmax', # Viewport-percentage lengths
+ 'deg', 'grad', 'rad', 'turn', # Angle
+ 'ms', 's', # Duration
+ 'Hz', 'kHz', # Frequency
+ 'dpi', 'dpcm', 'dppx', # Resolution
+ '%'] # Other
+ properties: {}
+
+ QualifyingElement:
+ enabled: true
+ allow_element_with_attribute: false
+ allow_element_with_class: false
+ allow_element_with_id: false
+
+ SelectorDepth:
+ enabled: true
+ max_depth: 3
+
+ SelectorFormat:
+ enabled: true
+ convention: hyphenated_lowercase # or 'strict_BEM', or 'hyphenated_BEM', or 'snake_case', or 'camel_case', or a regex pattern
+
+ Shorthand:
+ enabled: true
+ allowed_shorthands: [1, 2, 3]
+
+ SingleLinePerProperty:
+ enabled: true
+ allow_single_line_rule_sets: true
+
+ SingleLinePerSelector:
+ enabled: true
+
+ SpaceAfterComma:
+ enabled: true
+
+ SpaceAfterPropertyColon:
+ enabled: true
+ style: one_space # or 'no_space', or 'at_least_one_space', or 'aligned'
+
+ SpaceAfterPropertyName:
+ enabled: true
+
+ SpaceAfterVariableName:
+ enabled: true
+
+ SpaceAroundOperator:
+ enabled: true
+ style: one_space # or 'no_space'
+
+ SpaceBeforeBrace:
+ enabled: true
+ style: space # or 'new_line'
+ allow_single_line_padding: false
+
+ SpaceBetweenParens:
+ enabled: true
+ spaces: 0
+
+ StringQuotes:
+ enabled: true
+ style: single_quotes # or double_quotes
+
+ TrailingSemicolon:
+ enabled: true
+
+ TrailingWhitespace:
+ enabled: true
+
+ TrailingZero:
+ enabled: false
+
+ TransitionAll:
+ enabled: false
+
+ UnnecessaryMantissa:
+ enabled: true
+
+ UnnecessaryParentReference:
+ enabled: true
+
+ UrlFormat:
+ enabled: true
+
+ UrlQuotes:
+ enabled: true
+
+ VariableForProperty:
+ enabled: false
+ properties: []
+
+ VendorPrefix:
+ enabled: false
+ identifier_list: base
+ additional_identifiers: []
+ excluded_identifiers: []
+
+ ZeroUnit:
+ enabled: true
+
+ Compass::*:
+ enabled: false
diff --git a/README.md b/README.md
index 270c4a5f..a3258576 100644
--- a/README.md
+++ b/README.md
@@ -32,8 +32,8 @@ Additionally, to work on the white labeling functionality, you need to edit your
```
-Code Conventions
-================
+JavaScript Code Conventions
+===========================
For this project, we're using:
* 4 Spaces
@@ -42,6 +42,15 @@ For this project, we're using:
* We don't use camel case for file naming but in everything Javascript related
* We use `let` instead of `var`: [SA Post](http://stackoverflow.com/questions/762011/javascript-let-keyword-vs-var-keyword)
+
+SCSS Code Conventions
+=====================
+Install [lint-scss](https://github.com/brigade/scss-lint), check the [editor integration docs](https://github.com/brigade/scss-lint#editor-integration) to integrate the lint in your editor.
+
+Some interesting links:
+* [Improving Sass code quality on theguardian.com](https://www.theguardian.com/info/developer-blog/2014/may/13/improving-sass-code-quality-on-theguardiancom)
+
+
Testing
===============
We're using Facebook's jest to do testing as it integrates nicely with react.js as well.
@@ -127,4 +136,4 @@ Moar stuff
- [24ways.org: JavaScript Modules the ES6 Way](http://24ways.org/2014/javascript-modules-the-es6-way/)
- [Babel: Learn ES6](https://babeljs.io/docs/learn-es6/)
- [egghead's awesome reactjs and flux tutorials](https://egghead.io/)
-- [Crockford's genious Javascript: The Good Parts (Tim has a copy)](http://www.amazon.de/JavaScript-Parts-Working-Shallow-Grain/dp/0596517742)
\ No newline at end of file
+- [Crockford's genious Javascript: The Good Parts (Tim has a copy)](http://www.amazon.de/JavaScript-Parts-Working-Shallow-Grain/dp/0596517742)
diff --git a/js/actions/contract_agreement_list_actions.js b/js/actions/contract_agreement_list_actions.js
new file mode 100644
index 00000000..589c1f51
--- /dev/null
+++ b/js/actions/contract_agreement_list_actions.js
@@ -0,0 +1,113 @@
+'use strict';
+
+import alt from '../alt';
+import Q from 'q';
+
+import OwnershipFetcher from '../fetchers/ownership_fetcher';
+import ContractListActions from './contract_list_actions';
+
+class ContractAgreementListActions {
+ constructor() {
+ this.generateActions(
+ 'updateContractAgreementList',
+ 'flushContractAgreementList'
+ );
+ }
+
+ fetchContractAgreementList(issuer, accepted, pending) {
+ this.actions.updateContractAgreementList(null);
+ return Q.Promise((resolve, reject) => {
+ OwnershipFetcher.fetchContractAgreementList(issuer, accepted, pending)
+ .then((contractAgreementList) => {
+ if (contractAgreementList.count > 0) {
+ this.actions.updateContractAgreementList(contractAgreementList.results);
+ resolve(contractAgreementList.results);
+ }
+ else{
+ resolve(null);
+ }
+ })
+ .catch((err) => {
+ console.logGlobal(err);
+ reject(err);
+ });
+ }
+ );
+ }
+
+ fetchAvailableContractAgreementList(issuer, createContractAgreement) {
+ return Q.Promise((resolve, reject) => {
+ OwnershipFetcher.fetchContractAgreementList(issuer, true, null)
+ .then((acceptedContractAgreementList) => {
+ // if there is at least an accepted contract agreement, we're going to
+ // use it
+ if(acceptedContractAgreementList.count > 0) {
+ this.actions.updateContractAgreementList(acceptedContractAgreementList.results);
+ } else {
+ // otherwise, we're looking for contract agreements that are still pending
+ //
+ // Normally nesting promises, but for this conditional one, it makes sense to not
+ // overcomplicate the method
+ OwnershipFetcher.fetchContractAgreementList(issuer, null, true)
+ .then((pendingContractAgreementList) => {
+ if(pendingContractAgreementList.count > 0) {
+ this.actions.updateContractAgreementList(pendingContractAgreementList.results);
+ } else {
+ // if there was neither a pending nor an active contractAgreement
+ // found and createContractAgreement is set to true, we create a
+ // new contract agreement
+ if(createContractAgreement) {
+ this.actions.createContractAgreementFromPublicContract(issuer);
+ }
+ }
+ })
+ .catch((err) => {
+ console.logGlobal(err);
+ reject(err);
+ });
+ }
+ })
+ .catch((err) => {
+ console.logGlobal(err);
+ reject(err);
+ });
+ }
+ );
+ }
+
+ createContractAgreementFromPublicContract(issuer) {
+ ContractListActions.fetchContractList(null, null, issuer)
+ .then((publicContract) => {
+ // create an agreement with the public contract if there is one
+ if (publicContract && publicContract.length > 0) {
+ return this.actions.createContractAgreement(null, publicContract[0]);
+ }
+ else {
+ /*
+ contractAgreementList in the store is already set to null;
+ */
+ }
+ }).then((publicContracAgreement) => {
+ if (publicContracAgreement) {
+ this.actions.updateContractAgreementList([publicContracAgreement]);
+ }
+ }).catch((err) => {
+ console.logGlobal(err);
+ });
+ }
+
+ createContractAgreement(issuer, contract){
+ return Q.Promise((resolve, reject) => {
+ OwnershipFetcher.createContractAgreement(issuer, contract).then(
+ (contractAgreement) => {
+ resolve(contractAgreement);
+ }
+ ).catch((err) => {
+ console.logGlobal(err);
+ reject(err);
+ });
+ });
+ }
+}
+
+export default alt.createActions(ContractAgreementListActions);
diff --git a/js/actions/contract_list_actions.js b/js/actions/contract_list_actions.js
new file mode 100644
index 00000000..5b874caf
--- /dev/null
+++ b/js/actions/contract_list_actions.js
@@ -0,0 +1,58 @@
+'use strict';
+
+import alt from '../alt';
+import OwnershipFetcher from '../fetchers/ownership_fetcher';
+import Q from 'q';
+
+class ContractListActions {
+ constructor() {
+ this.generateActions(
+ 'updateContractList',
+ 'flushContractList'
+ );
+ }
+
+ fetchContractList(isActive, isPublic, issuer) {
+ return Q.Promise((resolve, reject) => {
+ OwnershipFetcher.fetchContractList(isActive, isPublic, issuer)
+ .then((contracts) => {
+ this.actions.updateContractList(contracts.results);
+ resolve(contracts.results);
+ })
+ .catch((err) => {
+ console.logGlobal(err);
+ this.actions.updateContractList([]);
+ reject(err);
+ });
+ });
+ }
+
+
+ changeContract(contract){
+ return Q.Promise((resolve, reject) => {
+ OwnershipFetcher.changeContract(contract)
+ .then((res) => {
+ resolve(res);
+ })
+ .catch((err)=> {
+ console.logGlobal(err);
+ reject(err);
+ });
+ });
+ }
+
+ removeContract(contractId){
+ return Q.Promise( (resolve, reject) => {
+ OwnershipFetcher.deleteContract(contractId)
+ .then((res) => {
+ resolve(res);
+ })
+ .catch( (err) => {
+ console.logGlobal(err);
+ reject(err);
+ });
+ });
+ }
+}
+
+export default alt.createActions(ContractListActions);
diff --git a/js/actions/loan_contract_actions.js b/js/actions/loan_contract_actions.js
deleted file mode 100644
index cc7e5a5b..00000000
--- a/js/actions/loan_contract_actions.js
+++ /dev/null
@@ -1,48 +0,0 @@
-'use strict';
-
-import alt from '../alt';
-import OwnershipFetcher from '../fetchers/ownership_fetcher';
-
-
-class LoanContractActions {
- constructor() {
- this.generateActions(
- 'updateLoanContract',
- 'flushLoanContract'
- );
- }
-
- fetchLoanContract(email) {
- if(email.match(/.+\@.+\..+/)) {
- OwnershipFetcher.fetchLoanContract(email)
- .then((contracts) => {
- if (contracts && contracts.length > 0) {
- this.actions.updateLoanContract({
- contractKey: contracts[0].s3Key,
- contractUrl: contracts[0].s3Url,
- contractEmail: email
- });
- }
- else {
- this.actions.updateLoanContract({
- contractKey: null,
- contractUrl: null,
- contractEmail: null
- });
- }
- })
- .catch((err) => {
- console.logGlobal(err);
- this.actions.updateLoanContract({
- contractKey: null,
- contractUrl: null,
- contractEmail: null
- });
- });
- } else {
- /* No email was entered - Ignore and keep going*/
- }
- }
-}
-
-export default alt.createActions(LoanContractActions);
diff --git a/js/actions/loan_contract_list_actions.js b/js/actions/loan_contract_list_actions.js
deleted file mode 100644
index bc5cef82..00000000
--- a/js/actions/loan_contract_list_actions.js
+++ /dev/null
@@ -1,27 +0,0 @@
-'use strict';
-
-import alt from '../alt';
-import OwnershipFetcher from '../fetchers/ownership_fetcher';
-
-
-class LoanContractListActions {
- constructor() {
- this.generateActions(
- 'updateLoanContractList',
- 'flushLoanContractList'
- );
- }
-
- fetchLoanContractList() {
- OwnershipFetcher.fetchLoanContractList()
- .then((contracts) => {
- this.actions.updateLoanContractList(contracts);
- })
- .catch((err) => {
- console.logGlobal(err);
- this.actions.updateLoanContractList([]);
- });
- }
-}
-
-export default alt.createActions(LoanContractListActions);
diff --git a/js/actions/notification_actions.js b/js/actions/notification_actions.js
new file mode 100644
index 00000000..9318c922
--- /dev/null
+++ b/js/actions/notification_actions.js
@@ -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);
diff --git a/js/actions/piece_list_actions.js b/js/actions/piece_list_actions.js
index 2fd15c04..ae5ac090 100644
--- a/js/actions/piece_list_actions.js
+++ b/js/actions/piece_list_actions.js
@@ -57,7 +57,7 @@ class PieceListActions {
PieceListFetcher
.fetchRequestActions()
.then((res) => {
- this.actions.updatePieceListRequestActions(res.piece_ids);
+ this.actions.updatePieceListRequestActions(res);
})
.catch((err) => console.logGlobal(err));
}
diff --git a/js/app.js b/js/app.js
index addd0494..30a57d2b 100644
--- a/js/app.js
+++ b/js/app.js
@@ -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,8 +72,10 @@ class AppGateway {
subdomain = settings.subdomain;
}
+ window.document.body.classList.add('client--' + subdomain);
+
EventActions.applicationWillBoot(settings);
- Router.run(getRoutes(type, subdomain), Router.HistoryLocation, (App) => {
+ window.appRouter = Router.run(getRoutes(type, subdomain), Router.HistoryLocation, (App) => {
React.render(
{getLangText('We could not find any works related to you...')}
{getLangText('To register one, click')} {getLangText('here')}!