From 77a7fc784b4dbf43462d60a7c7e1e35b8e1e1672 Mon Sep 17 00:00:00 2001 From: Huan Li Date: Sun, 28 Apr 2019 00:03:01 +0800 Subject: [PATCH 01/15] Fix Server-side filtering on challenge listings --- __tests__/__snapshots__/index.js.snap | 225 +++++++ docs/actions.challenge-listing.md | 271 ++++++++ docs/buckets.md | 50 ++ docs/index.md | 27 +- docs/reducers.challenge-listing.md | 310 ++++++++++ docs/sort.md | 20 + docs/tc.md | 17 +- docs/url.md | 27 + src/actions/challenge-listing.js | 450 ++++++++++++++ src/actions/index.js | 2 + src/config/index.js | 4 + src/index.js | 2 +- src/reducers/challenge-listing.js | 848 ++++++++++++++++++++++++++ src/reducers/index.js | 3 + src/services/challenges.js | 4 +- src/utils/challenge/buckets.js | 153 +++++ src/utils/challenge/filter.js | 33 + src/utils/challenge/sort.js | 84 +++ src/utils/index.js | 6 + src/utils/tc.js | 47 ++ src/utils/url.js | 49 ++ 21 files changed, 2625 insertions(+), 7 deletions(-) create mode 100644 docs/actions.challenge-listing.md create mode 100644 docs/buckets.md create mode 100644 docs/reducers.challenge-listing.md create mode 100644 docs/sort.md create mode 100644 docs/url.md create mode 100644 src/actions/challenge-listing.js create mode 100644 src/config/index.js create mode 100644 src/reducers/challenge-listing.js create mode 100644 src/utils/challenge/buckets.js create mode 100644 src/utils/challenge/sort.js create mode 100644 src/utils/url.js diff --git a/__tests__/__snapshots__/index.js.snap b/__tests__/__snapshots__/index.js.snap index 0542a66c..e7ee51bc 100644 --- a/__tests__/__snapshots__/index.js.snap +++ b/__tests__/__snapshots__/index.js.snap @@ -29,6 +29,31 @@ Object { "updateChallengeDone": [Function], "updateChallengeInit": [Function], }, + "challengeListing": Object { + "dropChallenges": [Function], + "expandTag": [Function], + "getActiveChallengesDone": [Function], + "getActiveChallengesInit": [Function], + "getAllActiveChallengesDone": [Function], + "getAllActiveChallengesInit": [Function], + "getChallengeSubtracksDone": [Function], + "getChallengeSubtracksInit": [Function], + "getChallengeTagsDone": [Function], + "getChallengeTagsInit": [Function], + "getMoreChallenges": [Function], + "getPastChallengesDone": [Function], + "getPastChallengesInit": [Function], + "getRestActiveChallengesDone": [Function], + "getRestActiveChallengesInit": [Function], + "getReviewOpportunitiesDone": [Function], + "getReviewOpportunitiesInit": [Function], + "getSrmsDone": [Function], + "getSrmsInit": [Function], + "selectCommunity": [Function], + "setDatepickerStatus": [Function], + "setFilter": [Function], + "setSort": [Function], + }, "direct": Object { "dropAll": [Function], "getProjectDetailsDone": [Function], @@ -173,13 +198,145 @@ Object { }, }, "challenge": Object { + "buckets": Object { + "BUCKETS": Object { + "ALL": "all", + "MY": "my", + "ONGOING": "ongoing", + "OPEN_FOR_REGISTRATION": "openForRegistration", + "PAST": "past", + "REVIEW_OPPORTUNITIES": "reviewOpportunities", + "SAVED_FILTER": "saved-filter", + "SAVED_REVIEW_OPPORTUNITIES_FILTER": "savedReviewOpportunitiesFilter", + "UPCOMING": "upcoming", + }, + "BUCKET_DATA": Object { + "all": Object { + "filter": Object { + "started": true, + "status": Array [ + "ACTIVE", + ], + }, + "hideCount": false, + "name": "All Challenges", + "sorts": Array [], + }, + "my": Object { + "filter": Object { + "started": true, + "status": Array [ + "ACTIVE", + ], + }, + "hideCount": false, + "name": "My Challenges", + "sorts": Array [ + "most-recent", + "time-to-submit", + "num-registrants", + "num-submissions", + "prize-high-to-low", + "title-a-to-z", + ], + }, + "ongoing": Object { + "filter": Object { + "registrationOpen": false, + "started": true, + "status": Array [ + "ACTIVE", + ], + }, + "hideCount": false, + "name": "Ongoing challenges", + "sorts": Array [ + "most-recent", + "current-phase", + "title-a-to-z", + "prize-high-to-low", + ], + }, + "openForRegistration": Object { + "filter": Object { + "registrationOpen": true, + "started": true, + "status": Array [ + "ACTIVE", + ], + }, + "hideCount": false, + "name": "Open for registration", + "sorts": Array [ + "most-recent", + "time-to-register", + "time-to-submit", + "num-registrants", + "num-submissions", + "prize-high-to-low", + "title-a-to-z", + ], + }, + "past": Object { + "filter": Object { + "status": Array [ + "COMPLETED", + "PAST", + ], + }, + "hideCount": true, + "name": "Past challenges", + "sorts": Array [ + "most-recent", + "prize-high-to-low", + "title-a-to-z", + ], + }, + "reviewOpportunities": Object { + "filter": Object {}, + "hideCount": true, + "name": "Open for review", + "sorts": Array [ + "review-opportunities-start-date", + "review-opportunities-payment", + "review-opportunities-title-a-to-z", + ], + }, + "savedReviewOpportunitiesFilter": Object { + "filter": Object {}, + "sorts": Array [ + "review-opportunities-start-date", + "review-opportunities-payment", + "review-opportunities-title-a-to-z", + ], + }, + "upcoming": Object { + "filter": Object { + "upcoming": true, + }, + "hideCount": true, + "name": "Upcoming challenges", + "sorts": Array [ + "most-recent", + "prize-high-to-low", + "title-a-to-z", + ], + }, + }, + "default": undefined, + "getBuckets": [Function], + "isReviewOpportunitiesBucket": [Function], + "registerBucket": [Function], + }, "filter": Object { "addTrack": [Function], "combine": [Function], "default": undefined, + "filterByDate": [Function], "getFilterFunction": [Function], "getReviewOpportunitiesFilterFunction": [Function], "mapToBackend": [Function], + "newMeta": [Function], "removeTrack": [Function], "setEndDate": [Function], "setReviewOpportunityType": [Function], @@ -188,6 +345,67 @@ Object { "setTags": [Function], "setText": [Function], }, + "sort": Object { + "SORTS": Object { + "CURRENT_PHASE": "current-phase", + "MOST_RECENT": "most-recent", + "NUM_REGISTRANTS": "num-registrants", + "NUM_SUBMISSIONS": "num-submissions", + "PRIZE_HIGH_TO_LOW": "prize-high-to-low", + "REVIEW_OPPORTUNITIES_PAYMENT": "review-opportunities-payment", + "REVIEW_OPPORTUNITIES_START_DATE": "review-opportunities-start-date", + "REVIEW_OPPORTUNITIES_TITLE_A_TO_Z": "review-opportunities-title-a-to-z", + "TIME_TO_REGISTER": "time-to-register", + "TIME_TO_SUBMIT": "time-to-submit", + "TITLE_A_TO_Z": "title-a-to-z", + }, + "SORTS_DATA": Object { + "current-phase": Object { + "func": [Function], + "name": "Current phase", + }, + "most-recent": Object { + "func": [Function], + "name": "Most recent", + }, + "num-registrants": Object { + "func": [Function], + "name": "# of registrants", + }, + "num-submissions": Object { + "func": [Function], + "name": "# of submissions", + }, + "prize-high-to-low": Object { + "func": [Function], + "name": "Prize high to low", + }, + "review-opportunities-payment": Object { + "func": [Function], + "name": "Payment", + }, + "review-opportunities-start-date": Object { + "func": [Function], + "name": "Review start date", + }, + "review-opportunities-title-a-to-z": Object { + "func": [Function], + "name": "Title A-Z", + }, + "time-to-register": Object { + "func": [Function], + "name": "Time to register", + }, + "time-to-submit": Object { + "func": [Function], + "name": "Time to submit", + }, + "title-a-to-z": Object { + "func": [Function], + "name": "Title A-Z", + }, + }, + }, }, "errors": Object { "ERROR_ICON_TYPES": Object { @@ -227,6 +445,7 @@ Object { "reducers": Object { "auth": [Function], "challenge": [Function], + "challengeListing": [Function], "direct": [Function], "errors": [Function], "groups": [Function], @@ -321,11 +540,17 @@ Object { }, "getApiResponsePayload": [Function], "getLookerApiResponsePayload": [Function], + "processSRM": [Function], }, "time": Object { "default": undefined, "delay": [Function], "formatDuration": [Function], }, + "url": Object { + "default": undefined, + "removeTrailingSlash": [Function], + "updateQuery": [Function], + }, } `; diff --git a/docs/actions.challenge-listing.md b/docs/actions.challenge-listing.md new file mode 100644 index 00000000..85843947 --- /dev/null +++ b/docs/actions.challenge-listing.md @@ -0,0 +1,271 @@ + + +## actions.challenge-listing +Actions related to Topcoder challenge-listing APIs. + + +* [actions.challenge-listing](#module_actions.challenge-listing) + * [.dropChallenges(bucket)](#module_actions.challenge-listing.dropChallenges) ⇒ Action + * [.getMoreChallenges(bucket)](#module_actions.challenge-listing.getMoreChallenges) ⇒ Action + * [.getAllActiveChallengesInit(uuid)](#module_actions.challenge-listing.getAllActiveChallengesInit) ⇒ Action + * [.getAllActiveChallengesDone(uuid, tokenV3)](#module_actions.challenge-listing.getAllActiveChallengesDone) ⇒ Action + * [.getActiveChallengesInit(uuid, page, frontFilter, sort, bucket)](#module_actions.challenge-listing.getActiveChallengesInit) ⇒ Action + * [.getActiveChallengesDone( + uuid, page, backendFilter, tokenV3, frontFilter, sort, bucket, +)](#module_actions.challenge-listing.getActiveChallengesDone) ⇒ Action + * [.getRestActiveChallengesInit(uuid)](#module_actions.challenge-listing.getRestActiveChallengesInit) ⇒ Action + * [.getRestActiveChallengesDone( + uuid, tokenV3, backendFilter, frontFilter, sort, bucket, +)](#module_actions.challenge-listing.getRestActiveChallengesDone) ⇒ Action + * [.getChallengeSubtracksInit()](#module_actions.challenge-listing.getChallengeSubtrackInit) ⇒ Action + * [.getChallengeSubtracksDone()](#module_actions.challenge-listing.getChallengeSubtracksDone) ⇒ Action + * [.getChallengeTagsInit()](#module_actions.challenge-listing.getChallengeTagsInit) ⇒ Action + * [.getChallengeTagsDone()](#module_actions.challenge-listing.getChallengeTagsDone) ⇒ Action + * [.getPastChallengesInit(uuid, page, frontFilter, sort)](#module_actions.challenge-listing.getPastChallengesInit) ⇒ Action + * [.getPastChallengesDone(uuid, page, filter, tokenV3, frontFilter, sort)](#module_actions.challenge-listing.getPastChallengesDone) ⇒ Action + * [.getReviewOpportunitiesInit(uuid, page, sort)](#module_actions.challenge-listing.getReviewOpportunitiesInit) ⇒ Action + * [.getReviewOpportunitiesDone(uuid, page, tokenV3, sort, frontFilter)](#module_actions.challenge-listing.getReviewOpportunitiesDone) ⇒ Action + * [.getSrmsInit(uuid)](#module_actions.challenge-listing.getSrmsInit) ⇒ Action + * [.getSrmsDone(uuid, handle, params, tokenV3)](#module_actions.challenge-listing.getSrmsDone) ⇒ Action + * [.expandTag(id)](#module_actions.challenge-listing.expandTag) ⇒ Action + * [.selectCommunity()](#module_actions.challenge-listing.selectCommunity) ⇒ Action + * [.setFilter()](#module_actions.challenge-listing.setFilter) ⇒ Action + * [.setDatepickerStatus(status)](#module_actions.challenge-listing.setDatepickerStatus) ⇒ Action + * [.setSort(bucket, sort)](#module_actions.challenge-listing.setSort) ⇒ Action + + + +### actions.challenge-listing.dropChallenges(bucket) ⇒ Action +Creates an action that drops from Redux store all challenges-list related loaded. +**Kind**: static method of [actions.challenge-listing](#module_actions.challenge-listing) + +| Param | Type | Description | +| --- | --- | --- | +| bucket | String | Bucket name | + + + +### actions.challenge-listing.getMoreChallenges(bucket) ⇒ Action +Creates an action that get more challenges of bucket. +**Kind**: static method of [actions.challenge-listing](#module_actions.challenge-listing) + +| Param | Type | Description | +| --- | --- | --- | +| bucket | String | Bucket name | + + + +### actions.challenge-listing.getAllActiveChallengesInit(uuid) ⇒ Action +Creates an action that signals beginning of all active challenges loading. +**Kind**: static method of [actions.challenge-listing](#module_actions.challenge-listing) + +| Param | Type | Description | +| --- | --- | --- | +| uuid | String | UUID of the operation (the same should be passed into the corresponding | + + + +### actions.challenge-listing.getAllActiveChallengesInit(uuid, tokenV3) ⇒ Action +Creates an action that loads all active challenges. +**Kind**: static method of [actions.challenge-listing](#module_actions.challenge-listing) + +| Param | Type | Description | +| --- | --- | --- | +| uuid | String | UUID of the operation (the same should be passed into the corresponding | +| tokenV3 | String | Topcoder v3 auth token. | + + +### actions.challenge-listing.getActiveChallengesInit(uuid, page, frontFilter, sort, bucket) ⇒ Action +Creates an action that signals beginning of active challenges of bucket loading. +**Kind**: static method of [actions.challenge-listing](#module_actions.challenge-listing) + +| Param | Type | Description | +| --- | --- | --- | +| uuid | String | UUID of the operation (the same should be passed into the corresponding | +| page | Number | Page number of fetch data | +| frontFilter | Object | Filter Object from Client | +| sort | String | Sort name | +| bucket | String | Bucket name | + + + +### actions.challenge-listing.getActiveChallengesDone( + uuid, page, backendFilter, tokenV3, frontFilter, sort, bucket, +) ⇒ Action +Creates an action that loads active challenges of bucket. +**Kind**: static method of [actions.challenge-listing](#module_actions.challenge-listing) + +| Param | Type | Description | +| --- | --- | --- | +| uuid | String | UUID of the operation (the same should be passed into the corresponding | +| page | Number | Page number of fetch data | +| backendFilter | Object | Filter Object from Backend | +| tokenV3 | String | Topcoder v3 auth token | +| frontFilter | Object | Filter Object from Client | +| sort | String | Sort name | +| bucket | String | Bucket name | + + + +### actions.challenge-listing.getRestActiveChallengesInit(uuid) ⇒ Action +Creates an action that signals beginning of rest active challenges of bucket loading. +**Kind**: static method of [actions.challenge-listing](#module_actions.challenge-listing) + +| Param | Type | Description | +| --- | --- | --- | +| uuid | String | UUID of the operation (the same should be passed into the corresponding | + + + +### actions.challenge-listing.getRestActiveChallengesDone( + uuid, tokenV3, backendFilter, frontFilter, sort, bucket, +) ⇒ Action +Creates an action that loads rest active challenges of bucket. +**Kind**: static method of [actions.challenge-listing](#module_actions.challenge-listing) + +| Param | Type | Description | +| --- | --- | --- | +| uuid | String | UUID of the operation (the same should be passed into the corresponding | +| tokenV3 | String | Topcoder v3 auth token | +| backendFilter | Object | Filter Object from Backend | +| frontFilter | Object | Filter Object from Client | +| sort | String | Sort name | +| bucket | String | Bucket name | + + + +### actions.challenge-listing.getChallengeSubtracksInit() ⇒ Action +Creates an action that signals beginning of challenge substrcks loading. +**Kind**: static method of [actions.challenge-listing](#module_actions.challenge-listing) + + +### actions.challenge-listing.getChallengeSubtracksDone()⇒ Action +Creates an action that loads challenge substrcks. +**Kind**: static method of [actions.challenge-listing](#module_actions.challenge-listing) + + +### actions.challenge-listing.getChallengeTagsInit() ⇒ Action +Creates an action that signals beginning of challenge tags loading. +**Kind**: static method of [actions.challenge-listing](#module_actions.challenge-listing) + + +### actions.challenge-listing.getChallengeTagsDone()⇒ Action +Creates an action that loads challenge tags. +**Kind**: static method of [actions.challenge-listing](#module_actions.challenge-listing) + + +### actions.challenge-listing.getPastChallengesInit(uuid, page, frontFilter, sort) ⇒ Action +Creates an action that signals beginning of past challenges loading. +**Kind**: static method of [actions.challenge-listing](#module_actions.challenge-listing) + +| Param | Type | Description | +| --- | --- | --- | +| uuid | String | UUID of the operation (the same should be passed into the corresponding | +| page | Number | Page number of fetch data | +| frontFilter | Object | Filter Object from Client | +| sort | String | Sort name | + + + +### actions.challenge-listing.getPastChallengesDone(uuid, page, filter, tokenV3, frontFilter, sort) ⇒ Action +Creates an action that loads past challenges. +**Kind**: static method of [actions.challenge-listing](#module_actions.challenge-listing) + +| Param | Type | Description | +| --- | --- | --- | +| uuid | String | UUID of the operation (the same should be passed into the corresponding | +| page | Number | Page number of fetch data | +| filter | Object | Filter Object from Backend | +| tokenV3 | String | Topcoder v3 auth token | +| frontFilter | Object | Filter Object from Client | +| sort | String | Sort name | + + + +### actions.challenge-listing.getReviewOpportunitiesInit(uuid, page, sort) ⇒ Action +Creates an action that signals beginning of review opportunities loading. +**Kind**: static method of [actions.challenge-listing](#module_actions.challenge-listing) + +| Param | Type | Description | +| --- | --- | --- | +| uuid | String | UUID of the operation (the same should be passed into the corresponding | +| page | Number | Page number of fetch data | +| sort | String | Sort name | + + + +### actions.challenge-listing.getReviewOpportunitiesDone(uuid, page, tokenV3, sort, frontFilter) ⇒ Action +Creates an action that loads review oportunites. +**Kind**: static method of [actions.challenge-listing](#module_actions.challenge-listing) + +| Param | Type | Description | +| --- | --- | --- | +| uuid | String | UUID of the operation (the same should be passed into the corresponding | +| page | Number | Page number of fetch data | +| tokenV3 | String | Topcoder v3 auth token | +| sort | String | Sort name | +| frontFilter | Object | Filter Object from Client | + + + + +### actions.challenge-listing.getSrmsInit(uuid) ⇒ Action +Creates an action that signals beginning of SRMs loading. +**Kind**: static method of [actions.challenge-listing](#module_actions.challenge-listing) + +| Param | Type | Description | +| --- | --- | --- | +| uuid | String | UUID of the operation (the same should be passed into the corresponding | + + +### actions.challenge-listing.getSrmsDone(uuid, handle, params, tokenV3) ⇒ Action +Creates an action that SRMs. +**Kind**: static method of [actions.challenge-listing](#module_actions.challenge-listing) + +| Param | Type | Description | +| --- | --- | --- | +| uuid | String | UUID of the operation (the same should be passed into the corresponding | +| handle | String | Topcoder member handle | +| params | Object | params of fetch data | +| tokenV3 | String | Topcoder v3 auth token | + + + +### actions.challenge-listing.expandTag(id) ⇒ Action +Creates an action that set tag id +**Kind**: static method of [actions.challenge-listing](#module_actions.challenge-listing) + +| Param | Type | Description | +| --- | --- | --- | +| id | String | Id of tag | + + + +### actions.challenge-listing.selectCommunity() ⇒ Action +Creates an action that pass community id +**Kind**: static method of [actions.challenge-listing](#module_actions.challenge-listing) + + +### actions.challenge-listing.setFilter() ⇒ Action +Creates an action that pass filter value +**Kind**: static method of [actions.challenge-listing](#module_actions.challenge-listing) + + +### actions.challenge-listing.setDatepickerStatus(status) ⇒ Action +Creates an action that set Datepicker status +**Kind**: static method of [actions.challenge-listing](#module_actions.challenge-listing) + +| Param | Type | Description | +| --- | --- | --- | +| status | Boolean | Status datapicker | + + + +### actions.challenge-listing.setSort(bucket, sort) ⇒ Action +Creates an action that set sort of bucket +**Kind**: static method of [actions.challenge-listing](#module_actions.challenge-listing) + +| Param | Type | Description | +| --- | --- | --- | +| bucket | String | Bucket name | +| sort | String | Sort name | diff --git a/docs/buckets.md b/docs/buckets.md new file mode 100644 index 00000000..80072bd3 --- /dev/null +++ b/docs/buckets.md @@ -0,0 +1,50 @@ + + +## buckets +Collection of buckets of challenge + +* [challenge_buckets](#module_challenge_buckets) + * [.BUCKETS](#module_challenge_buckets.BUCKETS) + * [.BUCKET_DATA](#module_challenge_buckets.BUCKET_DATA) + * [.getBuckets(res)](#module_challenge_buckets.getBuckets) ⇒ Promise + * [.isReviewOpportunitiesBucket(res)](#module_challenge_buckets.isReviewOpportunitiesBucket) ⇒ Promise + * [.registerBucket](#module_challenge_buckets.registerBucket) + + +### challenge_buckets.BUCKETS +Bucket types +**Kind**: static constant of [challenge_buckets](#module_challenge_buckets) + + + +### challenge_buckets.BUCKET_DATA +The data of bucket +**Kind**: static constant of [challenge_buckets](#module_challenge_buckets) + + + +### challenge_buckets.getBuckets(userHandle) ⇒ Promise +Returns configuration of all possible challenge buckets. +**Kind**: static method of [challenge_buckets](#module_challenge_buckets) +**Returns**: Promise - Resolves to the payload. + +| Param | Type | +| --- | --- | +| res | Object | + + + +### challenge_buckets.isReviewOpportunitiesBucket(bucket) ⇒ Promise +Tests if a given bucket is of any of the Review Opportunities types +**Kind**: static method of [challenge_buckets](#module_challenge_buckets) +**Returns**: Promise - Resolves to the payload. + +| Param | Type | +| --- | --- | +| res | Boolean | + + +### challenge_buckets.registerBucket(id, bucket) ⇒ Promise +Registers a new bucket. +**Kind**: static method of [challenge_buckets](#module_challenge_buckets) + diff --git a/docs/index.md b/docs/index.md index ec065107..185c46d5 100644 --- a/docs/index.md +++ b/docs/index.md @@ -9,6 +9,11 @@ actions.challenge

Actions related to Topcoder challenges APIs.

+
+
+actions.challenge-listing
+

Actions related to Topcoder challenge-listing APIs.

+
actions.direct

Actions related to Direct API: access to projects, billing accounts, @@ -71,6 +76,11 @@ actions and reducer; thus, this module.

Reducer for actions.challenge actions.

State segment managed by this reducer has the following strcuture:

+
+
+reducers.challenge-listing
+

Reducer for actions.challenge-listing actions.

+
reducers.direct

Reducer for handling the results of Direct-related actions.

@@ -275,4 +285,19 @@ the proxy will forward them to the service only if LOG_ENTRIES_TOKEN is set).

Utility functions for time/date related stuff

- +
+sort
+

Collection of challenge sort.

+
+
+
+tc
+

Collection of challenge buckets.

+
+
+
+
+url
+

Collection of url function.

+
+
diff --git a/docs/reducers.challenge-listing.md b/docs/reducers.challenge-listing.md new file mode 100644 index 00000000..ff80e6f4 --- /dev/null +++ b/docs/reducers.challenge-listing.md @@ -0,0 +1,310 @@ + + +## reducers.challenge-listing +Reducer for [actions.challenge-listing](#module_actions.challenge-listing) actions. + +State segment managed by this reducer has the following strcuture: + +**Todo** + +- [ ] Document the structure. + + +* [reducers.challenge-listing](#module_reducers.challenge-listing) + * _static_ + * [.default](#module_reducers.challenge-listing.default) + * [.factory(options)](#module_reducers.challenge-listing.factory) ⇒ Promise + * _inner_ + * [~dropChallenges(state, action)](#module_reducers.challenge-listing..dropChallenges) ⇒ Object + * [~getMoreChallenges(state, action)](#module_reducers.challenge-listing..getMoreChallenges) ⇒ Object + * [~expandTag(state, action)](#module_reducers.challenge-listing..expandTag) ⇒ Object + * [~getAllActiveChallengesInit(state, action)](#module_reducers.challenge-listing..onGetAllActiveChallengesInit) ⇒ Object + * [~getAllActiveChallengesDone(state, action)](#module_reducers.challenge-listing..onGetAllActiveChallengesDone) ⇒ Object + * [~getActiveChallengesInit(state, action)](#module_reducers.challenge-listing..getActiveChallengesInit) ⇒ Object + * [~getActiveChallengesDone(state, action)](#module_reducers.challenge-listing..getActiveChallengesDone) ⇒ Object + * [~getRestActiveChallengesInit(state, action)](#module_reducers.challenge-listing..getRestActiveChallengesInit) ⇒ Object + * [~getRestActiveChallengesDone(state, action)](#module_reducers.challenge-listing..getRestActiveChallengesDone) ⇒ Object + * [~getChallengeSubtracksInit()](#module_reducers.challenge-listing..getChallengeSubtracksInit) ⇒ Object + * [~getChallengeSubtracksDone(state, action)](#module_reducers.challenge-listing..getChallengeSubtracksDone) ⇒ Object + * [~getChallengeTagsInit()](#module_reducers.challenge-listing..getChallengeTagsInit) ⇒ Object + * [~getChallengeTagsDone(state, action)](#module_reducers.challenge-listing..getChallengeTagsDone) ⇒ Object + * [~getPastChallengesInit(state, action)](#module_reducers.challenge-listing..getPastChallengesInit) ⇒ Object + * [~getReviewOpportunitiesInit(state, action)](#module_reducers.challenge-listing..getReviewOpportunitiesInit) ⇒ Object + * [~getReviewOpportunitiesDone(state, action)](#module_reducers.challenge-listing..getReviewOpportunitiesDone) ⇒ Object + * [~getSrmsInit(state, action)](#module_reducers.challenge-listing..getSrmsInit) ⇒ Object + * [~getSrmsDone(state, action)](#module_reducers.challenge-listing..getSrmsDone) ⇒ Object + * [~selectCommunity(state, action)](#module_reducers.challenge-listing..selectCommunity) ⇒ Object + * [~setFilter(state, action)](#module_reducers.challenge-listing..setFilter) ⇒ Object + * [~setSort(state, action)](#module_reducers.challenge-listing..setSort) ⇒ Object + * [~setDatePickerStatus(state, action)](#module_reducers.challenge-listing..setDatePickerStatus) ⇒ Object + * [~create(initialState)](#module_reducers.challenge..create) ⇒ function + + +### reducers.challenge-listing.default +Reducer with default intial state. +**Kind**: static property of [reducers.challenge-listing](#module_reducers.challenge-listing) + + +### reducers.challenge-listing.factory() ⇒ Promise +Factory which creates a new reducer with its initial state tailored to the +given options object, if specified (for server-side rendering). If options +object is not specified, it creates just the default reducer. Accepted options are: + +**Kind**: static method of [reducers.challenge-listing](#module_reducers.challenge-listing) +**Resolves**: Function(state, action): state New reducer. + + +### reducers.challenge-listing~dropChallenges(state, action) ⇒ Object +Handles CHALLENGE_LISTING/DROP_CHALLENGES action. +**Kind**: inner method of [reducers.challenge-listing](#module_reducers.challenge-listing) +**Returns**: Object - New state + +| Param | Type | +| --- | --- | +| state | Object | +| action | Object | + + +### reducers.challenge-listing~getMoreChallenges(state, action) ⇒ Object +Handles CHALLENGE_LISTING/GET_MORE_CHALLENGES action. +**Kind**: inner method of [reducers.challenge-listing](#module_reducers.challenge-listing) +**Returns**: Object - New state + +| Param | Type | +| --- | --- | +| state | Object | +| action | Object | + + +### reducers.challenge-listing~expandTag(state, action) ⇒ Object +Handles CHALLENGE_LISTING/EXPAND_TAG action. +**Kind**: inner method of [reducers.challenge-listing](#module_reducers.challenge-listing) +**Returns**: Object - New state + +| Param | Type | +| --- | --- | +| state | Object | +| action | Object | + + +### reducers.challenge-listing~getAllActiveChallengesInit(state, action) ⇒ Object +Handles CHALLENGE_LISTING/GET_ALL_ACTIVE_CHALLENGES_INIT action. +**Kind**: inner method of [reducers.challenge-listing](#module_reducers.challenge-listing) +**Returns**: Object - New state + +| Param | Type | +| --- | --- | +| state | Object | +| action | Object | + + +### reducers.challenge-listing~getAllActiveChallengesDone(state, action) ⇒ Object +Handles CHALLENGE_LISTING/GET_ALL_ACTIVE_CHALLENGES_DONE action. +**Kind**: inner method of [reducers.challenge-listing](#module_reducers.challenge-listing) +**Returns**: Object - New state + +| Param | Type | +| --- | --- | +| state | Object | +| action | Object | + + +### reducers.challenge-listing~getActiveChallengesInit(state, action) ⇒ Object +Handles CHALLENGE_LISTING/GET_ACTIVE_CHALLENGES_INIT action. +**Kind**: inner method of [reducers.challenge-listing](#module_reducers.challenge-listing) +**Returns**: Object - New state + +| Param | Type | +| --- | --- | +| state | Object | +| action | Object | + + +### reducers.challenge-listing~getActiveChallengesDone(state, action) ⇒ Object +Handles CHALLENGE_LISTING/GET_ACTIVE_CHALLENGES_DONE action. +**Kind**: inner method of [reducers.challenge-listing](#module_reducers.challenge-listing) +**Returns**: Object - New state + +| Param | Type | +| --- | --- | +| state | Object | +| action | Object | + + +### reducers.challenge-listing~getRestActiveChallengesInit(state, action) ⇒ Object +Handles CHALLENGE_LISTING/GET_REST_ACTIVE_CHALLENGES_INIT action. +**Kind**: inner method of [reducers.challenge-listing](#module_reducers.challenge-listing) +**Returns**: Object - New state + +| Param | Type | +| --- | --- | +| state | Object | +| action | Object | + + +### reducers.challenge-listing~getRestActiveChallengesDone(state, action) ⇒ Object +Handles CHALLENGE_LISTING/GET_REST_ACTIVE_CHALLENGES_DONE action. +**Kind**: inner method of [reducers.challenge-listing](#module_reducers.challenge-listing) +**Returns**: Object - New state + +| Param | Type | +| --- | --- | +| state | Object | +| action | Object | + + +### reducers.challenge-listing~getChallengeSubtracksInit() ⇒ Object +Handles CHALLENGE_LISTING/GET_CHALLENGE_SUBTRACKS_INIT action. +**Kind**: inner method of [reducers.challenge-listing](#module_reducers.challenge-listing) + + +### reducers.challenge-listing~getChallengeSubtracksDone(state, action) ⇒ Object +Handles CHALLENGE_LISTING/GET_CHALLENGE_SUBTRACKS_DONE action. +**Kind**: inner method of [reducers.challenge-listing](#module_reducers.challenge-listing) + +**Returns**: Object - New state + +| Param | Type | +| --- | --- | +| state | Object | +| action | Object | + + +### reducers.challenge-listing~getChallengeTagsInit() ⇒ Object +Handles CHALLENGE_LISTING/GET_CHALLENGE_TAGS_INIT action. +**Kind**: inner method of [reducers.challenge-listing](#module_reducers.challenge-listing) + + +### reducers.challenge-listing~getChallengeTagsDone(state, action) ⇒ Object +Handles CHALLENGE_LISTING/GET_CHALLENGE_TAGS_DONE action. +**Kind**: inner method of [reducers.challenge-listing](#module_reducers.challenge-listing) + +**Returns**: Object - New state + +| Param | Type | +| --- | --- | +| state | Object | +| action | Object | + + +### reducers.challenge-listing~getPastChallengesInit(state, action) ⇒ Object +Handles CHALLENGE_LISTING/GET_PAST_CHALLENGES_INIT action. +**Kind**: inner method of [reducers.challenge-listing](#module_reducers.challenge-listing) + +**Returns**: Object - New state + +| Param | Type | +| --- | --- | +| state | Object | +| action | Object | + + +### reducers.challenge-listing~getPastChallengesDone(state, action) ⇒ Object +Handles CHALLENGE_LISTING/GET_PAST_CHALLENGES_DONE action. +**Kind**: inner method of [reducers.challenge-listing](#module_reducers.challenge-listing) + +**Returns**: Object - New state + +| Param | Type | +| --- | --- | +| state | Object | +| action | Object | + + +### reducers.challenge-listing~getReviewOpportunitiesInit(state, action) ⇒ Object +Handles CHALLENGE_LISTING/GET_REVIEW_OPPORTUNITIES_INIT action. +**Kind**: inner method of [reducers.challenge-listing](#module_reducers.challenge-listing) + +**Returns**: Object - New state + +| Param | Type | +| --- | --- | +| uuid | Object | +| action | Object | + + +### reducers.challenge-listing~getReviewOpportunitiesDone(state, action) ⇒ Object +Handles CHALLENGE_LISTING/GET_REVIEW_OPPORTUNITIES_DONE action. +**Kind**: inner method of [reducers.challenge-listing](#module_reducers.challenge-listing) + +**Returns**: Object - New state + +| Param | Type | +| --- | --- | +| uuid | Object | +| action | Object | + + +### reducers.challenge-listing~getSrmsInit(state, action) ⇒ Object +Handles CHALLENGE_LISTING/GET_SRMS_INIT action. +**Kind**: inner method of [reducers.challenge-listing](#module_reducers.challenge-listing) + +**Returns**: Object - New state + +| Param | Type | +| --- | --- | +| uuid | Object | +| action | Object | + + +### reducers.challenge-listing~getSrmsDone(state, action) ⇒ Object +Handles CHALLENGE_LISTING/GET_SRMS_DONE action. +**Kind**: inner method of [reducers.challenge-listing](#module_reducers.challenge-listing) + +**Returns**: Object - New state + +| Param | Type | +| --- | --- | +| uuid | Object | +| action | Object | + + + +### reducers.challenge-listing~selectCommunity(state, action) ⇒ Object +Handles CHALLENGE_LISTING/SELECT_COMMUNITY action. +**Kind**: inner method of [reducers.challenge-listing](#module_reducers.challenge-listing) + +**Returns**: Object - New state + +| Param | Type | +| --- | --- | +| uuid | Object | +| action | Object | + + +### reducers.challenge-listing~setFilter(state, action) ⇒ Object +Handles CHALLENGE_LISTING/SET_FILTER action. +**Kind**: inner method of [reducers.challenge-listing](#module_reducers.challenge-listing) + +**Returns**: Object - New state + +| Param | Type | +| --- | --- | +| uuid | Object | +| action | Object | + + +### reducers.challenge-listing~setDatePickerStatus(state, action) ⇒ Object +Handles CHALLENGE_LISTING/SET_DATEPICKER_STATUS action. +**Kind**: inner method of [reducers.challenge-listing](#module_reducers.challenge-listing) + +**Returns**: Object - New state + +| Param | Type | +| --- | --- | +| uuid | Object | +| action | Object | + + + + +### reducers.challenge-listing~create(initialState) ⇒ function +Creates a new Challenge-listing reducer with the specified initial state. + +**Kind**: inner method of [reducers.challenge-listing](#module_reducers.challenge-listing) +**Returns**: function - Challenge-listing reducer. + +| Param | Type | Description | +| --- | --- | --- | +| initialState | Object | Optional. Initial state. | + diff --git a/docs/sort.md b/docs/sort.md new file mode 100644 index 00000000..a7f21d61 --- /dev/null +++ b/docs/sort.md @@ -0,0 +1,20 @@ + + +## sort +Collection of challenge list sort + +* [challenge_sort](#module_challenge_sort) + * [.SORTS](#module_challenge_sort.BSORTS) + * [.SORTS_DATA](#module_challenge_sort.SORTS_DATA) + + + +### challenge_sort.SORTS +Sort types +**Kind**: static constant of [challenge_sort](#module_challenge_sort) + + + +### challenge_sort.SORTS_DATA +The data of sort +**Kind**: static constant of [challenge_sort](#module_challenge_sort) diff --git a/docs/tc.md b/docs/tc.md index 8e060798..1345ed26 100644 --- a/docs/tc.md +++ b/docs/tc.md @@ -11,23 +11,34 @@ Collection of small Topcoder-related functions. * [tc](#module_tc) * [.REVIEW_OPPORTUNITY_TYPES](#module_tc.REVIEW_OPPORTUNITY_TYPES) * [.getApiResponsePayload(res)](#module_tc.getApiResponsePayload) ⇒ Promise + * [.processSRM(res)](#module_tc.processSRM) ⇒ Promise ### tc.REVIEW_OPPORTUNITY_TYPES Review Opportunity types -**Kind**: static constant of [tc](#module_tc) +**Kind**: static constant of [tc](#module_tc) ### tc.getApiResponsePayload(res) ⇒ Promise -Gets payload from a standard success response from TC v2 API; or throws +Gets payload from a standard success response from TC API; or throws an error in case of a failure response. -**Kind**: static method of [tc](#module_tc) +**Kind**: static method of [tc](#module_tc) **Returns**: Promise - Resolves to the payload. | Param | Type | | --- | --- | | res | Object | + +### tc.processSRM(res) ⇒ Promise +process srm to populate additional infomation + +**Kind**: static method of [tc](#module_tc) +**Returns**: Promise - Resolves to the payload. + +| Param | Type | +| --- | --- | +| res | Object | diff --git a/docs/url.md b/docs/url.md new file mode 100644 index 00000000..acdfb108 --- /dev/null +++ b/docs/url.md @@ -0,0 +1,27 @@ + + +## url +Collection of url functions. + +* [url](#module_url) + * [.updateQuery](#module_url.updateQuery) + * [.removeTrailingSlash(res)](#module_url.removeTrailingSlash) ⇒ Promise + + +### url.updateQuery +If executed client-side (determined in this case by the presence of global + * window object), this function updates query section of URL; otherwise does + * nothing. +**Kind**: static method of [tc](#module_url) + + + +### url.removeTrailingSlash(res) ⇒ Promise +Cleans/removes trailing slash from url +**Kind**: static method of [url](#module_url) +**Returns**: Promise - Resolves to the payload. + +| Param | Type | +| --- | --- | +| res | String | + diff --git a/src/actions/challenge-listing.js b/src/actions/challenge-listing.js new file mode 100644 index 00000000..d8acf22a --- /dev/null +++ b/src/actions/challenge-listing.js @@ -0,0 +1,450 @@ +/** + * Challenge listing actions. + */ + +import _ from 'lodash'; +import { createActions } from 'redux-actions'; +import { decodeToken } from 'tc-accounts'; +import 'isomorphic-fetch'; +import { processSRM, COMPETITION_TRACKS } from '../utils/tc'; +import { services } from '../services'; +import { errors } from '../utils'; +import * as filterUtil from '../utils/challenge/filter'; +import * as config from '../config'; + +const { fireErrorMessage } = errors; +const { getService } = services.challenge; +const { getReviewOpportunitiesService } = services.reviewOpportunities; +const { PAGE_SIZE, REVIEW_OPPORTUNITY_PAGE_SIZE } = config; + +/** + * Process filter + * Development challenges having Data Science tech tag, still should be + * included into data science track. + * @param filter + * @returns {string} + */ +function processFilter(filter) { + const newFilter = _.clone(filter); + if (_.has(filter, 'track') + && filter.track.includes(COMPETITION_TRACKS.DATA_SCIENCE.toUpperCase()) + && !filter.track.includes(COMPETITION_TRACKS.DEVELOP.toUpperCase()) + ) { + newFilter.track = `${newFilter.track},${COMPETITION_TRACKS.DEVELOP.toUpperCase()}`; + } + return newFilter; +} + +/** + * Private. Loads from the backend all challenges matching some conditions. + * @param {Function} getter Given params object of shape { limit, offset } + * loads from the backend at most "limit" challenges, skipping the first + * "offset" ones. Returns loaded challenges as an array. + * @param {Number} page Optional. Next page of challenges to load. + * @param {Array} prev Optional. Challenges loaded so far. + */ +function getAll(getter, page = 0, prev) { + /* Amount of challenges to fetch in one API call. 50 is the current maximum + * amount of challenges the backend returns, event when the larger limit is + * explicitely required. */ + return getter({ + limit: PAGE_SIZE, + offset: page * PAGE_SIZE, + }).then((res) => { + if (res.challenges.length === 0) { + return prev || res; + } + // parse challenges and meta + let current = {}; + if (prev) { + current.challenges = prev.challenges.concat(res.challenges); + current.meta = res.meta; + } else { + current = res; + } + return getAll(getter, 1 + page, current); + }); +} + +/** + * Gets possible challenge subtracks. + * @return {Promise} + */ +function getChallengeSubtracksDone() { + return getService() + .getChallengeSubtracks() + .then(res => res.sort((a, b) => a.name.localeCompare(b.name))); +} + +/** + * Gets possible challenge tags (technologies). + * @return {Promise} + */ +function getChallengeTagsDone() { + return getService() + .getChallengeTags() + .then(res => res.map(item => item.name) + .sort((a, b) => a.localeCompare(b))); +} + +/** + * Notifies about reloading of all active challenges. The UUID is stored in the + * state, and only challenges fetched by getAllActiveChallengesDone action with + * the same UUID will be accepted into the state. + * @param {String} uuid + * @param {String} page + * @param {Object} frontFilter + * @param {String} sort + * @param {String} bucket + * @return {String} + */ +function getActiveChallengesInit(uuid, page, frontFilter, sort, bucket) { + return { + uuid, page, frontFilter, sort, bucket, + }; +} + +/** TODO: Inspect if the 2 actions bellow can be removed? + * They do duplicate what is done in `getActiveChallengesDone` but fetch all challenges + * which was refactored in listing-improve + */ +function getAllActiveChallengesInit(uuid) { + return uuid; +} +function getAllActiveChallengesDone(uuid, tokenV3) { + const filter = { status: 'ACTIVE' }; + const service = getService(tokenV3); + const calls = [ + getAll(params => service.getChallenges(filter, params)), + ]; + let user; + if (tokenV3) { + user = decodeToken(tokenV3).handle; + // Handle any errors on this endpoint so that the non-user specific challenges + // will still be loaded. + calls.push(getAll(params => service.getUserChallenges(user, filter, params) + .catch(() => ({ challenges: [] })))); + } + return Promise.all(calls).then(([ch, uch]) => { + /* uch array contains challenges where the user is participating in +@@ -111,8 +124,8 @@ function getAllActiveChallengesDone(uuid, tokenV3) { + * challenges in an efficient way. */ + if (uch) { + const map = {}; + uch.challenges.forEach((item) => { map[item.id] = item; }); + ch.challenges.forEach((item) => { + if (map[item.id]) { + /* It is fine to reassing, as the array we modifying is created just + * above within the same function. */ + /* eslint-disable no-param-reassign */ + item.users[user] = true; + item.userDetails = map[item.id].userDetails; + /* eslint-enable no-param-reassign */ + } + }); + } + + return { uuid, challenges: ch.challenges }; + }); +} + +/** + * Gets 1 page of active challenges (including marathon matches) from the backend. + * Once this action is completed any active challenges saved to the state before + * will be dropped, and the newly fetched ones will be stored there. + * Loading of all challenges wil start in background. + * @param {String} uuid + * @param {Number} page + * @param {Object} backendFilter Backend filter to use. + * @param {String} tokenV3 Optional. Topcoder auth token v3. Without token only + * public challenges will be fetched. With the token provided, the action will + * also fetch private challenges related to this user. + * @param {Object} frontFilter + * @param {String} sort + * @param {String} bucket + + * @return {Promise} + */ +function getActiveChallengesDone( + uuid, page, backendFilter, tokenV3, frontFilter = {}, sort, bucket, +) { + const filter = processFilter({ + ...backendFilter, + status: 'ACTIVE', + }); + + const service = getService(tokenV3); + const calls = [ + service.getChallenges(filter, { + limit: PAGE_SIZE, + offset: page * PAGE_SIZE, + }), + ]; + let user; + if (tokenV3) { + user = decodeToken(tokenV3).handle; + // Handle any errors on this endpoint so that the non-user specific challenges + // will still be loaded. + calls.push(service.getUserChallenges(user, filter, { + limit: PAGE_SIZE, + offset: page * PAGE_SIZE, + }).catch(() => ({ challenges: [] }))); + } + return Promise.all(calls).then(([ch, uch]) => { + /* uch array contains challenges where the user is participating in + * some role. The same challenge are already listed in res array, but they + * are not attributed to the user there. This block of code marks user + * challenges in an efficient way. */ + if (uch) { + const map = {}; + uch.challenges.forEach((item) => { map[item.id] = item; }); + ch.challenges.forEach((item) => { + if (map[item.id]) { + /* It is fine to reassing, as the array we modifying is created just + * above within the same function. */ + /* eslint-disable no-param-reassign */ + item.users[user] = true; + item.userDetails = map[item.id].userDetails; + /* eslint-enable no-param-reassign */ + } + }); + } + + let { challenges, meta } = ch; + // filter by date range and re-compute meta + // we can safely remove the next two lines when backend support date range + challenges = filterUtil.filterByDate(challenges, frontFilter); + meta = filterUtil.newMeta(meta, challenges, frontFilter); + return { + uuid, + handle: tokenV3 ? user : null, + challenges, + meta, + frontFilter, + sort, + bucket, + tokenV3, + }; + }); +} + +/** + * Init loading of all challenges + * @param {String} uuid + */ +function getRestActiveChallengesInit(uuid) { + return { uuid }; +} + +/** + * Loading all challenges + * @param {String} uuid + * @param {String} tokenV3 + * @param {Object} backendFilter + * @param {Object} frontFilter + * @param {String} sort + * @param {String} bucket + */ +function getRestActiveChallengesDone( + uuid, tokenV3, backendFilter, frontFilter, sort, bucket, +) { + const filter = processFilter({ + ...backendFilter, + status: 'ACTIVE', + }); + + const service = getService(tokenV3); + const calls = [ + getAll(params => service.getChallenges(filter, params), 1), + ]; + let user; + if (tokenV3) { + user = decodeToken(tokenV3).handle; + calls.push(getAll(params => service.getUserChallenges(user, filter, params) + .catch(() => ({ challenges: [] }))), 1); + } + return Promise.all(calls).then(([ch, uch]) => { + /* uch array contains challenges where the user is participating in + * some role. The same challenge are already listed in res array, but they + * are not attributed to the user there. This block of code marks user + * challenges in an efficient way. */ + if (uch) { + const map = {}; + uch.challenges.forEach((item) => { map[item.id] = item; }); + ch.challenges.forEach((item) => { + if (map[item.id]) { + /* It is fine to reassing, as the array we modifying is created just + * above within the same function. */ + /* eslint-disable no-param-reassign */ + item.users[user] = true; + item.userDetails = map[item.id].userDetails; + /* eslint-enable no-param-reassign */ + } + }); + } + + let { challenges } = ch; + // filter by date range and re-compute meta + // we can safely remove the next two lines when backend support date range + challenges = filterUtil.filterByDate(challenges, frontFilter); + const meta = filterUtil.newMeta(undefined, challenges, frontFilter); + + return { + uuid, + handle: tokenV3 ? user : null, + challenges, + frontFilter, + meta, + sort, + bucket, + }; + }); +} + +/** + * Notifies the state that we are about to load the specified page of past + * challenges. + * @param {String} uuid + * @param {Number} page + * @param {Object} frontFilter + * @param {String} sort + * @return {Object} + */ +function getPastChallengesInit(uuid, page, frontFilter, sort) { + return { + uuid, + page, + frontFilter, + sort, + }; +} + +/** + * Gets the specified page of past challenges (including MMs). + * @param {String} uuid + * @param {Number} page Page of challenges to fetch. + * @param {Object} filter Backend filter to use. + * @param {String} tokenV3 Optional. Topcoder auth token v3. + * @param {Object} frontFilter Optional. Original frontend filter. + * @param {String} sort + * @return {Object} + */ +function getPastChallengesDone(uuid, page, filter, tokenV3, frontFilter = {}, sort) { + const service = getService(tokenV3); + const newFilter = processFilter({ + ...filter, + status: 'COMPLETED', + }); + return service.getChallenges(newFilter, { + limit: PAGE_SIZE, + offset: page * PAGE_SIZE, + }).then(({ challenges }) => ({ + uuid, + challenges, + frontFilter, + sort, + })); +} + +/** + * Action to get a list of currently open Review Opportunities using V3 API + * @param {String} uuid Unique identifier for init/donen instance from shortid module + * @param {Number} page Page of review opportunities to fetch. + * @param {String} tokenV3 Optional. + * @param {String} sort Optional. + * @param {Object} frontFilter Optional. + * @return {Object} Action object + */ +function getReviewOpportunitiesDone(uuid, page, tokenV3, sort, frontFilter = {}) { + return getReviewOpportunitiesService(tokenV3) + .getReviewOpportunities(REVIEW_OPPORTUNITY_PAGE_SIZE, page * REVIEW_OPPORTUNITY_PAGE_SIZE) + .then(loaded => ({ + uuid, loaded, sort, frontFilter, + })) + .catch((error) => { + fireErrorMessage('Error Getting Review Opportunities', error.content || error); + return Promise.reject(error); + }); +} + +/** + * Payload creator for the action that inits the loading of SRMs. + * @param {String} uuid + * @return {String} + */ +function getSrmsInit(uuid) { + return uuid; +} + +/** + * Payload creator for the action that loads SRMs. + * @param {String} uuid + * @param {String} handle + * @param {Object} params + * @param {String} tokenV3 + */ +function getSrmsDone(uuid, handle, params, tokenV3) { + const service = getService(tokenV3); + const promises = [service.getSrms(params)]; + if (handle) { + promises.push(service.getUserSrms(handle, params)); + } + return Promise.all(promises).then((data) => { + let srms = data[0]; + const userSrms = data[1]; + const userSrmsMap = {}; + _.forEach(userSrms, (srm) => { + userSrmsMap[srm.id] = srm; + }); + srms = _.map(srms, (srm) => { + if (userSrmsMap[srm.id]) { + return processSRM(srm); + } + return srm; + }); + return { uuid, data: srms }; + }); +} + +export default createActions({ + CHALLENGE_LISTING: { + DROP_CHALLENGES: bucket => ({ bucket }), + + GET_MORE_CHALLENGES: bucket => ({ bucket }), + + GET_ALL_ACTIVE_CHALLENGES_INIT: getAllActiveChallengesInit, + GET_ALL_ACTIVE_CHALLENGES_DONE: getAllActiveChallengesDone, + + GET_ACTIVE_CHALLENGES_INIT: getActiveChallengesInit, + GET_ACTIVE_CHALLENGES_DONE: getActiveChallengesDone, + + GET_REST_ACTIVE_CHALLENGES_INIT: getRestActiveChallengesInit, + GET_REST_ACTIVE_CHALLENGES_DONE: getRestActiveChallengesDone, + + GET_CHALLENGE_SUBTRACKS_INIT: _.noop, + GET_CHALLENGE_SUBTRACKS_DONE: getChallengeSubtracksDone, + + GET_CHALLENGE_TAGS_INIT: _.noop, + GET_CHALLENGE_TAGS_DONE: getChallengeTagsDone, + + GET_PAST_CHALLENGES_INIT: getPastChallengesInit, + GET_PAST_CHALLENGES_DONE: getPastChallengesDone, + + GET_REVIEW_OPPORTUNITIES_INIT: (uuid, page, sort) => ({ uuid, page, sort }), + GET_REVIEW_OPPORTUNITIES_DONE: getReviewOpportunitiesDone, + + GET_SRMS_INIT: getSrmsInit, + GET_SRMS_DONE: getSrmsDone, + + EXPAND_TAG: id => id, + + /* Pass in community ID. */ + SELECT_COMMUNITY: _.identity, + + SET_FILTER: _.identity, + + SET_DATEPICKER_STATUS: status => ({ status }), + + SET_SORT: (bucket, sort) => ({ bucket, sort }), + }, +}); diff --git a/src/actions/index.js b/src/actions/index.js index 8b1a241b..db1d6f66 100644 --- a/src/actions/index.js +++ b/src/actions/index.js @@ -13,6 +13,7 @@ import reviewOpportunityActions from './reviewOpportunity'; import lookupActions from './lookup'; import settingsActions from './settings'; import lookerActions from './looker'; +import challengeListingActions from './challenge-listing'; export const actions = { auth: authActions.auth, @@ -30,6 +31,7 @@ export const actions = { lookup: lookupActions.lookup, settings: settingsActions.settings, looker: lookerActions.looker, + challengeListing: challengeListingActions.challengeListing, }; export default undefined; diff --git a/src/config/index.js b/src/config/index.js new file mode 100644 index 00000000..2a5ab790 --- /dev/null +++ b/src/config/index.js @@ -0,0 +1,4 @@ +module.exports = { + PAGE_SIZE: 50, + REVIEW_OPPORTUNITY_PAGE_SIZE: 1000, +}; diff --git a/src/index.js b/src/index.js index a108716a..15b0e7d7 100644 --- a/src/index.js +++ b/src/index.js @@ -12,5 +12,5 @@ export { actions } from './actions'; export { services } from './services'; export { - challenge, logger, errors, tc, time, mock, + challenge, logger, errors, tc, time, mock, url, } from './utils'; diff --git a/src/reducers/challenge-listing.js b/src/reducers/challenge-listing.js new file mode 100644 index 00000000..b1d87967 --- /dev/null +++ b/src/reducers/challenge-listing.js @@ -0,0 +1,848 @@ +/** + * Reducer for state.challengeListing. + */ + +import _ from 'lodash'; +import { handleActions } from 'redux-actions'; +import moment from 'moment'; +import { updateQuery } from '../utils/url'; +import { SORTS_DATA } from '../utils/challenge/sort'; +import actions from '../actions/challenge-listing'; +import { logger, errors, challenge as challengeUtils } from '../utils'; + +const { fireErrorMessage } = errors; +const { filter: Filter } = challengeUtils; +const { BUCKETS, BUCKET_DATA, getBuckets } = challengeUtils.buckets; + +/** + * Process challenge data for bucket + * @param handle user handle + * @param challenges all challenges + * @param loaded fetched challenges of bucket + * @param bucket bucket name + * @param sorts all sorts data + * @param sort sort name + * @param filter filter object + * @param frontFilter filter object + */ +function processBucketData(handle, challenges, loaded, bucket, sorts, sort, filter, frontFilter) { + const buckets = _.isEmpty(handle) ? BUCKET_DATA : getBuckets(handle); + const data = _.has(challenges, bucket) ? challenges[bucket] + .filter(filter) + .concat(loaded) : _.clone(loaded); + + const finalFilters = { + ...frontFilter, + ...buckets[bucket].filter, + }; + + const bucketFilter = bucket !== BUCKETS.REVIEW_OPPORTUNITIES + ? Filter.getFilterFunction(finalFilters) + : Filter.getReviewOpportunitiesFilterFunction(finalFilters); + const filteredData = []; + for (let i = 0; i < data.length; i += 1) { + if (bucketFilter(data[i])) { + filteredData.push(data[i]); + } + } + + if (bucket !== BUCKETS.ALL) { + if (!_.isEmpty(sort)) { + filteredData.sort(SORTS_DATA[sort].func); + return filteredData; + } + + if (_.has(sorts, bucket)) { + filteredData.sort(SORTS_DATA[sorts[bucket]].func); + } else { + filteredData.sort(SORTS_DATA[BUCKET_DATA[bucket].sorts[0]].func); + } + } + + return filteredData; +} + +/** + * Check the challenges of bucket have been loaded all + * @param challenges all challenges + * @param bucket bucket name + * @param loaded loaded challenges this time + * @param data processed data + * @returns {boolean} + */ +function checkAllLoaded(challenges, bucket, loaded, data) { + let isAll = false; + if (loaded.length === 0) { + isAll = true; + } else if (!_.isEmpty(_.get(challenges, bucket)) + && challenges[bucket].length === data.length) { + isAll = true; + } + + return isAll; +} + +/** TODO: Inspect if the 2 actions bellow can be removed? + * They do duplicate what is done in `getActiveChallengesDone` but fetch all challenges + * which was refactored in listing-improve + */ +function onGetAllActiveChallengesInit(state, { payload }) { + return { ...state, loadingActiveChallengesUUID: payload }; +} +function onGetAllActiveChallengesDone(state, { error, payload }) { + if (error) { + logger.error(payload); + return state; + } + const { uuid, challenges: loaded } = payload; + if (uuid !== state.loadingActiveChallengesUUID) return state; + /* Once all active challenges are fetched from the API, we remove from the + * store any active challenges stored there previously, and also any + * challenges with IDs matching any challenges loaded now as active. */ + const ids = new Set(); + loaded.forEach(item => ids.add(item.id)); + const challenges = state.challenges + .filter(item => item.status !== 'ACTIVE' && !ids.has(item.id)) + .concat(loaded); + + return { + ...state, + challenges, + lastUpdateOfActiveChallenges: Date.now(), + loadingActiveChallengesUUID: '', + }; +} + +/** + * Called when 1st page of ative challenges is loaded from `/challenges` api + * @param {*} state + * @param {*} param1 + */ +function onGetActiveChallengesDone(state, { error, payload }) { + if (error) { + logger.error(payload); + return state; + } + const { + uuid, challenges: loaded, sort, bucket, tokenV3, handle, frontFilter, + meta, + } = payload; + + /* Once all active challenges are fetched from the API, we remove from the + * store any active challenges stored there previously, and also any + * challenges with IDs matching any challenges loaded now as active. */ + const ids = new Set(); + loaded.forEach(item => ids.add(item.id)); + + let filter; + let newChallenges = {}; + const otherState = {}; + switch (bucket) { + case BUCKETS.ALL: { + if (uuid !== state.loadingActiveChallengesUUID) return state; + /* Fetching 0 page of active challenges also drops any active challenges + * loaded to the state before. */ + filter = state.lastRequestedPageOfActiveChallenges + ? item => !ids.has(item.id) + : item => !ids.has(item.id) && item.status !== 'ACTIVE'; + + // my + const my = !_.isEmpty(tokenV3) ? processBucketData( + handle, state.challenges, loaded, BUCKETS.MY, state.sorts, sort, filter, + ) : []; + // open for registration + const open = processBucketData( + handle, state.challenges, loaded, BUCKETS.OPEN_FOR_REGISTRATION, state.sorts, sort, filter, + ); + // ongoing + const ongoing = processBucketData( + handle, state.challenges, loaded, BUCKETS.ONGOING, state.sorts, sort, filter, + ); + newChallenges = _.clone(state.challenges); + newChallenges[BUCKETS.MY] = my; + newChallenges[BUCKETS.OPEN_FOR_REGISTRATION] = open; + newChallenges[BUCKETS.ONGOING] = ongoing; + otherState.loadingActiveChallengesUUID = ''; + otherState.meta = _.clone(meta); + } + break; + case BUCKETS.MY: { + if (uuid !== state.loadingMyChallengesUUID) return state; + /* Fetching 0 page of active challenges also drops any active challenges + * loaded to the state before. */ + filter = state.lastRequestedPageOfMyChallenges + ? item => !ids.has(item.id) + : item => !ids.has(item.id) && item.status !== 'ACTIVE'; + + const data = processBucketData( + handle, state.challenges, loaded, bucket, state.sorts, sort, filter, + ); + newChallenges = _.cloneDeep(state.challenges); + newChallenges[bucket] = data; + otherState.loadingMyChallengesUUID = ''; + otherState.allMyChallengesLoaded = checkAllLoaded(state.challenges, bucket, loaded, data); + otherState.gettingMoreMyChallenges = !otherState.allMyChallengesLoaded; + otherState.meta = _.clone(meta); + /* TODO Due to the meta of backend response is currently not correct, +/* so should update counts after fetch all challenges of bucket */ + if (_.get(meta, 'myChallengesCount') !== data.length && otherState.allMyChallengesLoaded) { + otherState.meta.myChallengesCount = data.length; + otherState.meta.allChallengesCount = meta.allChallengesCount + + data.length - meta.myChallengesCount; + } + } + break; + case BUCKETS.OPEN_FOR_REGISTRATION: { + if (uuid !== state.loadingOpenChallengesUUID) return state; + /* Fetching 0 page of active challenges also drops any active challenges + * loaded to the state before. */ + filter = state.lastRequestedPageOfOpenChallenges + ? item => !ids.has(item.id) + : item => !ids.has(item.id) && item.status !== 'ACTIVE'; + + const data = processBucketData( + handle, state.challenges, loaded, bucket, state.sorts, sort, filter, + ); + + newChallenges = _.cloneDeep(state.challenges); + newChallenges[bucket] = data; + otherState.loadingOpenChallengesUUID = ''; + otherState.allOpenChallengesLoaded = checkAllLoaded(state.challenges, bucket, loaded, data); + otherState.gettingMoreOpenChallenges = !otherState.allOpenChallengesLoaded; + otherState.meta = _.clone(meta); + /* TODO Due to the meta of backend response is currently not correct, + /* so should update counts after fetch all challenges of bucket */ + if (_.get(meta, 'openChallengesCount') !== data.length && otherState.allOpenChallengesLoaded) { + otherState.meta.openChallengesCount = data.length; + otherState.meta.allChallengesCount = meta.allChallengesCount + + data.length - meta.openChallengesCount; + } + } + break; + case BUCKETS.ONGOING: { + if (uuid !== state.loadingOnGoingChallengesUUID) return state; + /* Fetching 0 page of active challenges also drops any active challenges + * loaded to the state before. */ + filter = state.lastRequestedPageOfOnGoingChallenges + ? item => !ids.has(item.id) + : item => !ids.has(item.id) && item.status !== 'ACTIVE'; + + const data = processBucketData( + handle, state.challenges, loaded, bucket, state.sorts, sort, filter, + ); + newChallenges = _.cloneDeep(state.challenges); + newChallenges[bucket] = data; + otherState.loadingOnGoingChallengesUUID = ''; + otherState.allOnGoingChallengesLoaded = checkAllLoaded(state.challenges, + bucket, loaded, data); + otherState.gettingMoreOnGoingChallenges = !otherState.allOnGoingChallengesLoaded; + /* TODO Due to the meta of backend response is currently not correct, + /* so should update counts after fetch all challenges of bucket */ + otherState.meta = _.clone(meta); + if (_.get(meta, 'ongoingChallengesCount') !== data.length && otherState.allOnGoingChallengesLoaded) { + otherState.meta.ongoingChallengesCount = data.length; + otherState.meta.allChallengesCount = meta.allChallengesCount + + data.length - meta.ongoingChallengesCount; + } + } + break; + default: + break; + } + + // all challenges used for other components like sub communities + newChallenges[BUCKETS.ALL] = processBucketData( + handle, state.challenges, loaded, BUCKETS.ALL, null, null, filter, frontFilter, + ); + + return { + ...state, + ...otherState, + challenges: newChallenges, + lastUpdateOfActiveChallenges: Date.now(), + }; +} + +/** + * Called when loading of 1st page of active challenges is started + * @param {*} state + * @param {*} param1 + */ +function onGetActiveChallengesInit(state, { payload }) { + const { page, bucket, uuid } = payload; + const otherState = {}; + switch (bucket) { + case BUCKETS.ALL: + otherState.loadingActiveChallengesUUID = uuid; + otherState.lastRequestedPageOfActiveChallenges = page; + break; + case BUCKETS.MY: + otherState.loadingMyChallengesUUID = uuid; + otherState.lastRequestedPageOfMyChallenges = page; + break; + case BUCKETS.OPEN_FOR_REGISTRATION: + otherState.loadingOpenChallengesUUID = uuid; + otherState.lastRequestedPageOfOpenChallenges = page; + break; + case BUCKETS.ONGOING: + otherState.loadingOnGoingChallengesUUID = uuid; + otherState.lastRequestedPageOfOnGoingChallenges = page; + break; + default: + break; + } + + return { + ...state, + ...otherState, + }; +} +function onGetRestActiveChallengesInit(state, { payload }) { + return { + ...state, + loadingRestActiveChallengesUUID: payload.uuid, + }; +} + +/** + * Called when all challenges are loaded + * @param {*} state + * @param {*} param1 + */ +function onGetRestActiveChallengesDone(state, { error, payload }) { + if (error) { + logger.error(payload); + return state; + } + const { + uuid, challenges: loaded, meta: newMeta, sort, bucket, handle, frontFilter, + } = payload; + if (uuid !== state.loadingRestActiveChallengesUUID) return state; + + /* Once all active challenges are fetched from the API, we remove from the + * store any active challenges stored there previously, and also any + * challenges with IDs matching any challenges loaded now as active. */ + const ids = new Set(); + loaded.forEach(item => ids.add(item.id)); + + /* Fetching 0 page of active challenges also drops any active challenges + * loaded to the state before. */ + const filter = item => !ids.has(item.id); + + const otherState = {}; + let newChallenges = {}; + switch (bucket) { + case BUCKETS.MY: + case BUCKETS.OPEN_FOR_REGISTRATION: + case BUCKETS.ONGOING: { + const data = processBucketData( + handle, state.challenges, loaded, bucket, state.sorts, sort, filter, frontFilter, + ); + newChallenges = _.cloneDeep(state.challenges); + newChallenges[bucket] = data; + switch (bucket) { + case BUCKETS.MY: + otherState.allMyChallengesLoaded = true; + otherState.gettingMoreMyChallenges = false; + break; + case BUCKETS.OPEN_FOR_REGISTRATION: + otherState.allOpenChallengesLoaded = true; + otherState.gettingMoreOpenChallenges = false; + break; + case BUCKETS.ONGOING: + otherState.allOnGoingChallengesLoaded = true; + otherState.gettingMoreOnGoingChallenges = false; + break; + default: + break; + } + } + break; + default: + break; + } + + const meta = newMeta || state.meta; + + return { + ...state, + challenges: newChallenges, + ...otherState, + meta, + lastUpdateOfActiveChallenges: Date.now(), + lastRequestedPageOfActiveChallenges: -1, + loadingRestActiveChallengesUUID: '', + }; +} + +/** + * Handles CHALLENGE_LISTING/GET_CHALLENGE_SUBTRACKS_DONE action. + * @param {Object} state + * @param {Object} action + * @return {Object} + */ +function onGetChallengeSubtracksDone(state, action) { + if (action.error) logger.error(action.payload); + return { + ...state, + challengeSubtracks: action.error ? [] : action.payload, + challengeSubtracksMap: action.error ? {} : _.keyBy(action.payload, 'subTrack'), + loadingChallengeSubtracks: false, + }; +} + +/** + * Handles CHALLENGE_LISTING/GET_CHALLENGE_TAGS_DONE action. + * @param {Object} state + * @param {Object} action + * @return {Object} + */ +function onGetChallengeTagsDone(state, action) { + if (action.error) logger.error(action.payload); + return { + ...state, + challengeTags: action.error ? [] : action.payload, + loadingChallengeTags: false, + }; +} + +function onGetPastChallengesInit(state, action) { + const { frontFilter, page, uuid } = action.payload; + const tracks = frontFilter && frontFilter.tracks; + if (tracks && _.isEmpty(tracks)) { + return { + ...state, + allPastChallengesLoaded: true, + loadingPastChallengesUUID: '', + }; + } + + return { + ...state, + lastRequestedPageOfPastChallenges: page, + loadingPastChallengesUUID: uuid, + }; +} + +function onGetPastChallengesDone(state, { error, payload }) { + if (error) { + logger.error(payload); + return state; + } + const { + uuid, challenges: loaded, frontFilter, sort, + } = payload; + if (uuid !== state.loadingPastChallengesUUID) return state; + + const ids = new Set(); + loaded.forEach(item => ids.add(item.id)); + + /* Fetching 0 page of past challenges also drops any past challenges + * loaded to the state before. */ + const filter = state.lastRequestedPageOfPastChallenges + ? item => !ids.has(item.id) + : item => !ids.has(item.id) && item.status !== 'COMPLETED' && item.status !== 'PAST'; + + const pasts = processBucketData( + null, state.challenges, loaded, BUCKETS.PAST, state.sorts, sort, filter, frontFilter, + ); + + let keepPastPlaceholders = false; + if (loaded.length) { + const ff = Filter.getFilterFunction(frontFilter); + keepPastPlaceholders = pasts.filter(ff).length + - (_.has(state.challenges, BUCKETS.PAST) + ? state.challenges[BUCKETS.PAST].filter(ff).length : 0) < 10; + } + + const newChallenges = _.cloneDeep(state.challenges); + newChallenges[BUCKETS.PAST] = pasts; + + return { + ...state, + allPastChallengesLoaded: loaded.length === 0, + challenges: newChallenges, + keepPastPlaceholders, + loadingPastChallengesUUID: '', + }; +} + +function onSelectCommunity(state, { payload }) { + updateQuery({ communityId: payload || undefined }); + return { + ...state, + selectedCommunityId: payload, + + /* Page numbers of past/upcoming challenges depend on the filters. To keep + * the code simple we just reset them each time a filter is modified. + * (This community selection defines community-specific filter for + * challenges). */ + allPastChallengesLoaded: false, + lastRequestedPageOfPastChallenges: -1, + }; +} + +/** + * @param {Object} state + * @param {Object} action + * @return {Object} + */ +function onSetFilter(state, { payload }) { + /* Validation of filter parameters: they may come from URL query, thus + * validation is not a bad idea. As you may note, at the moment we do not + * do it very carefully (many params are not validated). */ + const filter = _.clone(payload); + if (_.isPlainObject(filter.tags)) { + filter.tags = _.values(filter.tags); + } + if (_.isPlainObject(filter.subtracks)) { + filter.subtracks = _.values(filter.subtracks); + } + if (filter.startDate && !moment(filter.startDate).isValid()) { + delete filter.startDate; + } + if (filter.endDate && !moment(filter.endDate).isValid()) { + delete filter.endDate; + } + + /* Update of URL and generation of the state. */ + updateQuery({ filter }); + return { + ...state, + filter, + + /* Page numbers of past/upcoming challenges depend on the filters. To keep + * the code simple we just reset them each time a filter is modified. */ + allPastChallengesLoaded: false, + lastRequestedPageOfPastChallenges: -1, + }; +} + +/** + * Handles CHALLENGE_LISTING/GET_REVIEW_OPPORTUNITIES_INIT action. + * @param {Object} state + * @param {Object} action Payload will be page, uuid + * @return {Object} New state + */ +function onGetReviewOpportunitiesInit(state, { payload }) { + return { + ...state, + lastRequestedPageOfReviewOpportunities: payload.page, + loadingReviewOpportunitiesUUID: payload.uuid, + }; +} + +/** + * Handles CHALLENGE_LISTING/GET_REVIEW_OPPORTUNITIES_DONE action. + * @param {Object} state + * @param {Object} action Payload will be JSON from api call and UUID + * @return {Object} New state + */ +function onGetReviewOpportunitiesDone(state, { payload, error }) { + if (error) { + return state; + } + + const { + uuid, + loaded, + sort, + frontFilter, + } = payload; + + if (uuid !== state.loadingReviewOpportunitiesUUID) return state; + + const ids = new Set(); + loaded.forEach(item => ids.add(item.id)); + + const filter = item => !ids.has(item.id); + + const reviewOpportunities = processBucketData( + null, state, loaded, BUCKETS.REVIEW_OPPORTUNITIES, + state.sorts, sort, filter, frontFilter, + ); + + return { + ...state, + reviewOpportunities, + loadingReviewOpportunitiesUUID: '', + allReviewOpportunitiesLoaded: loaded.length === 0, + }; +} + +/** + * Inits the loading of SRMs. + * @param {Object} state + * @param {String} payload Operation UUID. + * @return {Object} New state. + */ +function onGetSrmsInit(state, { payload }) { + return { + ...state, + srms: { + ...state.srms, + loadingUuid: payload, + }, + }; +} + +/** + * Handles loaded SRMs. + * @param {Object} state + * @param {Object} action + * @return {Object} New state. + */ +function onGetSrmsDone(state, { error, payload }) { + if (error) { + logger.error('Failed to load SRMs', payload); + fireErrorMessage('Failed to load SRMs', ''); + return state; + } + + const { uuid, data } = payload; + if (state.srms.loadingUuid !== uuid) return state; + return { + ...state, + srms: { + data, + loadingUuid: '', + timestamp: Date.now(), + }, + }; +} + +/** + * Creates a new Challenge Listing reducer with the specified initial state. + * @param {Object} initialState Optional. Initial state. + * @return Challenge Listing reducer. + */ +function create(initialState) { + const a = actions.challengeListing; + return handleActions({ + [a.dropChallenges]: (state, { payload }) => { + const { bucket } = payload; + const otherState = {}; + switch (bucket) { + case BUCKETS.REVIEW_OPPORTUNITIES: + otherState.lastRequestedPageOfReviewOpportunities = -1; + otherState.reviewOpportunities = []; + otherState.allReviewOpportunitiesLoaded = false; + break; + case BUCKETS.PAST: + otherState.challenges = _.cloneDeep(state.challenges); + otherState.lastRequestedPageOfPastChallenges = -1; + otherState.challenges.past = []; + otherState.allPastChallengesLoaded = false; + break; + default: + otherState.challenges = {}; + otherState.allMyChallengesLoaded = false; + otherState.allOnGoingChallengesLoaded = false; + otherState.allOpenChallengesLoaded = false; + otherState.allActiveChallengesLoaded = false; + otherState.allPastChallengesLoaded = false; + otherState.allReviewOpportunitiesLoaded = false; + otherState.lastRequestedPageOfActiveChallenges = -1; + otherState.lastRequestedPageOfMyChallenges = -1; + otherState.lastRequestedPageOfOpenChallenges = -1; + otherState.lastRequestedPageOfOnGoingChallenges = -1; + otherState.lastRequestedPageOfPastChallenges = -1; + otherState.lastRequestedPageOfReviewOpportunities = -1; + otherState.lastUpdateOfActiveChallenges = -1; + otherState.loadingActiveChallengesUUID = ''; + otherState.loadingMyChallengesUUID = ''; + otherState.loadingOpenChallengesUUID = ''; + otherState.loadingOnGoingChallengesUUID = ''; + otherState.loadingRestActiveChallengesUUID = ''; + otherState.loadingPastChallengesUUID = ''; + otherState.loadingReviewOpportunitiesUUID = ''; + otherState.reviewOpportunities = []; + otherState.meta = { + allChallengesCount: 0, + myChallengesCount: 0, + ongoingChallengesCount: 0, + openChallengesCount: 0, + totalCount: 0, + }; + break; + } + + return ({ + ...state, + ...otherState, + }); + }, + + [a.getMoreChallenges]: (state, { payload }) => { + const { bucket } = payload; + const otherState = {}; + switch (bucket) { + case BUCKETS.MY: + otherState.gettingMoreMyChallenges = true; + break; + case BUCKETS.ONGOING: + otherState.gettingMoreOnGoingChallenges = true; + break; + case BUCKETS.OPEN_FOR_REGISTRATION: + otherState.gettingMoreOpenChallenges = true; + break; + default: + break; + } + return ({ + ...state, + ...otherState, + }); + }, + + [a.expandTag]: (state, { payload }) => ({ + ...state, + expandedTags: [...state.expandedTags, payload], + }), + + [a.getAllActiveChallengesInit]: onGetAllActiveChallengesInit, + [a.getAllActiveChallengesDone]: onGetAllActiveChallengesDone, + + [a.getActiveChallengesInit]: onGetActiveChallengesInit, + [a.getActiveChallengesDone]: onGetActiveChallengesDone, + + [a.getRestActiveChallengesInit]: onGetRestActiveChallengesInit, + [a.getRestActiveChallengesDone]: onGetRestActiveChallengesDone, + + [a.getChallengeSubtracksInit]: state => ({ + ...state, + loadingChallengeSubtracks: true, + }), + [a.getChallengeSubtracksDone]: onGetChallengeSubtracksDone, + + [a.getChallengeTagsInit]: state => ({ + ...state, + loadingChallengeTags: true, + }), + [a.getChallengeTagsDone]: onGetChallengeTagsDone, + + [a.getPastChallengesInit]: onGetPastChallengesInit, + [a.getPastChallengesDone]: onGetPastChallengesDone, + + [a.getReviewOpportunitiesInit]: onGetReviewOpportunitiesInit, + [a.getReviewOpportunitiesDone]: onGetReviewOpportunitiesDone, + + [a.getSrmsInit]: onGetSrmsInit, + [a.getSrmsDone]: onGetSrmsDone, + + [a.selectCommunity]: onSelectCommunity, + + [a.setFilter]: onSetFilter, + [a.setSort]: (state, { payload }) => { + const otherState = {}; + switch (payload.bucket) { + case BUCKETS.PAST: + otherState.lastRequestedPageOfPastChallenges = -1; + break; + case BUCKETS.MY: + case BUCKETS.OPEN_FOR_REGISTRATION: + case BUCKETS.ONGOING: + otherState.lastRequestedPageOfActiveChallenges = -1; + break; + case BUCKETS.REVIEW_OPPORTUNITIES: + otherState.lastRequestedPageOfReviewOpportunities = -1; + break; + default: + break; + } + return ({ + ...state, + ...otherState, + sorts: { + ...state.sorts, + [payload.bucket]: payload.sort, + }, + }); + }, + + [a.setDatePickerStatus]: (state, { payload }) => { + const { status } = payload; + return ({ + ...state, + datepickerOpen: status, + }); + }, + }, _.defaults(_.clone(initialState) || {}, { + allMyChallengesLoaded: false, + allOnGoingChallengesLoaded: false, + allOpenChallengesLoaded: false, + allActiveChallengesLoaded: false, + allPastChallengesLoaded: false, + allReviewOpportunitiesLoaded: false, + + challenges: {}, + challengeSubtracks: [], + challengeSubtracksMap: {}, + challengeTags: [], + + expandedTags: [], + + gettingMoreChallenges: false, + gettingMoreMyChallenges: false, + gettingMoreOnGoingChallenges: false, + gettingMoreOpenChallenges: false, + + filter: {}, + + keepPastPlaceholders: false, + + lastRequestedPageOfActiveChallenges: -1, + lastRequestedPageOfMyChallenges: -1, + lastRequestedPageOfOnGoingChallenges: -1, + lastRequestedPageOfOpenChallenges: -1, + lastRequestedPageOfPastChallenges: -1, + lastRequestedPageOfReviewOpportunities: -1, + lastUpdateOfActiveChallenges: 0, + + loadingActiveChallengesUUID: '', + loadingMyChallengesUUID: '', + loadingOnGoingChallengesUUID: '', + loadingOpenChallengesUUID: '', + loadingRestActiveChallengesUUID: '', + loadingPastChallengesUUID: '', + loadingReviewOpportunitiesUUID: '', + + loadingChallengeSubtracks: false, + loadingChallengeTags: false, + + reviewOpportunities: [], + + selectedCommunityId: '', + + sorts: {}, + + srms: { + data: [], + loadingUuid: '', + timestamp: 0, + }, + + meta: { + allChallengesCount: 0, + myChallengesCount: 0, + ongoingChallengesCount: 0, + openChallengesCount: 0, + totalCount: 0, + }, + + datepickerOpen: false, + })); +} + +/** + * The factory creates the new reducer with initial state tailored to the given + * ExpressJS HTTP request, if specified (for server-side rendering). If no HTTP + * request is specified, it creates the default reducer. + * @return {Promise} Resolves to the new reducer. + */ +export function factory() { + return Promise.resolve(create()); +} + +/* Default reducer with empty initial state. */ +export default create(); diff --git a/src/reducers/index.js b/src/reducers/index.js index 15fd144c..aee34c61 100644 --- a/src/reducers/index.js +++ b/src/reducers/index.js @@ -22,6 +22,7 @@ import settings, { factory as settingsFactory } from './settings'; import looker, { factory as lookerFactory } from './looker'; +import challengeListing, { factory as challengeListingFactory } from './challenge-listing'; export function factory(options) { @@ -41,6 +42,7 @@ export function factory(options) { mySubmissionsManagement: mySubmissionsManagementFactory(options), settings: settingsFactory(options), looker: lookerFactory(options), + challengeListing: challengeListingFactory(options), }); } @@ -60,4 +62,5 @@ export default ({ mySubmissionsManagement, settings, looker, + challengeListing, }); diff --git a/src/services/challenges.js b/src/services/challenges.js index 604a2879..04244726 100644 --- a/src/services/challenges.js +++ b/src/services/challenges.js @@ -244,10 +244,10 @@ class ChallengesService { params = {}, ) => { const query = { - filter: qs.stringify(filters, { encode: false }), + filter: qs.stringify(filters, { encode: false }).replace('&', '%26'), ...params, }; - const url = `${endpoint}?${qs.stringify(query)}`; + const url = `${endpoint}?${qs.stringify(query, { encode: false })}`; const res = await this.private.api.get(url).then(checkError); return { challenges: res.content || [], diff --git a/src/utils/challenge/buckets.js b/src/utils/challenge/buckets.js new file mode 100644 index 00000000..d9e9221c --- /dev/null +++ b/src/utils/challenge/buckets.js @@ -0,0 +1,153 @@ +/** + * Standard challenge buckets + */ + +import _ from 'lodash'; +import { SORTS } from './sort'; + +export const BUCKETS = { + ALL: 'all', + MY: 'my', + OPEN_FOR_REGISTRATION: 'openForRegistration', + ONGOING: 'ongoing', + PAST: 'past', + SAVED_FILTER: 'saved-filter', + UPCOMING: 'upcoming', + REVIEW_OPPORTUNITIES: 'reviewOpportunities', + SAVED_REVIEW_OPPORTUNITIES_FILTER: 'savedReviewOpportunitiesFilter', +}; + +export const BUCKET_DATA = { + [BUCKETS.ALL]: { + filter: { + started: true, + status: ['ACTIVE'], + }, + hideCount: false, + name: 'All Challenges', + sorts: [], + }, + [BUCKETS.MY]: { + filter: { + started: true, + status: ['ACTIVE'], + }, + hideCount: false, + name: 'My Challenges', + sorts: [ + SORTS.MOST_RECENT, + SORTS.TIME_TO_SUBMIT, + SORTS.NUM_REGISTRANTS, + SORTS.NUM_SUBMISSIONS, + SORTS.PRIZE_HIGH_TO_LOW, + SORTS.TITLE_A_TO_Z, + ], + }, + [BUCKETS.OPEN_FOR_REGISTRATION]: { + filter: { + registrationOpen: true, + started: true, + status: ['ACTIVE'], + }, + hideCount: false, + name: 'Open for registration', + sorts: [ + SORTS.MOST_RECENT, + SORTS.TIME_TO_REGISTER, + SORTS.TIME_TO_SUBMIT, + SORTS.NUM_REGISTRANTS, + SORTS.NUM_SUBMISSIONS, + SORTS.PRIZE_HIGH_TO_LOW, + SORTS.TITLE_A_TO_Z, + ], + }, + [BUCKETS.ONGOING]: { + filter: { + registrationOpen: false, + started: true, + status: ['ACTIVE'], + }, + hideCount: false, + name: 'Ongoing challenges', + sorts: [ + SORTS.MOST_RECENT, + SORTS.CURRENT_PHASE, + SORTS.TITLE_A_TO_Z, + SORTS.PRIZE_HIGH_TO_LOW, + ], + }, + [BUCKETS.UPCOMING]: { + filter: { + upcoming: true, + }, + hideCount: true, + name: 'Upcoming challenges', + sorts: [ + SORTS.MOST_RECENT, + SORTS.PRIZE_HIGH_TO_LOW, + SORTS.TITLE_A_TO_Z, + ], + }, + [BUCKETS.PAST]: { + filter: { status: ['COMPLETED', 'PAST'] }, + hideCount: true, + name: 'Past challenges', + sorts: [ + SORTS.MOST_RECENT, + SORTS.PRIZE_HIGH_TO_LOW, + SORTS.TITLE_A_TO_Z, + ], + }, + [BUCKETS.REVIEW_OPPORTUNITIES]: { + filter: {}, + hideCount: true, + name: 'Open for review', + sorts: [ + SORTS.REVIEW_OPPORTUNITIES_START_DATE, + SORTS.REVIEW_OPPORTUNITIES_PAYMENT, + SORTS.REVIEW_OPPORTUNITIES_TITLE_A_TO_Z, + ], + }, + [BUCKETS.SAVED_REVIEW_OPPORTUNITIES_FILTER]: { + filter: {}, + sorts: [ + SORTS.REVIEW_OPPORTUNITIES_START_DATE, + SORTS.REVIEW_OPPORTUNITIES_PAYMENT, + SORTS.REVIEW_OPPORTUNITIES_TITLE_A_TO_Z, + ], + }, +}; + +/** + * Returns configuration of all possible challenge buckets. + * @param {String} userHandle Handle of the authenticated + * user to filter out My Challenges. + */ +export function getBuckets(userHandle) { + const res = _.cloneDeep(BUCKET_DATA); + res[BUCKETS.MY].filter.users = [userHandle]; + return res; +} + +/** + * Tests if a given bucket is of any of the Review Opportunities types + * @param {String} bucket The bucket in question + * @return {Boolean} True if the bucket contains Review Opportunities + */ +export const isReviewOpportunitiesBucket = bucket => ( + bucket === BUCKETS.REVIEW_OPPORTUNITIES || bucket === BUCKETS.SAVED_REVIEW_OPPORTUNITIES_FILTER); + +/** + * Registers a new bucket. + * @param {String} id + * @param {Object} bucket + */ +export function registerBucket(id, bucket) { + if (BUCKET_DATA[id]) { + throw new Error('Bucket ID clush with an existing bucket'); + } + BUCKETS[id] = id; + BUCKET_DATA[id] = bucket; +} + +export default undefined; diff --git a/src/utils/challenge/filter.js b/src/utils/challenge/filter.js index 28e00654..bdd343af 100644 --- a/src/utils/challenge/filter.js +++ b/src/utils/challenge/filter.js @@ -177,6 +177,39 @@ function filterByUsers(challenge, state) { return state.users.find(user => challenge.users[user]); } +/** + * [filterByDate filter challenges by date reange] + * @param {[type]} challenges input challenges + * @param {[type]} filter filter including startDate and endDate + * @return {[type]} filtered challenges array + */ +export function filterByDate(challenges, filter) { + let cs = challenges.filter(c => filterByStartDate(c, filter)); + cs = cs.filter(c => filterByEndDate(c, filter)); + return cs; +} + +/** + * [newMeta compute new meta via challenges and filter] + * @param {[type]} meta old meta + * @param {[type]} challenges input challenges + * @param {[type]} filter filter including startDate and end endDate + * @return {[type]} new meta + */ +export function newMeta(meta, challenges, filter) { + if (!filter.startDate && !filter.endDate) { + return meta; + } + const m = { + }; + m.allChallengesCount = challenges.length; + m.openChallengesCount = challenges.filter(c => c.registrationOpen === 'Yes').length; + m.ongoingChallengesCount = m.allChallengesCount - m.openChallengesCount; + m.myChallengesCount = challenges.filter(c => c.user && !_.isEmpty(c.user)).length; + m.totalCount = challenges.length; + return m; +} + /** * Returns clone of the state with the specified competition track added. * @param {Object} state diff --git a/src/utils/challenge/sort.js b/src/utils/challenge/sort.js new file mode 100644 index 00000000..4091b0c0 --- /dev/null +++ b/src/utils/challenge/sort.js @@ -0,0 +1,84 @@ +/** + * Collection of compare function to sort challenges in different ways. + */ + +import moment from 'moment'; +import { sumBy } from 'lodash'; + +export const SORTS = { + CURRENT_PHASE: 'current-phase', + MOST_RECENT: 'most-recent', + NUM_REGISTRANTS: 'num-registrants', + NUM_SUBMISSIONS: 'num-submissions', + PRIZE_HIGH_TO_LOW: 'prize-high-to-low', + TIME_TO_REGISTER: 'time-to-register', + TIME_TO_SUBMIT: 'time-to-submit', + TITLE_A_TO_Z: 'title-a-to-z', + REVIEW_OPPORTUNITIES_TITLE_A_TO_Z: 'review-opportunities-title-a-to-z', + REVIEW_OPPORTUNITIES_PAYMENT: 'review-opportunities-payment', + REVIEW_OPPORTUNITIES_START_DATE: 'review-opportunities-start-date', +}; + +export const SORTS_DATA = { + [SORTS.CURRENT_PHASE]: { + func: (a, b) => a.status.localeCompare(b.status), + name: 'Current phase', + }, + [SORTS.MOST_RECENT]: { + func: (a, b) => moment(b.registrationStartDate).diff(a.registrationStartDate), + name: 'Most recent', + }, + [SORTS.NUM_REGISTRANTS]: { + func: (a, b) => b.numRegistrants - a.numRegistrants, + name: '# of registrants', + }, + [SORTS.NUM_SUBMISSIONS]: { + func: (a, b) => b.numSubmissions - a.numSubmissions, + name: '# of submissions', + }, + [SORTS.PRIZE_HIGH_TO_LOW]: { + func: (a, b) => b.totalPrize - a.totalPrize, + name: 'Prize high to low', + }, + [SORTS.TIME_TO_REGISTER]: { + func: (a, b) => moment(a.registrationEndDate || a.submissionEndDate) + .diff(b.registrationEndDate || b.submissionEndDate), + name: 'Time to register', + }, + [SORTS.TIME_TO_SUBMIT]: { + func: (a, b) => { + function nextSubEndDate(o) { + if (o.checkpointSubmissionEndDate && moment(o.checkpointSubmissionEndDate).isAfter()) { + return o.checkpointSubmissionEndDate; + } + return o.submissionEndDate; + } + + const aDate = nextSubEndDate(a); + const bDate = nextSubEndDate(b); + + if (moment(aDate).isBefore()) return 1; + if (moment(bDate).isBefore()) return -1; + + return moment(aDate).diff(bDate); + }, + name: 'Time to submit', + }, + [SORTS.TITLE_A_TO_Z]: { + func: (a, b) => a.name.localeCompare(b.name), + name: 'Title A-Z', + }, + [SORTS.REVIEW_OPPORTUNITIES_TITLE_A_TO_Z]: { + func: (a, b) => a.challenge.title.localeCompare(b.challenge.title), + name: 'Title A-Z', + }, + [SORTS.REVIEW_OPPORTUNITIES_PAYMENT]: { + func: (a, b) => sumBy(b.payments, 'payment') - sumBy(a.payments, 'payment'), + name: 'Payment', + }, + [SORTS.REVIEW_OPPORTUNITIES_START_DATE]: { + // This will implicitly use moment#valueOf + func: (a, b) => moment(a.startDate) - moment(b.startDate), + name: 'Review start date', + }, +}; diff --git a/src/utils/index.js b/src/utils/index.js index b63d63b8..e7e4ff52 100644 --- a/src/utils/index.js +++ b/src/utils/index.js @@ -7,9 +7,14 @@ import * as time from './time'; import * as mock from './mock'; import * as errors from './errors'; import * as filter from './challenge/filter'; +import * as buckets from './challenge/buckets'; +import * as sort from './challenge/sort'; +import * as url from './url'; const challenge = { filter, + buckets, + sort, }; export { @@ -19,4 +24,5 @@ export { time, mock, errors, + url, }; diff --git a/src/utils/tc.js b/src/utils/tc.js index 77fce85a..08309d16 100644 --- a/src/utils/tc.js +++ b/src/utils/tc.js @@ -4,6 +4,9 @@ * @todo More TC-related utils should be moved here from Community-app. */ +import _ from 'lodash'; +import moment from 'moment'; + /** * Codes of the Topcoder communities. */ @@ -60,3 +63,47 @@ export async function getLookerApiResponsePayload(res) { status: x.status, }; } + +/** + * process srm to populate additional infomation + * adopt from topcoder-app repo + * @param {Object} s srm to process + * @return {Object} processed srm + */ +export function processSRM(s) { + const srm = _.cloneDeep(s); + srm.userStatus = 'registered'; + if (Array.isArray(srm.rounds) && srm.rounds.length) { + if (srm.rounds[0].userSRMDetails && srm.rounds[0].userSRMDetails.rated) { + srm.result = srm.rounds[0].userSRMDetails; + } + if (srm.rounds[0].codingStartAt) { + srm.codingStartAt = srm.rounds[0].codingStartAt; + } + if (srm.rounds[0].codingEndAt) { + srm.codingEndAt = srm.rounds[0].codingEndAt; + } + if (srm.rounds[0].registrationStartAt) { + srm.registrationStartAt = srm.rounds[0].registrationStartAt; + } + if (srm.rounds[0].registrationEndAt) { + srm.registrationEndAt = srm.rounds[0].registrationEndAt; + } + } + + // determines if the current phase is registration + let start = moment(srm.registrationStartAt).unix(); + let end = moment(srm.registrationEndAt).unix(); + let now = moment().unix(); + if (start <= now && end >= now) { + srm.currentPhase = 'REGISTRATION'; + } + // determines if the current phase is coding + start = moment(srm.codingStartAt).unix(); + end = moment(srm.codingEndAt).unix(); + now = moment().unix(); + if (start <= now && end >= now) { + srm.currentPhase = 'CODING'; + } + return srm; +} diff --git a/src/utils/url.js b/src/utils/url.js new file mode 100644 index 00000000..73154aa7 --- /dev/null +++ b/src/utils/url.js @@ -0,0 +1,49 @@ +/** + * Various URL-related functions. + */ + +/* global window */ + +import _ from 'lodash'; +import qs from 'qs'; +import { isomorphy } from 'topcoder-react-utils'; + +/** + * If executed client-side (determined in this case by the presence of global + * window object), this function updates query section of URL; otherwise does + * nothing. + * @param {Object} update Specifies the update to make. Current query will be + * parsed into JS object, then update will be merged into that object, and the + * result will be pushed back to the query section of URL. I.e. to unset some + * field of the query, that field should be explicitely mentioned inside + * 'update' as undefined. + */ +export function updateQuery(update) { + if (isomorphy.isServerSide()) return; + + let query = qs.parse(window.location.search.slice(1)); + + /* _.merge won't work here, because it just ignores the fields explicitely + * set as undefined in the objects to be merged, rather than deleting such + * fields in the target object. */ + _.forIn(update, (value, key) => { + if (_.isUndefined(value)) delete query[key]; + else query[key] = value; + }); + query = `?${qs.stringify(query, { encodeValuesOnly: true })}`; + window.history.replaceState(window.history.state, '', query); +} + +/** + * Cleans/removes trailing slash from url + * + * @param {String} url The url to clean + * @return {String} + */ +export function removeTrailingSlash(url) { + return url.charAt(url.length - 1) === '/' + ? url.slice(0, -1) + : url; +} + +export default undefined; From c76aaa1ec7203a8ce2f709d3bc3ebb32cb6e8add Mon Sep 17 00:00:00 2001 From: Huan Li Date: Sun, 28 Apr 2019 00:13:04 +0800 Subject: [PATCH 02/15] Update _tests_/_snapshots_ --- __tests__/__snapshots__/index.js.snap | 3 +++ 1 file changed, 3 insertions(+) diff --git a/__tests__/__snapshots__/index.js.snap b/__tests__/__snapshots__/index.js.snap index e7ee51bc..0386661f 100644 --- a/__tests__/__snapshots__/index.js.snap +++ b/__tests__/__snapshots__/index.js.snap @@ -426,14 +426,17 @@ Object { "countReset": [Function], "debug": [Function], "dir": [Function], + "dirxml": [Function], "error": [Function], "group": [Function], "groupCollapsed": [Function], "groupEnd": [Function], "info": [Function], "log": [Function], + "table": [Function], "time": [Function], "timeEnd": [Function], + "timeLog": [Function], "trace": [Function], "warn": [Function], }, From fc0f961f699d138e434bf361a25069e84dfbf729 Mon Sep 17 00:00:00 2001 From: Huan Li Date: Sun, 28 Apr 2019 10:28:35 +0800 Subject: [PATCH 03/15] Update _tests_/_snapshots --- __tests__/__snapshots__/index.js.snap | 3 --- 1 file changed, 3 deletions(-) diff --git a/__tests__/__snapshots__/index.js.snap b/__tests__/__snapshots__/index.js.snap index 0386661f..e7ee51bc 100644 --- a/__tests__/__snapshots__/index.js.snap +++ b/__tests__/__snapshots__/index.js.snap @@ -426,17 +426,14 @@ Object { "countReset": [Function], "debug": [Function], "dir": [Function], - "dirxml": [Function], "error": [Function], "group": [Function], "groupCollapsed": [Function], "groupEnd": [Function], "info": [Function], "log": [Function], - "table": [Function], "time": [Function], "timeEnd": [Function], - "timeLog": [Function], "trace": [Function], "warn": [Function], }, From af685a23e9eaf6cb6f63b40de045e2e1604f8279 Mon Sep 17 00:00:00 2001 From: Sushil Date: Mon, 29 Apr 2019 15:32:15 +0530 Subject: [PATCH 04/15] upgrading topcoder-react-lib version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index cc4ec5c2..d65f4707 100644 --- a/package.json +++ b/package.json @@ -31,7 +31,7 @@ "lint:js": "./node_modules/.bin/eslint --ext .js,.jsx .", "test": "npm run lint && npm run jest" }, - "version": "0.7.11", + "version": "0.7.12", "dependencies": { "auth0-js": "^6.8.4", "isomorphic-fetch": "^2.2.1", From e91a940c1bb79887c9a26c5dbe06874b48fa9ea8 Mon Sep 17 00:00:00 2001 From: Huan Li Date: Tue, 30 Apr 2019 22:14:01 +0800 Subject: [PATCH 05/15] Update filter object when fetch challenges. If filter's track has DATA_SCIENCE, should add subTrack DEVELOP_MARATHON_MATCH and remove DS track from filter object. --- src/actions/challenge-listing.js | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/actions/challenge-listing.js b/src/actions/challenge-listing.js index d8acf22a..ad872794 100644 --- a/src/actions/challenge-listing.js +++ b/src/actions/challenge-listing.js @@ -19,8 +19,8 @@ const { PAGE_SIZE, REVIEW_OPPORTUNITY_PAGE_SIZE } = config; /** * Process filter - * Development challenges having Data Science tech tag, still should be - * included into data science track. + * When filter includes Data Science track, still should be + * included DEVELOP_MARATHON_MATCH sub track. * @param filter * @returns {string} */ @@ -28,9 +28,15 @@ function processFilter(filter) { const newFilter = _.clone(filter); if (_.has(filter, 'track') && filter.track.includes(COMPETITION_TRACKS.DATA_SCIENCE.toUpperCase()) - && !filter.track.includes(COMPETITION_TRACKS.DEVELOP.toUpperCase()) + && ((_.has(filter, 'subTrack') && !filter.subTrack.includes('DEVELOP_MARATHON_MATCH')) + || !_.has(filter, 'subTrack')) ) { - newFilter.track = `${newFilter.track},${COMPETITION_TRACKS.DEVELOP.toUpperCase()}`; + newFilter.subTrack = `${_.has(filter, 'subTrack') ? `${newFilter.subTrack},DEVELOP_MARATHON_MATCH` : 'DEVELOP_MARATHON_MATCH'}`; + newFilter.track = _.remove(filter.track.split(','), item => item.toUpperCase() !== COMPETITION_TRACKS.DATA_SCIENCE.toUpperCase()).join(','); + + if (_.isEmpty(newFilter.track)) { + delete newFilter.track; + } } return newFilter; } From f8f8bd8ee609c0303c254fdf5b96b7044b352413 Mon Sep 17 00:00:00 2001 From: Thomas Kranitsas Date: Tue, 30 Apr 2019 17:23:05 +0300 Subject: [PATCH 06/15] Bump npm version --- package-lock.json | 1634 +++++++++++++++++++++++++++++++++++++++++---- package.json | 2 +- 2 files changed, 1515 insertions(+), 121 deletions(-) diff --git a/package-lock.json b/package-lock.json index c1a94426..1a483aee 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "topcoder-react-lib", - "version": "0.7.11", + "version": "0.7.13", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -714,8 +714,7 @@ "abab": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.0.tgz", - "integrity": "sha512-sY5AXXVZv4Y1VACTtR11UJCPHHudgY5i26Qj5TypE6DKlIApbwb5uqhXcJ5UUGbvZNRh7EeIoW+LrJumBsKp7w==", - "dev": true + "integrity": "sha512-sY5AXXVZv4Y1VACTtR11UJCPHHudgY5i26Qj5TypE6DKlIApbwb5uqhXcJ5UUGbvZNRh7EeIoW+LrJumBsKp7w==" }, "abbrev": { "version": "1.1.1", @@ -735,8 +734,7 @@ "acorn": { "version": "5.7.3", "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.3.tgz", - "integrity": "sha512-T/zvzYRfbVojPWahDsE5evJdHb3oJoQfFbsrKM7w5Zcs++Tr257tia3BmMP8XYVjp1S9RZXQMh7gao96BlqZOw==", - "dev": true + "integrity": "sha512-T/zvzYRfbVojPWahDsE5evJdHb3oJoQfFbsrKM7w5Zcs++Tr257tia3BmMP8XYVjp1S9RZXQMh7gao96BlqZOw==" }, "acorn-dynamic-import": { "version": "3.0.0", @@ -852,6 +850,16 @@ "integrity": "sha1-6GuBnGAs+IIa1jdBNpjx3sAhhHo=", "dev": true }, + "align-text": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/align-text/-/align-text-0.1.4.tgz", + "integrity": "sha1-DNkKVhCT810KmSVsIrcGlDP60Rc=", + "requires": { + "kind-of": "^3.0.2", + "longest": "^1.0.1", + "repeat-string": "^1.5.2" + } + }, "alphanum-sort": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/alphanum-sort/-/alphanum-sort-1.0.2.tgz", @@ -861,8 +869,7 @@ "amdefine": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz", - "integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=", - "dev": true + "integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=" }, "ansi-colors": { "version": "3.2.1", @@ -925,6 +932,216 @@ "default-require-extensions": "^1.0.0" } }, + "appirio-tech-api-schemas": { + "version": "5.0.70", + "resolved": "https://registry.npmjs.org/appirio-tech-api-schemas/-/appirio-tech-api-schemas-5.0.70.tgz", + "integrity": "sha1-3RtCG/rw8PSokKRTgHSlVYDQy8s=", + "requires": { + "auto-config-fake-server": "2.x.x" + } + }, + "appirio-tech-client-app-layer": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/appirio-tech-client-app-layer/-/appirio-tech-client-app-layer-0.1.3.tgz", + "integrity": "sha1-uO5YdHMM2r209RE8NMQvwOsxoOs=", + "requires": { + "axios": "^0.8.1", + "history": "^1.17.0", + "html-webpack-plugin": "^1.7.0", + "humps": "^0.6.0", + "isomorphic-fetch": "^2.1.1", + "jwt-decode": "^1.4.0", + "lodash": "^4.0.0", + "normalizr": "^1.0.0", + "q": "^1.4.1", + "react": "^0.14.0", + "react-dom": "^0.14.0", + "react-redux": "^4.0.0", + "react-router": "^1.0.3", + "redux": "^3.0.0", + "redux-form": "^4.1.0", + "redux-logger": "^2.4.0", + "redux-router": "^1.0.0-beta3", + "redux-thunk": "^0.1.0" + }, + "dependencies": { + "hoist-non-react-statics": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.0.tgz", + "integrity": "sha512-0XsbTXxgiaCDYDIWFcwkmerZPSwywfUqYmwT4jzewKTQSWoE6FCMoUVOeBJWK3E/CrWbxRG3m5GzY4lnIwGRBA==", + "requires": { + "react-is": "^16.7.0" + } + }, + "prop-types": { + "version": "15.7.2", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz", + "integrity": "sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==", + "requires": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.8.1" + } + }, + "react": { + "version": "0.14.9", + "resolved": "https://registry.npmjs.org/react/-/react-0.14.9.tgz", + "integrity": "sha1-kRCmSXxJ1EuhwO3TF67CnC4NkdE=", + "requires": { + "envify": "^3.0.0", + "fbjs": "^0.6.1" + } + }, + "react-dom": { + "version": "0.14.9", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-0.14.9.tgz", + "integrity": "sha1-BQZKPc8PsYgKOyv8nVjFXY2fYpM=" + }, + "react-is": { + "version": "16.8.6", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.8.6.tgz", + "integrity": "sha512-aUk3bHfZ2bRSVFFbbeVS4i+lNPZr3/WM5jT2J5omUVV1zzcs1nAaf3l51ctA5FFvCRbhrH0bdAsRRQddFJZPtA==" + }, + "react-redux": { + "version": "4.4.10", + "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-4.4.10.tgz", + "integrity": "sha512-tjL0Bmpkj75Td0k+lXlF8Fc8a9GuXFv/3ahUOCXExWs/jhsKiQeTffdH0j5byejCGCRL4tvGFYlrwBF1X/Aujg==", + "requires": { + "create-react-class": "^15.5.1", + "hoist-non-react-statics": "^3.3.0", + "invariant": "^2.0.0", + "lodash": "^4.17.11", + "loose-envify": "^1.4.0", + "prop-types": "^15.7.2" + } + }, + "redux-thunk": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-0.1.0.tgz", + "integrity": "sha1-jjR2BoCLNb+Kkn30YW9v7RAYJuU=" + } + } + }, + "appirio-tech-react-components": { + "version": "github:appirio-tech/react-components#c32437a192c886dfbd2d4f9847d46ec1a34b5cc4", + "from": "github:appirio-tech/react-components#feature/connectv2", + "requires": { + "appirio-tech-api-schemas": "^5.0.69", + "appirio-tech-client-app-layer": "^0.1.3", + "classnames": "^2.2.3", + "coffee-script": "^1.12.7", + "coffeescript": "^1.12.7", + "formsy-react": "^0.19.5", + "isomorphic-fetch": "^2.2.1", + "libphonenumber-js": "^1.4.6", + "lodash": "^4.0.0", + "moment": "^2.11.2", + "react": "^15.3.1", + "react-addons-pure-render-mixin": "^15.3.1", + "react-addons-update": "^15.3.1", + "react-avatar": "^2.2.0", + "react-datetime": "^2.0.2", + "react-dom": "^15.3.1", + "react-dropzone": "^3.5.3", + "react-popper": "^0.7.5", + "react-redux": "^4.4.5", + "react-router-dom": "^4.2.2", + "react-select": "^0.9.1", + "react-switch-button": "^1.1.2", + "react-textarea-autosize": "^5.2.1", + "react-transition-group": "^2.2.1", + "redux-thunk": "^2.1.0", + "tc-ui": "git+https://github.com/appirio-tech/tc-ui.git#feature/connectv2", + "uncontrollable": "^4.0.1" + }, + "dependencies": { + "coffeescript": { + "version": "1.12.7", + "resolved": "https://registry.npmjs.org/coffeescript/-/coffeescript-1.12.7.tgz", + "integrity": "sha512-pLXHFxQMPklVoEekowk8b3erNynC+DVJzChxS/LCBBgR6/8AJkHivkm//zbowcfc7BTCAjryuhx6gPqPRfsFoA==" + }, + "core-js": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-1.2.7.tgz", + "integrity": "sha1-ZSKUwUZR2yj6k70tX/KYOk8IxjY=" + }, + "fbjs": { + "version": "0.8.17", + "resolved": "https://registry.npmjs.org/fbjs/-/fbjs-0.8.17.tgz", + "integrity": "sha1-xNWY6taUkRJlPWWIsBpc3Nn5D90=", + "requires": { + "core-js": "^1.0.0", + "isomorphic-fetch": "^2.1.1", + "loose-envify": "^1.0.0", + "object-assign": "^4.1.0", + "promise": "^7.1.1", + "setimmediate": "^1.0.5", + "ua-parser-js": "^0.7.18" + } + }, + "hoist-non-react-statics": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.0.tgz", + "integrity": "sha512-0XsbTXxgiaCDYDIWFcwkmerZPSwywfUqYmwT4jzewKTQSWoE6FCMoUVOeBJWK3E/CrWbxRG3m5GzY4lnIwGRBA==", + "requires": { + "react-is": "^16.7.0" + } + }, + "react": { + "version": "15.6.2", + "resolved": "https://registry.npmjs.org/react/-/react-15.6.2.tgz", + "integrity": "sha1-26BDSrQ5z+gvEI8PURZjkIF5qnI=", + "requires": { + "create-react-class": "^15.6.0", + "fbjs": "^0.8.9", + "loose-envify": "^1.1.0", + "object-assign": "^4.1.0", + "prop-types": "^15.5.10" + } + }, + "react-dom": { + "version": "15.6.2", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-15.6.2.tgz", + "integrity": "sha1-Qc+t9pO3V/rycIRDodH9WgK+9zA=", + "requires": { + "fbjs": "^0.8.9", + "loose-envify": "^1.1.0", + "object-assign": "^4.1.0", + "prop-types": "^15.5.10" + } + }, + "react-is": { + "version": "16.8.6", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.8.6.tgz", + "integrity": "sha512-aUk3bHfZ2bRSVFFbbeVS4i+lNPZr3/WM5jT2J5omUVV1zzcs1nAaf3l51ctA5FFvCRbhrH0bdAsRRQddFJZPtA==" + }, + "react-redux": { + "version": "4.4.10", + "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-4.4.10.tgz", + "integrity": "sha512-tjL0Bmpkj75Td0k+lXlF8Fc8a9GuXFv/3ahUOCXExWs/jhsKiQeTffdH0j5byejCGCRL4tvGFYlrwBF1X/Aujg==", + "requires": { + "create-react-class": "^15.5.1", + "hoist-non-react-statics": "^3.3.0", + "invariant": "^2.0.0", + "lodash": "^4.17.11", + "loose-envify": "^1.4.0", + "prop-types": "^15.7.2" + }, + "dependencies": { + "prop-types": { + "version": "15.7.2", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz", + "integrity": "sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==", + "requires": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.8.1" + } + } + } + } + } + }, "aproba": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", @@ -1060,6 +1277,11 @@ "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=", "dev": true }, + "asap": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", + "integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=" + }, "asn1": { "version": "0.2.4", "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", @@ -1149,6 +1371,11 @@ "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=", "dev": true }, + "ast-types": { + "version": "0.9.6", + "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.9.6.tgz", + "integrity": "sha1-ECyenpAF0+fjgpvwxPok7oYu6bk=" + }, "ast-types-flow": { "version": "0.0.7", "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.7.tgz", @@ -1161,6 +1388,11 @@ "integrity": "sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==", "dev": true }, + "async": { + "version": "0.2.10", + "resolved": "https://registry.npmjs.org/async/-/async-0.2.10.tgz", + "integrity": "sha1-trvgsGdLnXGXCMo43owjfLUmw9E=" + }, "async-each": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.1.tgz", @@ -1182,8 +1414,7 @@ "asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", - "dev": true + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" }, "atob": { "version": "2.1.2", @@ -1191,6 +1422,14 @@ "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", "dev": true }, + "attr-accept": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/attr-accept/-/attr-accept-1.1.3.tgz", + "integrity": "sha512-iT40nudw8zmCweivz6j58g+RT33I4KbaIvRUhjNmDwO2WmsQUxFEZZYZ5w3vXe5x5MX9D7mfvA/XaLOZYFR9EQ==", + "requires": { + "core-js": "^2.5.0" + } + }, "auth0-js": { "version": "6.8.4", "resolved": "http://registry.npmjs.org/auth0-js/-/auth0-js-6.8.4.tgz", @@ -1199,6 +1438,7 @@ "Base64": "~0.1.3", "json-fallback": "0.0.1", "jsonp": "~0.0.4", + "qs": "git+https://github.com/jfromaniello/node-querystring.git#fix_ie7_bug_with_arrays", "reqwest": "^1.1.4", "trim": "~0.0.1", "winchan": "^0.1.1", @@ -1207,10 +1447,18 @@ "dependencies": { "qs": { "version": "git+https://github.com/jfromaniello/node-querystring.git#5d96513991635e3e22d7aa54a8584d6ce97cace8", - "from": "git+https://github.com/jfromaniello/node-querystring.git#5d96513991635e3e22d7aa54a8584d6ce97cace8" + "from": "git+https://github.com/jfromaniello/node-querystring.git#fix_ie7_bug_with_arrays" } } }, + "auto-config-fake-server": { + "version": "2.0.604", + "resolved": "https://registry.npmjs.org/auto-config-fake-server/-/auto-config-fake-server-2.0.604.tgz", + "integrity": "sha1-FY5RTIR5nRQ5iNw/w7mpkwnNhkY=", + "requires": { + "sinon": "2.0.0-pre" + } + }, "autoprefixer": { "version": "8.6.5", "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-8.6.5.tgz", @@ -1237,6 +1485,14 @@ "integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==", "dev": true }, + "axios": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.8.1.tgz", + "integrity": "sha1-4Or+wPNGE5Un3Dt5/cv/gDSiQEU=", + "requires": { + "follow-redirects": "0.0.7" + } + }, "axobject-query": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-2.0.2.tgz", @@ -3187,8 +3443,7 @@ "balanced-match": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", - "dev": true + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" }, "base": { "version": "0.11.2", @@ -3262,6 +3517,11 @@ "resolved": "https://registry.npmjs.org/base16/-/base16-1.0.0.tgz", "integrity": "sha1-4pf2DX7BAUp6lxo568ipjAtoHnA=" }, + "base62": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/base62/-/base62-1.2.8.tgz", + "integrity": "sha512-V6YHUbjLxN1ymqNLb1DPHoU1CpfdL7d2YTIp5W3U4hhoG4hhxNmsFDs66M9EXxBiSEke5Bt5dwdfMwwZF70iLA==" + }, "base64-js": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.0.tgz", @@ -3309,8 +3569,12 @@ "bluebird": { "version": "3.5.2", "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.2.tgz", - "integrity": "sha512-dhHTWMI7kMx5whMQntl7Vr9C6BvV10lFXDAasnqnrMYhXVCzzk6IO9Fo2L75jXHT07WrOngL1WDXOp+yYS91Yg==", - "dev": true + "integrity": "sha512-dhHTWMI7kMx5whMQntl7Vr9C6BvV10lFXDAasnqnrMYhXVCzzk6IO9Fo2L75jXHT07WrOngL1WDXOp+yYS91Yg==" + }, + "blueimp-tmpl": { + "version": "2.5.7", + "resolved": "https://registry.npmjs.org/blueimp-tmpl/-/blueimp-tmpl-2.5.7.tgz", + "integrity": "sha1-M/sSwTnWVRKuQK+9ji3vjZ25ZJA=" }, "bn.js": { "version": "4.11.8", @@ -3358,11 +3622,20 @@ } } }, + "bourbon": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/bourbon/-/bourbon-4.3.4.tgz", + "integrity": "sha1-TaOAAp6SwMj5dkx3lFGhNLEefMM=" + }, + "bourbon-neat": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/bourbon-neat/-/bourbon-neat-1.7.2.tgz", + "integrity": "sha1-oiixJ0R53iR20yszFTEHylBTzz0=" + }, "brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -3638,11 +3911,19 @@ "integrity": "sha1-r6uWJikQp/M8GaV3WCXGnzTjUMo=", "dev": true }, + "camel-case": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-1.2.2.tgz", + "integrity": "sha1-Gsp8TRlTWaLOmVV5NDPG5VQlEfI=", + "requires": { + "sentence-case": "^1.1.1", + "upper-case": "^1.1.1" + } + }, "camelcase": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz", - "integrity": "sha1-m7UwTS4LVmmLLHWLCKPqqdqlijk=", - "dev": true + "integrity": "sha1-m7UwTS4LVmmLLHWLCKPqqdqlijk=" }, "camelcase-keys": { "version": "2.1.0", @@ -3733,6 +4014,15 @@ "integrity": "sha512-Jt9tIBkRc9POUof7QA/VwWd+58fKkEEfI+/t1/eOlxKM7ZhrczNzMFefge7Ai+39y1pR/pP6cI19guHy3FSLmw==", "dev": true }, + "center-align": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/center-align/-/center-align-0.1.3.tgz", + "integrity": "sha1-qg0yYptu6XIgBBHL1EYckHvCt60=", + "requires": { + "align-text": "^0.1.3", + "lazy-cache": "^1.0.3" + } + }, "chalk": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz", @@ -3743,6 +4033,29 @@ "supports-color": "^5.3.0" } }, + "change-case": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/change-case/-/change-case-2.3.1.tgz", + "integrity": "sha1-LE/ePwY7tB0AzWjg1aCdthy+iU8=", + "requires": { + "camel-case": "^1.1.1", + "constant-case": "^1.1.0", + "dot-case": "^1.1.0", + "is-lower-case": "^1.1.0", + "is-upper-case": "^1.1.0", + "lower-case": "^1.1.1", + "lower-case-first": "^1.0.0", + "param-case": "^1.1.0", + "pascal-case": "^1.1.0", + "path-case": "^1.1.0", + "sentence-case": "^1.1.1", + "snake-case": "^1.1.0", + "swap-case": "^1.1.0", + "title-case": "^1.1.0", + "upper-case": "^1.1.1", + "upper-case-first": "^1.1.0" + } + }, "character-entities": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-1.2.2.tgz", @@ -3773,6 +4086,11 @@ "integrity": "sha1-tUc7M9yXxCTl2Y3IfVXU2KKci/I=", "dev": true }, + "charenc": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/charenc/-/charenc-0.0.2.tgz", + "integrity": "sha1-wKHS86cJLgN3S/qD8UwPxXkKhmc=" + }, "chokidar": { "version": "1.7.0", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-1.7.0.tgz", @@ -3893,6 +4211,30 @@ } } }, + "classnames": { + "version": "2.2.6", + "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.2.6.tgz", + "integrity": "sha512-JR/iSQOSt+LQIWwrwEzJ9uk0xfN3mTVYMwt1Ir5mUcSN6pU+V4zQFFaJsclJbPuAUQH+yfWef6tm7l1quW3C8Q==" + }, + "clean-css": { + "version": "3.4.28", + "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-3.4.28.tgz", + "integrity": "sha1-vxlF6C/ICPVWlebd6uwBQA79A/8=", + "requires": { + "commander": "2.8.x", + "source-map": "0.4.x" + }, + "dependencies": { + "commander": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.8.1.tgz", + "integrity": "sha1-Br42f+v9oMMwqh4qBy09yXYkJdQ=", + "requires": { + "graceful-readlink": ">= 1.0.0" + } + } + } + }, "cli-cursor": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz", @@ -3908,6 +4250,16 @@ "integrity": "sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk=", "dev": true }, + "cliui": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-2.1.0.tgz", + "integrity": "sha1-S0dXYP+AJkx2LDoXGQMukcf+oNE=", + "requires": { + "center-align": "^0.1.1", + "right-align": "^0.1.1", + "wordwrap": "0.0.2" + } + }, "clone": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", @@ -3989,6 +4341,11 @@ } } }, + "coffee-script": { + "version": "1.12.7", + "resolved": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.12.7.tgz", + "integrity": "sha512-fLeEhqwymYat/MpTPUjSKHVYYl0ec2mOyALEMLmzr5i1isuG+6jfI2j2d5oBO3VIzgUXgBVIcOT9uH1TFxBckw==" + }, "collapse-white-space": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/collapse-white-space/-/collapse-white-space-1.0.4.tgz", @@ -4069,7 +4426,6 @@ "version": "1.0.7", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.7.tgz", "integrity": "sha512-brWl9y6vOB1xYPZcpZde3N9zDByXTosAeMDo4p1wzo6UMOX4vumB+TP1RZ76sfE6Md68Q0NJSrE/gbezd4Ul+w==", - "dev": true, "requires": { "delayed-stream": "~1.0.0" } @@ -4124,6 +4480,14 @@ "typical": "^2.6.1" } }, + "commander": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.9.0.tgz", + "integrity": "sha1-nJkJQXbhIkDLItbFFGCYQA/g99Q=", + "requires": { + "graceful-readlink": ">= 1.0.0" + } + }, "common-sequence": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/common-sequence/-/common-sequence-1.0.2.tgz", @@ -4136,11 +4500,26 @@ "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=", "dev": true }, + "commoner": { + "version": "0.10.8", + "resolved": "https://registry.npmjs.org/commoner/-/commoner-0.10.8.tgz", + "integrity": "sha1-NPw2cs0kOT6LtH5wyqApOBH08sU=", + "requires": { + "commander": "^2.5.0", + "detective": "^4.3.1", + "glob": "^5.0.15", + "graceful-fs": "^4.1.2", + "iconv-lite": "^0.4.5", + "mkdirp": "^0.5.0", + "private": "^0.1.6", + "q": "^1.1.2", + "recast": "^0.11.17" + } + }, "component-emitter": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz", - "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=", - "dev": true + "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=" }, "compressible": { "version": "2.0.15", @@ -4182,14 +4561,12 @@ "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", - "dev": true + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" }, "concat-stream": { "version": "1.5.2", "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.5.2.tgz", "integrity": "sha1-cIl4Yk2FavQaWnQd790mHadSwmY=", - "dev": true, "requires": { "inherits": "~2.0.1", "readable-stream": "~2.0.0", @@ -4236,6 +4613,15 @@ "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=", "dev": true }, + "constant-case": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/constant-case/-/constant-case-1.1.2.tgz", + "integrity": "sha1-jsLKW6ND4Aqjjb9OIA/VrJB+/WM=", + "requires": { + "snake-case": "^1.1.0", + "upper-case": "^1.1.1" + } + }, "constants-browserify": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz", @@ -4291,6 +4677,11 @@ "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" }, + "cookiejar": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.2.tgz", + "integrity": "sha512-Mw+adcfzPxcPeI+0WlvRrr/3lGVO0bD75SxX6811cxSh1Wbxx7xZBGK1eVtDf6si8rg2lhnUjsVLMFMfbRIuwA==" + }, "copy-concurrently": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/copy-concurrently/-/copy-concurrently-1.0.5.tgz", @@ -4319,8 +4710,7 @@ "core-util-is": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", - "dev": true + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" }, "cosmiconfig": { "version": "4.0.0", @@ -4399,6 +4789,37 @@ "sha.js": "^2.4.8" } }, + "create-react-class": { + "version": "15.6.3", + "resolved": "https://registry.npmjs.org/create-react-class/-/create-react-class-15.6.3.tgz", + "integrity": "sha512-M+/3Q6E6DLO6Yx3OwrWjwHBnvfXXYA7W+dFjt/ZDBemHO1DDZhsalX/NUtnTYclN6GfnBDRh4qRHjcDHmlJBJg==", + "requires": { + "fbjs": "^0.8.9", + "loose-envify": "^1.3.1", + "object-assign": "^4.1.1" + }, + "dependencies": { + "core-js": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-1.2.7.tgz", + "integrity": "sha1-ZSKUwUZR2yj6k70tX/KYOk8IxjY=" + }, + "fbjs": { + "version": "0.8.17", + "resolved": "https://registry.npmjs.org/fbjs/-/fbjs-0.8.17.tgz", + "integrity": "sha1-xNWY6taUkRJlPWWIsBpc3Nn5D90=", + "requires": { + "core-js": "^1.0.0", + "isomorphic-fetch": "^2.1.1", + "loose-envify": "^1.0.0", + "object-assign": "^4.1.0", + "promise": "^7.1.1", + "setimmediate": "^1.0.5", + "ua-parser-js": "^0.7.18" + } + } + } + }, "cross-spawn": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", @@ -4410,6 +4831,11 @@ "which": "^1.2.9" } }, + "crypt": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/crypt/-/crypt-0.0.2.tgz", + "integrity": "sha1-iNf/fsDfuG9xPch7u0LQRNPmxBs=" + }, "crypto-browserify": { "version": "3.12.0", "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz", @@ -4955,8 +5381,7 @@ "decamelize": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", - "dev": true + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=" }, "decamelize-keys": { "version": "1.1.0", @@ -4974,6 +5399,11 @@ "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=", "dev": true }, + "deep-diff": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/deep-diff/-/deep-diff-0.3.4.tgz", + "integrity": "sha1-qsXDmVIjar5fA3ojSQYLoBsArkg=" + }, "deep-equal": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.0.1.tgz", @@ -5083,8 +5513,7 @@ "defined": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/defined/-/defined-1.0.0.tgz", - "integrity": "sha1-yY2bzvdWdBiOEQlpFRGZ45sfppM=", - "dev": true + "integrity": "sha1-yY2bzvdWdBiOEQlpFRGZ45sfppM=" }, "del": { "version": "2.2.2", @@ -5112,8 +5541,7 @@ "delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", - "dev": true + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" }, "delegates": { "version": "1.0.0", @@ -5156,6 +5584,15 @@ "integrity": "sha1-9B8cEL5LAOh7XxPaaAdZ8sW/0+I=", "dev": true }, + "detective": { + "version": "4.7.1", + "resolved": "https://registry.npmjs.org/detective/-/detective-4.7.1.tgz", + "integrity": "sha512-H6PmeeUcZloWtdt4DAkFyzFL94arpHr3NOwwmVILFiy+9Qd4JTxxXrzfyGk/lmct2qVGBwTSwSXagqu2BxmWig==", + "requires": { + "acorn": "^5.2.1", + "defined": "^1.0.0" + } + }, "diff": { "version": "3.5.0", "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", @@ -5228,6 +5665,14 @@ "esutils": "^2.0.2" } }, + "dom-helpers": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-3.4.0.tgz", + "integrity": "sha512-LnuPJ+dwqKDIyotW1VzmOZ5TONUN7CwkCR5hrgawTUbkBGYdeoNLZo6nNfGkCrjtE1nXXaj7iMMpDa8/d9WoIA==", + "requires": { + "@babel/runtime": "^7.1.2" + } + }, "dom-serializer": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.1.0.tgz", @@ -5297,6 +5742,14 @@ "resolved": "https://registry.npmjs.org/dont-sniff-mimetype/-/dont-sniff-mimetype-1.0.0.tgz", "integrity": "sha1-WTKJDcn04vGeXrAqIAJuXl78j1g=" }, + "dot-case": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/dot-case/-/dot-case-1.1.2.tgz", + "integrity": "sha1-HnOCaQDeKNbeVIC8HeMdCEKwa+w=", + "requires": { + "sentence-case": "^1.1.2" + } + }, "dot-prop": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-4.2.0.tgz", @@ -5405,6 +5858,15 @@ "integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==", "dev": true }, + "envify": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/envify/-/envify-3.4.1.tgz", + "integrity": "sha1-1xIjKejfFoi6dxsSUBkXyc5cvOg=", + "requires": { + "jstransform": "^11.0.3", + "through": "~2.3.4" + } + }, "errno": { "version": "0.1.7", "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.7.tgz", @@ -5891,6 +6353,11 @@ "acorn-jsx": "^3.0.0" } }, + "esprima-fb": { + "version": "15001.1.0-dev-harmony-fb", + "resolved": "https://registry.npmjs.org/esprima-fb/-/esprima-fb-15001.1.0-dev-harmony-fb.tgz", + "integrity": "sha1-MKlHMDxrjV6VW+4rmbHSMyBqaQE=" + }, "esquery": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.0.1.tgz", @@ -6088,8 +6555,7 @@ "extend": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", - "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", - "dev": true + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" }, "extend-shallow": { "version": "3.0.2", @@ -6141,8 +6607,7 @@ "fast-deep-equal": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz", - "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=", - "dev": true + "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=" }, "fast-glob": { "version": "2.2.3", @@ -6488,8 +6953,7 @@ "fast-json-stable-stringify": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", - "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=", - "dev": true + "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=" }, "fast-levenshtein": { "version": "2.0.6", @@ -6512,6 +6976,30 @@ "bser": "^2.0.0" } }, + "fbjs": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/fbjs/-/fbjs-0.6.1.tgz", + "integrity": "sha1-lja3cF9bqWhNRLcveDISVK/IYPc=", + "requires": { + "core-js": "^1.0.0", + "loose-envify": "^1.0.0", + "promise": "^7.0.3", + "ua-parser-js": "^0.7.9", + "whatwg-fetch": "^0.9.0" + }, + "dependencies": { + "core-js": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-1.2.7.tgz", + "integrity": "sha1-ZSKUwUZR2yj6k70tX/KYOk8IxjY=" + }, + "whatwg-fetch": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-0.9.0.tgz", + "integrity": "sha1-DjaExsuZlbQ+/J3wPkw2XZX9nMA=" + } + } + }, "feature-policy": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/feature-policy/-/feature-policy-0.1.0.tgz", @@ -6572,6 +7060,11 @@ } } }, + "file-type": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-8.1.0.tgz", + "integrity": "sha512-qyQ0pzAy78gVoJsmYeNgl8uH8yKhr1lVhW7JbzJmnlRi0I4R2eEDEJZVKG8agpDnLpacwNbDhLNG/LMdxHD2YQ==" + }, "filename-regex": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/filename-regex/-/filename-regex-2.0.1.tgz", @@ -6604,6 +7097,49 @@ } } }, + "filestack-js": { + "version": "1.14.6", + "resolved": "https://registry.npmjs.org/filestack-js/-/filestack-js-1.14.6.tgz", + "integrity": "sha512-mcME182eOUy3OyU0F9rcATQf3/YY3N1suXYVv3hcS1RxeVHIIkM9XI6N9Qg5t04y0qOGud9xv/GO+oKhreCSIw==", + "requires": { + "abab": "^2.0.0", + "ajv": "^6.5.5", + "file-type": "^8.1.0", + "filestack-loader": "^3.0.4", + "is-svg": "^3.0.0", + "isutf8": "^2.0.2", + "spark-md5": "^3.0.0", + "superagent": "^3.8.3", + "tcomb-validation": "^3.4.1", + "tslib": "^1.9.3" + }, + "dependencies": { + "ajv": { + "version": "6.10.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.0.tgz", + "integrity": "sha512-nffhOpkymDECQyR0mnsUtoCE8RlX38G0rYP+wgLWFyZuUyuuojSSvi/+euOiQBIn63whYwYVIIH1TvE3tu4OEg==", + "requires": { + "fast-deep-equal": "^2.0.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "is-svg": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-svg/-/is-svg-3.0.0.tgz", + "integrity": "sha512-gi4iHK53LR2ujhLVVj+37Ykh9GLqYHX6JOVXbLAucaG/Cqw9xwdFOjDM2qeifLs1sF1npXXFvDu0r5HNgCMrzQ==", + "requires": { + "html-comment-regex": "^1.1.0" + } + } + } + }, + "filestack-loader": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/filestack-loader/-/filestack-loader-3.0.4.tgz", + "integrity": "sha512-b6uOCWHd1gM0+5KBA1rA4qfEgTqyTr5umLM4bBWT4z98WUwxa6KzCiq+z0VnR4rN+NCx6kyZ/wLXjGcPU32TxQ==" + }, "fill-range": { "version": "2.2.4", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-2.2.4.tgz", @@ -6731,7 +7267,31 @@ "resolved": "https://registry.npmjs.org/flux-standard-action/-/flux-standard-action-2.0.3.tgz", "integrity": "sha512-HR2IjMkqJreoFm1Hx7hmMAtUFeo+ad8hPMYPo8o3YSWjbSq0sMwuXMbv4giB3TXngYB7+svkAJewQwwvwsE6xw==", "requires": { - "lodash": "^4.0.0" + "lodash": "^4.0.0" + } + }, + "follow-redirects": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-0.0.7.tgz", + "integrity": "sha1-NLkLqyqRGqNHVx2pDyK9NuzYqRk=", + "requires": { + "debug": "^2.2.0", + "stream-consume": "^0.1.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + } } }, "for-in": { @@ -6759,13 +7319,38 @@ "version": "2.3.3", "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", - "dev": true, "requires": { "asynckit": "^0.4.0", "combined-stream": "^1.0.6", "mime-types": "^2.1.12" } }, + "form-data-to-object": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/form-data-to-object/-/form-data-to-object-0.2.0.tgz", + "integrity": "sha1-96jmjd2RChEApl4lrGpIQUP/gWg=" + }, + "formatio": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/formatio/-/formatio-1.1.1.tgz", + "integrity": "sha1-XtPM1jZVEJc4NGXZlhmRAOhhYek=", + "requires": { + "samsam": "~1.1" + } + }, + "formidable": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/formidable/-/formidable-1.2.1.tgz", + "integrity": "sha512-Fs9VRguL0gqGHkXS5GQiMCr1VhZBxz0JnJs4JmMp/2jL18Fmbzvv7vOFRU+U8TBkHEE/CX1qDXzJplVULgsLeg==" + }, + "formsy-react": { + "version": "0.19.5", + "resolved": "https://registry.npmjs.org/formsy-react/-/formsy-react-0.19.5.tgz", + "integrity": "sha1-dgpXrAETRC499MMJw2ON2SlX544=", + "requires": { + "form-data-to-object": "^0.2.0" + } + }, "forwarded": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", @@ -7510,6 +8095,18 @@ "assert-plus": "^1.0.0" } }, + "glob": { + "version": "5.0.15", + "resolved": "https://registry.npmjs.org/glob/-/glob-5.0.15.tgz", + "integrity": "sha1-G8k2ueAvSmA/zCIuz3Yz0wuLk7E=", + "requires": { + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "2 || 3", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, "glob-base": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/glob-base/-/glob-base-0.3.0.tgz", @@ -7646,8 +8243,12 @@ "graceful-fs": { "version": "4.1.11", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", - "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=", - "dev": true + "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=" + }, + "graceful-readlink": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/graceful-readlink/-/graceful-readlink-1.0.1.tgz", + "integrity": "sha1-TK+tdrxi8C+gObL5Tpo906ORpyU=" }, "growly": { "version": "1.3.0", @@ -7865,6 +8466,11 @@ "minimalistic-assert": "^1.0.1" } }, + "he": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.0.0.tgz", + "integrity": "sha1-baWyZdfyw7XkgHSRaODhWdBXKNo=" + }, "helmet": { "version": "3.14.0", "resolved": "https://registry.npmjs.org/helmet/-/helmet-3.14.0.tgz", @@ -7907,6 +8513,17 @@ "resolved": "https://registry.npmjs.org/hide-powered-by/-/hide-powered-by-1.0.0.tgz", "integrity": "sha1-SoWtZYgfYoV/xwr3F0oRhNzM4ys=" }, + "history": { + "version": "1.17.0", + "resolved": "https://registry.npmjs.org/history/-/history-1.17.0.tgz", + "integrity": "sha1-xUg8qlodH+oAoafY0ZuHQBZxHSk=", + "requires": { + "deep-equal": "^1.0.0", + "invariant": "^2.0.0", + "query-string": "^3.0.0", + "warning": "^2.0.0" + } + }, "hmac-drbg": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", @@ -7952,8 +8569,7 @@ "html-comment-regex": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/html-comment-regex/-/html-comment-regex-1.1.2.tgz", - "integrity": "sha512-P+M65QY2JQ5Y0G9KKdlDpo0zK+/OHptU5AaBwUfAIDJZk1MYf32Frm84EcOytfJE0t5JvkAnKlmjsXDnWzCJmQ==", - "dev": true + "integrity": "sha512-P+M65QY2JQ5Y0G9KKdlDpo0zK+/OHptU5AaBwUfAIDJZk1MYf32Frm84EcOytfJE0t5JvkAnKlmjsXDnWzCJmQ==" }, "html-encoding-sniffer": { "version": "1.0.2", @@ -7970,12 +8586,45 @@ "integrity": "sha1-DfKTUfByEWNRXfueVUPl9u7VFi8=", "dev": true }, + "html-minifier": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/html-minifier/-/html-minifier-1.5.0.tgz", + "integrity": "sha1-vrBf2cw0CUWGXBD0Cu30aa9LFTQ=", + "requires": { + "change-case": "2.3.x", + "clean-css": "3.4.x", + "commander": "2.9.x", + "concat-stream": "1.5.x", + "he": "1.0.x", + "ncname": "1.0.x", + "relateurl": "0.2.x", + "uglify-js": "2.6.x" + } + }, "html-tags": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/html-tags/-/html-tags-2.0.0.tgz", "integrity": "sha1-ELMKOGCF9Dzt41PMj6fLDe7qZos=", "dev": true }, + "html-webpack-plugin": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-1.7.0.tgz", + "integrity": "sha1-zQxzx5G9DIxFsk4wAb4zSmt0KXs=", + "requires": { + "bluebird": "^3.0.5", + "blueimp-tmpl": "^2.5.5", + "html-minifier": "^1.0.0", + "lodash": "^3.10.1" + }, + "dependencies": { + "lodash": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-3.10.1.tgz", + "integrity": "sha1-W/Rejkm6QYnhfUgnid/RW9FAt7Y=" + } + } + }, "htmlparser2": { "version": "3.10.0", "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.10.0.tgz", @@ -8040,6 +8689,11 @@ "integrity": "sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=", "dev": true }, + "humps": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/humps/-/humps-0.6.0.tgz", + "integrity": "sha1-phchA4bwRF0SLOtNlBSho5saHpQ=" + }, "iconv-lite": { "version": "0.4.24", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", @@ -8174,7 +8828,6 @@ "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "dev": true, "requires": { "once": "^1.3.0", "wrappy": "1" @@ -8304,8 +8957,7 @@ "is-buffer": { "version": "1.1.6", "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", - "dev": true + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" }, "is-builtin-module": { "version": "1.0.0", @@ -8440,6 +9092,14 @@ "integrity": "sha512-but/G3sapV3MNyqiDBLrOi4x8uCIw0RY3o/Vb5GT0sMFHrVV7731wFSVy41T5FO1og7G0gXLJh0MkgPRouko/A==", "dev": true }, + "is-lower-case": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/is-lower-case/-/is-lower-case-1.1.3.tgz", + "integrity": "sha1-fhR75HaNxGbbO/shzGCzHmrWk5M=", + "requires": { + "lower-case": "^1.1.0" + } + }, "is-number": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-2.1.0.tgz", @@ -8540,6 +9200,11 @@ "integrity": "sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg==", "dev": true }, + "is-retina": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/is-retina/-/is-retina-1.0.3.tgz", + "integrity": "sha1-10AbKGvqKuN/Ykd1iN5QTQuGR+M=" + }, "is-stream": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", @@ -8575,6 +9240,14 @@ "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", "dev": true }, + "is-upper-case": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/is-upper-case/-/is-upper-case-1.1.2.tgz", + "integrity": "sha1-jQsfp+eTOh5YSDYA7H2WYcuvdW8=", + "requires": { + "upper-case": "^1.1.0" + } + }, "is-utf8": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", @@ -8607,8 +9280,7 @@ "isarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" }, "isexe": { "version": "2.0.0", @@ -8776,6 +9448,11 @@ "handlebars": "^4.0.3" } }, + "isutf8": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/isutf8/-/isutf8-2.0.3.tgz", + "integrity": "sha512-ucppMz9qxhSceRJ8bP5SfdMdXukV718zXVgeSznBXkDGHbIcN5nptCPnosZhsN959eATLCD3751fo8tD86hM2Q==" + }, "jest": { "version": "23.6.0", "resolved": "https://registry.npmjs.org/jest/-/jest-23.6.0.tgz", @@ -9487,8 +10164,7 @@ "json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" }, "json-stable-stringify-without-jsonify": { "version": "1.0.1", @@ -9536,6 +10212,25 @@ "verror": "1.10.0" } }, + "jstransform": { + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/jstransform/-/jstransform-11.0.3.tgz", + "integrity": "sha1-CaeJk+CuTU70SH9hVakfYZDLQiM=", + "requires": { + "base62": "^1.1.0", + "commoner": "^0.10.1", + "esprima-fb": "^15001.1.0-dev-harmony-fb", + "object-assign": "^2.0.0", + "source-map": "^0.4.2" + }, + "dependencies": { + "object-assign": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-2.1.1.tgz", + "integrity": "sha1-Q8NuXVaf+OSBbE76i+AtJpZ8GKo=" + } + } + }, "jsx-ast-utils": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-2.0.1.tgz", @@ -9550,11 +10245,18 @@ "resolved": "https://registry.npmjs.org/just-curry-it/-/just-curry-it-3.1.0.tgz", "integrity": "sha512-mjzgSOFzlrurlURaHVjnQodyPNvrHrf1TbQP2XU9NSqBtHQPuHZ+Eb6TAJP7ASeJN9h9K0KXoRTs8u6ouHBKvg==" }, + "jwt-decode": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/jwt-decode/-/jwt-decode-1.5.1.tgz", + "integrity": "sha1-vajYcxubc57otKMaDQJcqUrpLTs=", + "requires": { + "Base64": "~0.1.3" + } + }, "kind-of": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, "requires": { "is-buffer": "^1.1.5" } @@ -9590,6 +10292,11 @@ "webpack-sources": "^1.1.0" } }, + "lazy-cache": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-1.0.4.tgz", + "integrity": "sha1-odePw6UEdMuAhF07O24dpJpEbo4=" + }, "lcid": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz", @@ -9641,6 +10348,23 @@ "type-check": "~0.3.2" } }, + "libphonenumber-js": { + "version": "1.7.15", + "resolved": "https://registry.npmjs.org/libphonenumber-js/-/libphonenumber-js-1.7.15.tgz", + "integrity": "sha512-FYqkPqPc8ZBAeGiCuU/4eKqvCyBP281DtayXoc+9XuRhgARn+CCTvy30VjfCevPROQkVxZKe2SfWj4d/3VrmVw==", + "requires": { + "minimist": "^1.2.0", + "semver-compare": "^1.0.0", + "xml2js": "^0.4.17" + }, + "dependencies": { + "minimist": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=" + } + } + }, "load-json-file": { "version": "2.0.0", "resolved": "http://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz", @@ -9909,6 +10633,16 @@ "chalk": "^2.0.1" } }, + "lolex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/lolex/-/lolex-1.3.2.tgz", + "integrity": "sha1-fD2mL/yzDw9agKJWbKJORdigHzE=" + }, + "longest": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz", + "integrity": "sha1-MKCy2jj3N3DoKUoNIuZiXtd9AJc=" + }, "longest-streak": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/longest-streak/-/longest-streak-2.0.2.tgz", @@ -9933,6 +10667,19 @@ "signal-exit": "^3.0.0" } }, + "lower-case": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-1.1.4.tgz", + "integrity": "sha1-miyr0bno4K6ZOkv31YdcOcQujqw=" + }, + "lower-case-first": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/lower-case-first/-/lower-case-first-1.0.2.tgz", + "integrity": "sha1-5dp8JvKacHO+AtUrrJmA5ZIq36E=", + "requires": { + "lower-case": "^1.1.2" + } + }, "lru-cache": { "version": "4.1.3", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.3.tgz", @@ -10027,6 +10774,16 @@ "integrity": "sha512-3Zs9P/0zzwTob2pdgT0CHZuMbnSUSp8MB1bddfm+HDmnFWHGT4jvEZRf+2RuPoa+cjdn/z25SEt5gFTqdhvJAg==", "dev": true }, + "md5": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/md5/-/md5-2.2.1.tgz", + "integrity": "sha1-U6s41f48iJG6RlMp6iP6wFQBJvk=", + "requires": { + "charenc": "~0.0.1", + "crypt": "~0.0.1", + "is-buffer": "~1.1.1" + } + }, "md5.js": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", @@ -10311,7 +11068,6 @@ "version": "3.0.4", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "dev": true, "requires": { "brace-expansion": "^1.1.7" } @@ -10319,8 +11075,7 @@ "minimist": { "version": "0.0.8", "resolved": "http://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", - "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", - "dev": true + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" }, "minimist-options": { "version": "3.0.2", @@ -10393,7 +11148,6 @@ "version": "0.5.1", "resolved": "http://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", - "dev": true, "requires": { "minimist": "0.0.8" } @@ -10541,6 +11295,14 @@ "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", "dev": true }, + "ncname": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/ncname/-/ncname-1.0.0.tgz", + "integrity": "sha1-W1etGLHKCShk72Kwse2BlPODtxw=", + "requires": { + "xml-char-classes": "^1.0.0" + } + }, "negotiator": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz", @@ -10563,6 +11325,14 @@ "resolved": "https://registry.npmjs.org/nocache/-/nocache-2.0.0.tgz", "integrity": "sha1-ICtIAhoMTL3i34DeFaF0Q8i0OYA=" }, + "node-bourbon": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/node-bourbon/-/node-bourbon-4.2.8.tgz", + "integrity": "sha1-5ETx8JQ0q3ZQ6jGMKOLhA9P5Qs0=", + "requires": { + "bourbon": "^4.2.6" + } + }, "node-fetch": { "version": "1.7.3", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-1.7.3.tgz", @@ -10709,6 +11479,15 @@ } } }, + "node-neat": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/node-neat/-/node-neat-1.7.2.tgz", + "integrity": "sha1-OEcpELgV4mG4sbmbpRmZRGWhXCE=", + "requires": { + "bourbon-neat": "1.7.2", + "node-bourbon": "^4.2.3" + } + }, "node-notifier": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/node-notifier/-/node-notifier-5.3.0.tgz", @@ -10896,6 +11675,21 @@ } } }, + "normalizr": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/normalizr/-/normalizr-1.4.1.tgz", + "integrity": "sha1-qjh8JGXxNhHK86rkK6+Y9wXoos4=", + "requires": { + "lodash": "^3.10.0" + }, + "dependencies": { + "lodash": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-3.10.1.tgz", + "integrity": "sha1-W/Rejkm6QYnhfUgnid/RW9FAt7Y=" + } + } + }, "npm-run-path": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", @@ -11094,7 +11888,6 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "dev": true, "requires": { "wrappy": "1" } @@ -11291,6 +12084,14 @@ } } }, + "param-case": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/param-case/-/param-case-1.1.2.tgz", + "integrity": "sha1-3LCRpDwlm5Io8cNB57akTqC/l0M=", + "requires": { + "sentence-case": "^1.1.2" + } + }, "parse-asn1": { "version": "5.1.1", "resolved": "http://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.1.tgz", @@ -11355,6 +12156,15 @@ "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.2.tgz", "integrity": "sha1-/CidTtiZMRlGDBViUyYs3I3mW/M=" }, + "pascal-case": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/pascal-case/-/pascal-case-1.1.2.tgz", + "integrity": "sha1-Pl1kogBDgwp8STRMLXS0G+DJyZs=", + "requires": { + "camel-case": "^1.1.1", + "upper-case-first": "^1.1.0" + } + }, "pascalcase": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", @@ -11367,6 +12177,14 @@ "integrity": "sha1-oLhwcpquIUAFt9UDLsLLuw+0RRo=", "dev": true }, + "path-case": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/path-case/-/path-case-1.1.2.tgz", + "integrity": "sha1-UM5roNO+090LXCqcRVNpdDRAlRQ=", + "requires": { + "sentence-case": "^1.1.2" + } + }, "path-dirname": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz", @@ -11382,8 +12200,7 @@ "path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", - "dev": true + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" }, "path-is-inside": { "version": "1.0.2", @@ -11509,6 +12326,11 @@ "integrity": "sha512-2qHaIQr2VLRFoxe2nASzsV6ef4yOOH+Fi9FBOVH6cqeSgUnoyySPZkxzLuzd+RYOQTRpROA0ztTMqxROKSb/nA==", "dev": true }, + "popper.js": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/popper.js/-/popper.js-1.15.0.tgz", + "integrity": "sha512-w010cY1oCUmI+9KwwlWki+r5jxKfTFDVoadl7MSrIujHU5MJ5OR6HTDj6Xo8aoR/QsA56x8jKjA59qGH4ELtrA==" + }, "posix-character-classes": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", @@ -13827,8 +14649,7 @@ "private": { "version": "0.1.8", "resolved": "https://registry.npmjs.org/private/-/private-0.1.8.tgz", - "integrity": "sha512-VvivMrbvd2nKkiG38qjULzlc+4Vx4wm/whI9pQD35YrARNnhxeiRktSOhSukRLFNlzg6Br/cJPet5J/u19r/mg==", - "dev": true + "integrity": "sha512-VvivMrbvd2nKkiG38qjULzlc+4Vx4wm/whI9pQD35YrARNnhxeiRktSOhSukRLFNlzg6Br/cJPet5J/u19r/mg==" }, "process": { "version": "0.5.2", @@ -13839,8 +14660,7 @@ "process-nextick-args": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz", - "integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M=", - "dev": true + "integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M=" }, "progress": { "version": "2.0.1", @@ -13848,6 +14668,14 @@ "integrity": "sha512-OE+a6vzqazc+K6LxJrX5UPyKFvGnL5CYmq2jFGNIBWHpc4QyE49/YOumcrpQFJpfejmvRtbJzgO1zPmMCqlbBg==", "dev": true }, + "promise": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz", + "integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==", + "requires": { + "asap": "~2.0.3" + } + }, "promise-inflight": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", @@ -13938,8 +14766,7 @@ "punycode": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", - "dev": true + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" }, "pure-color": { "version": "1.3.0", @@ -13949,14 +14776,21 @@ "q": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz", - "integrity": "sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc=", - "dev": true + "integrity": "sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc=" }, "qs": { "version": "6.5.2", "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==" }, + "query-string": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/query-string/-/query-string-3.0.3.tgz", + "integrity": "sha1-ri4UtNBQcdTpuetIc8NbDc1C5jg=", + "requires": { + "strict-uri-encode": "^1.0.0" + } + }, "querystring": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", @@ -14069,6 +14903,76 @@ "scheduler": "^0.10.0" } }, + "react-addons-pure-render-mixin": { + "version": "15.6.2", + "resolved": "https://registry.npmjs.org/react-addons-pure-render-mixin/-/react-addons-pure-render-mixin-15.6.2.tgz", + "integrity": "sha1-a4P0C2s27kBzXL1hJes/E84c3ck=", + "requires": { + "fbjs": "^0.8.4", + "object-assign": "^4.1.0" + }, + "dependencies": { + "core-js": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-1.2.7.tgz", + "integrity": "sha1-ZSKUwUZR2yj6k70tX/KYOk8IxjY=" + }, + "fbjs": { + "version": "0.8.17", + "resolved": "https://registry.npmjs.org/fbjs/-/fbjs-0.8.17.tgz", + "integrity": "sha1-xNWY6taUkRJlPWWIsBpc3Nn5D90=", + "requires": { + "core-js": "^1.0.0", + "isomorphic-fetch": "^2.1.1", + "loose-envify": "^1.0.0", + "object-assign": "^4.1.0", + "promise": "^7.1.1", + "setimmediate": "^1.0.5", + "ua-parser-js": "^0.7.18" + } + } + } + }, + "react-addons-update": { + "version": "15.6.2", + "resolved": "https://registry.npmjs.org/react-addons-update/-/react-addons-update-15.6.2.tgz", + "integrity": "sha1-5TdTxbNIh5dFEMiC1/sHWFHV5QQ=", + "requires": { + "fbjs": "^0.8.9", + "object-assign": "^4.1.0" + }, + "dependencies": { + "core-js": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-1.2.7.tgz", + "integrity": "sha1-ZSKUwUZR2yj6k70tX/KYOk8IxjY=" + }, + "fbjs": { + "version": "0.8.17", + "resolved": "https://registry.npmjs.org/fbjs/-/fbjs-0.8.17.tgz", + "integrity": "sha1-xNWY6taUkRJlPWWIsBpc3Nn5D90=", + "requires": { + "core-js": "^1.0.0", + "isomorphic-fetch": "^2.1.1", + "loose-envify": "^1.0.0", + "object-assign": "^4.1.0", + "promise": "^7.1.1", + "setimmediate": "^1.0.5", + "ua-parser-js": "^0.7.18" + } + } + } + }, + "react-avatar": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/react-avatar/-/react-avatar-2.5.1.tgz", + "integrity": "sha512-bwH5pWY6uxaKZt+IZBfD+SU3Dpy3FaKbmAzrOI4N8SATUPLXOdGaJHWUl6Vl8hHSwWSsoLh/m7xYHdnn0lofZw==", + "requires": { + "babel-runtime": ">=5.0.0", + "is-retina": "^1.0.3", + "md5": "^2.0.0" + } + }, "react-base16-styling": { "version": "0.5.3", "resolved": "https://registry.npmjs.org/react-base16-styling/-/react-base16-styling-0.5.3.tgz", @@ -14096,6 +15000,24 @@ } } }, + "react-datetime": { + "version": "2.16.3", + "resolved": "https://registry.npmjs.org/react-datetime/-/react-datetime-2.16.3.tgz", + "integrity": "sha512-amWfb5iGEiyqjLmqCLlPpu2oN415jK8wX1qoTq7qn6EYiU7qQgbNHglww014PT4O/3G5eo/3kbJu/M/IxxTyGw==", + "requires": { + "create-react-class": "^15.5.2", + "object-assign": "^3.0.0", + "prop-types": "^15.5.7", + "react-onclickoutside": "^6.5.0" + }, + "dependencies": { + "object-assign": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-3.0.0.tgz", + "integrity": "sha1-m+3VygiXlJvKR+f/QIBi1Un1h/I=" + } + } + }, "react-dock": { "version": "0.2.4", "resolved": "https://registry.npmjs.org/react-dock/-/react-dock-0.2.4.tgz", @@ -14116,6 +15038,15 @@ "scheduler": "^0.10.0" } }, + "react-dropzone": { + "version": "3.13.4", + "resolved": "https://registry.npmjs.org/react-dropzone/-/react-dropzone-3.13.4.tgz", + "integrity": "sha1-hNomgVxAM5aRxJtFRMLvehaRLMw=", + "requires": { + "attr-accept": "^1.0.3", + "prop-types": "^15.5.7" + } + }, "react-helmet": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/react-helmet/-/react-helmet-5.2.0.tgz", @@ -14141,6 +15072,11 @@ "shallowequal": "^1.0.2" } }, + "react-input-autosize": { + "version": "0.6.13", + "resolved": "https://registry.npmjs.org/react-input-autosize/-/react-input-autosize-0.6.13.tgz", + "integrity": "sha1-OG/3qdLD3AFsJlvy5Z05cFD2Wvc=" + }, "react-is": { "version": "16.6.0", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.6.0.tgz", @@ -14157,11 +15093,32 @@ "react-base16-styling": "^0.5.1" } }, + "react-lazy-cache": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/react-lazy-cache/-/react-lazy-cache-3.0.1.tgz", + "integrity": "sha1-DcZNON8XZ+93Z4xclBkAZMsRsM0=", + "requires": { + "deep-equal": "^1.0.1" + } + }, "react-lifecycles-compat": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz", - "integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==", - "dev": true + "integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==" + }, + "react-onclickoutside": { + "version": "6.8.0", + "resolved": "https://registry.npmjs.org/react-onclickoutside/-/react-onclickoutside-6.8.0.tgz", + "integrity": "sha512-5Q4Rn7QLEoh7WIe66KFvYIpWJ49GeHoygP1/EtJyZjXKgrWH19Tf0Ty3lWyQzrEEDyLOwUvvmBFSE3dcDdvagA==" + }, + "react-popper": { + "version": "0.7.5", + "resolved": "https://registry.npmjs.org/react-popper/-/react-popper-0.7.5.tgz", + "integrity": "sha512-ya9dhhGCf74JTOB2uyksEHhIGw7w9tNZRUJF73lEq2h4H5JT6MBa4PdT4G+sx6fZwq+xKZAL/sVNAIuojPn7Dg==", + "requires": { + "popper.js": "^1.12.5", + "prop-types": "^15.5.10" + } }, "react-pure-render": { "version": "1.0.2", @@ -14206,6 +15163,15 @@ } } }, + "react-router": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-1.0.3.tgz", + "integrity": "sha1-mA7KoFW4bkfIZUjCMq4FqIpB8Lc=", + "requires": { + "invariant": "^2.0.0", + "warning": "^2.0.0" + } + }, "react-router-dom": { "version": "4.3.1", "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-4.3.1.tgz", @@ -14265,6 +15231,15 @@ } } }, + "react-select": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/react-select/-/react-select-0.9.1.tgz", + "integrity": "sha1-4yKi0KBjlqSCBrBVPfXsR9Fgg7o=", + "requires": { + "classnames": "^2.2.0", + "react-input-autosize": "^0.6.2" + } + }, "react-side-effect": { "version": "1.1.5", "resolved": "https://registry.npmjs.org/react-side-effect/-/react-side-effect-1.1.5.tgz", @@ -14274,16 +15249,40 @@ "shallowequal": "^1.0.1" } }, + "react-switch-button": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/react-switch-button/-/react-switch-button-1.1.2.tgz", + "integrity": "sha1-jOhPaUa046k3PnttasjngNl/L08=" + }, "react-test-renderer": { "version": "16.6.0", "resolved": "https://registry.npmjs.org/react-test-renderer/-/react-test-renderer-16.6.0.tgz", "integrity": "sha512-w+Y3YT7OX1LP5KO7HCd0YR34Ol1qmISHaooPNMRYa6QzmwtcWhEGuZPr34wO8UCBIokswuhyLQUq7rjPDcEtJA==", "dev": true, "requires": { - "object-assign": "^4.1.1", + "object-assign": "^4.1.1", + "prop-types": "^15.6.2", + "react-is": "^16.6.0", + "scheduler": "^0.10.0" + } + }, + "react-textarea-autosize": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/react-textarea-autosize/-/react-textarea-autosize-5.2.1.tgz", + "integrity": "sha512-bx6z2I35aapr71ggw2yZIA4qhmqeTa4ZVsSaTeFvtf9kfcZppDBh2PbMt8lvbdmzEk7qbSFhAxR9vxEVm6oiMg==", + "requires": { + "prop-types": "^15.6.0" + } + }, + "react-transition-group": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-2.9.0.tgz", + "integrity": "sha512-+HzNTCHpeQyl4MJ/bdE0u6XRMe9+XG/+aL4mCxVN4DnPBQ0/5bfHWPDuOZUzYdMj94daZaZdCCc1Dzt9R/xSSg==", + "requires": { + "dom-helpers": "^3.4.0", + "loose-envify": "^1.4.0", "prop-types": "^15.6.2", - "react-is": "^16.6.0", - "scheduler": "^0.10.0" + "react-lifecycles-compat": "^3.0.4" } }, "read-pkg": { @@ -14311,7 +15310,6 @@ "version": "2.0.6", "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.0.6.tgz", "integrity": "sha1-j5A0HmilPMySh4jaz80Rs265t44=", - "dev": true, "requires": { "core-util-is": "~1.0.0", "inherits": "~2.0.1", @@ -14632,6 +15630,29 @@ "util.promisify": "^1.0.0" } }, + "recast": { + "version": "0.11.23", + "resolved": "https://registry.npmjs.org/recast/-/recast-0.11.23.tgz", + "integrity": "sha1-RR/TAEqx5N+bTktmN2sqIZEkYtM=", + "requires": { + "ast-types": "0.9.6", + "esprima": "~3.1.0", + "private": "~0.1.5", + "source-map": "~0.5.0" + }, + "dependencies": { + "esprima": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-3.1.3.tgz", + "integrity": "sha1-/cpRzuYTOJXjyI1TXOSdv/YqRjM=" + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" + } + } + }, "reconnect-core": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/reconnect-core/-/reconnect-core-1.3.0.tgz", @@ -14843,6 +15864,32 @@ "base16": "^1.0.0" } }, + "redux-form": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/redux-form/-/redux-form-4.2.2.tgz", + "integrity": "sha1-uK43pAcJBvRdvTCwcinxoQvyXLA=", + "requires": { + "deep-equal": "^1.0.1", + "hoist-non-react-statics": "^1.0.5", + "is-promise": "^2.1.0", + "react-lazy-cache": "^3.0.1" + }, + "dependencies": { + "hoist-non-react-statics": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-1.2.0.tgz", + "integrity": "sha1-qkSM8JhtVcxAdzsXF0t90GbLfPs=" + } + } + }, + "redux-logger": { + "version": "2.10.2", + "resolved": "https://registry.npmjs.org/redux-logger/-/redux-logger-2.10.2.tgz", + "integrity": "sha1-PFpfCm8yV3wd6t9mVfJX+CxsOTc=", + "requires": { + "deep-diff": "0.3.4" + } + }, "redux-promise": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/redux-promise/-/redux-promise-0.6.0.tgz", @@ -14852,6 +15899,19 @@ "is-promise": "^2.1.0" } }, + "redux-router": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/redux-router/-/redux-router-1.0.0.tgz", + "integrity": "sha1-PBZ240Qb7FD+jZJFfAF8tjaZM08=", + "requires": { + "deep-equal": "^1.0.1" + } + }, + "redux-thunk": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-2.3.0.tgz", + "integrity": "sha512-km6dclyFnmcvxhAcrQV2AkZmPQjzPDjgVlQtR0EQjxZPyJ0BnMf3in1ryuR8A2qU0HldVRfxYXbFSKlI3N7Slw==" + }, "referrer-policy": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/referrer-policy/-/referrer-policy-1.1.0.tgz", @@ -14956,6 +16016,11 @@ } } }, + "relateurl": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz", + "integrity": "sha1-VNvzd+UUQKypCkzSdGANP/LYiKk=" + }, "remark": { "version": "9.0.0", "resolved": "https://registry.npmjs.org/remark/-/remark-9.0.0.tgz", @@ -15043,8 +16108,7 @@ "repeat-string": { "version": "1.6.1", "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", - "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=", - "dev": true + "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=" }, "repeating": { "version": "2.0.1", @@ -15292,6 +16356,14 @@ "integrity": "sha1-mUWygD8hni96ygCtuLyfZA+ELJo=", "dev": true }, + "right-align": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/right-align/-/right-align-0.1.3.tgz", + "integrity": "sha1-YTObci/mo1FWiSENJOFMlhSGE+8=", + "requires": { + "align-text": "^0.1.1" + } + }, "rimraf": { "version": "2.6.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz", @@ -15385,6 +16457,11 @@ "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, + "samsam": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/samsam/-/samsam-1.1.2.tgz", + "integrity": "sha1-vsEf3IOp/aBjQBIQ5AF2wwJNFWc=" + }, "sane": { "version": "2.5.2", "resolved": "https://registry.npmjs.org/sane/-/sane-2.5.2.tgz", @@ -15923,8 +17000,7 @@ "sax": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", - "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", - "dev": true + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" }, "scheduler": { "version": "0.10.0", @@ -15966,6 +17042,11 @@ "resolved": "http://registry.npmjs.org/semver/-/semver-5.1.0.tgz", "integrity": "sha1-hfLPhVBGXE3wAM99hvawVBBqueU=" }, + "semver-compare": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/semver-compare/-/semver-compare-1.0.0.tgz", + "integrity": "sha1-De4hahyUGrN+nvsXiPavxf9VN/w=" + }, "send": { "version": "0.16.2", "resolved": "https://registry.npmjs.org/send/-/send-0.16.2.tgz", @@ -16006,6 +17087,14 @@ } } }, + "sentence-case": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/sentence-case/-/sentence-case-1.1.3.tgz", + "integrity": "sha1-gDSq/CFFdy06vhUJqkLJ4QQtwTk=", + "requires": { + "lower-case": "^1.1.1" + } + }, "serialize-javascript": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-1.5.0.tgz", @@ -16073,8 +17162,7 @@ "setimmediate": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", - "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=", - "dev": true + "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=" }, "setprototypeof": { "version": "1.1.0", @@ -16150,6 +17238,18 @@ "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", "dev": true }, + "sinon": { + "version": "2.0.0-pre", + "resolved": "https://registry.npmjs.org/sinon/-/sinon-2.0.0-pre.tgz", + "integrity": "sha1-GCk7APsvFVyZ6OW0bjH36t4ygV0=", + "requires": { + "formatio": "1.1.1", + "lolex": "1.3.2", + "samsam": "1.1.2", + "text-encoding": "0.5.2", + "util": ">=0.10.3 <1" + } + }, "sisteransi": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-0.1.1.tgz", @@ -16171,6 +17271,14 @@ "is-fullwidth-code-point": "^2.0.0" } }, + "snake-case": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/snake-case/-/snake-case-1.1.2.tgz", + "integrity": "sha1-DC8l4wUVjZoY09l3BmGH/vilpmo=", + "requires": { + "sentence-case": "^1.1.2" + } + }, "snapdragon": { "version": "0.8.2", "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", @@ -16341,7 +17449,6 @@ "version": "0.4.4", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz", "integrity": "sha1-66T12pwNyZneaAMti092FzZSA2s=", - "dev": true, "requires": { "amdefine": ">=0.0.4" } @@ -16382,6 +17489,11 @@ "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=", "dev": true }, + "spark-md5": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/spark-md5/-/spark-md5-3.0.0.tgz", + "integrity": "sha1-NyIifFTi+vJLHcbZM8wUTm9xv+8=" + }, "spdx-correct": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.0.2.tgz", @@ -16544,6 +17656,11 @@ } } }, + "stream-consume": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/stream-consume/-/stream-consume-0.1.1.tgz", + "integrity": "sha512-tNa3hzgkjEP7XbCkbRXe1jpg+ievoa0O4SCFlMOYEscGSS4JJsckGL8swUyAa/ApGU3Ae4t6Honor4HhL+tRyg==" + }, "stream-each": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/stream-each/-/stream-each-1.2.3.tgz", @@ -16620,8 +17737,7 @@ "strict-uri-encode": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz", - "integrity": "sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM=", - "dev": true + "integrity": "sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM=" }, "string-hash": { "version": "1.1.3", @@ -16686,8 +17802,7 @@ "string_decoder": { "version": "0.10.31", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", - "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", - "dev": true + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=" }, "stringify-entities": { "version": "1.3.2", @@ -17165,6 +18280,60 @@ } } }, + "superagent": { + "version": "3.8.3", + "resolved": "https://registry.npmjs.org/superagent/-/superagent-3.8.3.tgz", + "integrity": "sha512-GLQtLMCoEIK4eDv6OGtkOoSMt3D+oq0y3dsxMuYuDvaNUvuT8eFBuLmfR0iYYzHC1e8hpzC6ZsxbuP6DIalMFA==", + "requires": { + "component-emitter": "^1.2.0", + "cookiejar": "^2.1.0", + "debug": "^3.1.0", + "extend": "^3.0.0", + "form-data": "^2.3.1", + "formidable": "^1.2.0", + "methods": "^1.1.1", + "mime": "^1.4.1", + "qs": "^6.5.1", + "readable-stream": "^2.3.5" + }, + "dependencies": { + "debug": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "requires": { + "ms": "^2.1.1" + } + }, + "process-nextick-args": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", + "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==" + }, + "readable-stream": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, "supports-color": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", @@ -17194,6 +18363,15 @@ "whet.extend": "~0.9.9" } }, + "swap-case": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/swap-case/-/swap-case-1.1.2.tgz", + "integrity": "sha1-w5IDpFhzhfrTyFCgvRvK+ggZdOM=", + "requires": { + "lower-case": "^1.1.1", + "upper-case": "^1.1.1" + } + }, "symbol-observable": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.2.0.tgz", @@ -17298,8 +18476,10 @@ "angular-messages": "^1.5.2", "appirio-tech-ng-iso-constants": "^1.0.6", "appirio-tech-ng-ui-components": "^2.2.4", + "appirio-tech-react-components": "github:appirio-tech/react-components#feature/connectv2", "auth0-js": "^9.6.1", "babel-polyfill": "^6.7.4", + "filestack-js": "^1.13.2", "isomorphic-fetch": "^2.2.1", "lodash": "^4.6.1", "ng-onload": "^0.2.1", @@ -17576,30 +18756,6 @@ } } }, - "appirio-tech-react-components": { - "version": "github:appirio-tech/react-components#3ede60d1876aa38ecaf92fc67a8953b7654cdeff", - "from": "github:appirio-tech/react-components#3ede60d1876aa38ecaf92fc67a8953b7654cdeff", - "requires": { - "appirio-tech-api-schemas": "^5.0.69", - "classnames": "^2.2.3", - "coffee-script": "^1.12.7", - "isomorphic-fetch": "^2.2.1", - "lodash": "^4.0.0", - "moment": "^2.11.2", - "react": "^15.3.1", - "react-dom": "^15.3.1", - "react-redux": "^4.4.5", - "react-router-dom": "^4.2.2", - "react-select": "^0.9.1" - }, - "dependencies": { - "moment": { - "version": "2.24.0", - "resolved": "https://registry.npmjs.org/moment/-/moment-2.24.0.tgz", - "integrity": "sha512-bV7f+6l2QigeBBZSM/6yTNq4P2fNpSWj/0e7jQcy87A8e7o2nAfP/34/2ky5Vw4B9S446EtIhodAzkFCcR4dQg==" - } - } - }, "appirio-tech-webpack-config": { "version": "0.3.20", "resolved": "https://registry.npmjs.org/appirio-tech-webpack-config/-/appirio-tech-webpack-config-0.3.20.tgz", @@ -25368,6 +26524,137 @@ } } }, + "tc-ui": { + "version": "git+https://github.com/appirio-tech/tc-ui.git#e577a0e704136f1e9ecce92ce4c0626aab932691", + "from": "git+https://github.com/appirio-tech/tc-ui.git#feature/connectv2", + "requires": { + "classnames": "^2.2.3", + "lodash": "^4.0.0", + "moment": "^2.11.2", + "node-neat": "~1.7.1-beta1", + "react": "^0.14.7", + "react-datetime": "^2.0.2", + "react-dom": "^0.14.7", + "react-dropzone": "^3.3.2", + "react-redux": "^4.2.1", + "react-router": "^2.0.0-rc6", + "react-select": "^0.9.1", + "redux": "^3.3.1" + }, + "dependencies": { + "history": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/history/-/history-2.1.2.tgz", + "integrity": "sha1-SqLeiXoOSGfkU5hDvm7Nsphr/ew=", + "requires": { + "deep-equal": "^1.0.0", + "invariant": "^2.0.0", + "query-string": "^3.0.0", + "warning": "^2.0.0" + }, + "dependencies": { + "warning": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/warning/-/warning-2.1.0.tgz", + "integrity": "sha1-ISINnGOvx3qMkhEeARr3Bc4MaQE=", + "requires": { + "loose-envify": "^1.0.0" + } + } + } + }, + "hoist-non-react-statics": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.0.tgz", + "integrity": "sha512-0XsbTXxgiaCDYDIWFcwkmerZPSwywfUqYmwT4jzewKTQSWoE6FCMoUVOeBJWK3E/CrWbxRG3m5GzY4lnIwGRBA==", + "requires": { + "react-is": "^16.7.0" + } + }, + "prop-types": { + "version": "15.7.2", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz", + "integrity": "sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==", + "requires": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.8.1" + } + }, + "react": { + "version": "0.14.9", + "resolved": "https://registry.npmjs.org/react/-/react-0.14.9.tgz", + "integrity": "sha1-kRCmSXxJ1EuhwO3TF67CnC4NkdE=", + "requires": { + "envify": "^3.0.0", + "fbjs": "^0.6.1" + } + }, + "react-dom": { + "version": "0.14.9", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-0.14.9.tgz", + "integrity": "sha1-BQZKPc8PsYgKOyv8nVjFXY2fYpM=" + }, + "react-is": { + "version": "16.8.6", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.8.6.tgz", + "integrity": "sha512-aUk3bHfZ2bRSVFFbbeVS4i+lNPZr3/WM5jT2J5omUVV1zzcs1nAaf3l51ctA5FFvCRbhrH0bdAsRRQddFJZPtA==" + }, + "react-redux": { + "version": "4.4.10", + "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-4.4.10.tgz", + "integrity": "sha512-tjL0Bmpkj75Td0k+lXlF8Fc8a9GuXFv/3ahUOCXExWs/jhsKiQeTffdH0j5byejCGCRL4tvGFYlrwBF1X/Aujg==", + "requires": { + "create-react-class": "^15.5.1", + "hoist-non-react-statics": "^3.3.0", + "invariant": "^2.0.0", + "lodash": "^4.17.11", + "loose-envify": "^1.4.0", + "prop-types": "^15.7.2" + } + }, + "react-router": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-2.8.1.tgz", + "integrity": "sha1-c+lJH2zrMW0Pd5gpCBhj43juTtc=", + "requires": { + "history": "^2.1.2", + "hoist-non-react-statics": "^1.2.0", + "invariant": "^2.2.1", + "loose-envify": "^1.2.0", + "warning": "^3.0.0" + }, + "dependencies": { + "hoist-non-react-statics": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-1.2.0.tgz", + "integrity": "sha1-qkSM8JhtVcxAdzsXF0t90GbLfPs=" + } + } + }, + "warning": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/warning/-/warning-3.0.0.tgz", + "integrity": "sha1-MuU3fLVy3kqwR1O9+IIcAe1gW3w=", + "requires": { + "loose-envify": "^1.0.0" + } + } + } + }, + "tcomb": { + "version": "3.2.29", + "resolved": "https://registry.npmjs.org/tcomb/-/tcomb-3.2.29.tgz", + "integrity": "sha512-di2Hd1DB2Zfw6StGv861JoAF5h/uQVu/QJp2g8KVbtfKnoHdBQl5M32YWq6mnSYBQ1vFFrns5B1haWJL7rKaOQ==" + }, + "tcomb-validation": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/tcomb-validation/-/tcomb-validation-3.4.1.tgz", + "integrity": "sha512-urVVMQOma4RXwiVCa2nM2eqrAomHROHvWPuj6UkDGz/eb5kcy0x6P0dVt6kzpUZtYMNoAqJLWmz1BPtxrtjtrA==", + "requires": { + "tcomb": "^3.0.0" + } + }, "temp-path": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/temp-path/-/temp-path-1.0.0.tgz", @@ -25477,6 +26764,11 @@ "typical": "^2.6.1" } }, + "text-encoding": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/text-encoding/-/text-encoding-0.5.2.tgz", + "integrity": "sha1-hbRmCBnwiHd2CUZVUWkP6hN9gko=" + }, "text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", @@ -25492,8 +26784,7 @@ "through": { "version": "2.3.8", "resolved": "http://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", - "dev": true + "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=" }, "through2": { "version": "2.0.3", @@ -25552,6 +26843,15 @@ "setimmediate": "^1.0.4" } }, + "title-case": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/title-case/-/title-case-1.1.2.tgz", + "integrity": "sha1-+uSmrlRr+iLQg6DuqRCkDRLtT1o=", + "requires": { + "sentence-case": "^1.1.1", + "upper-case": "^1.0.3" + } + }, "tmp": { "version": "0.0.33", "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", @@ -25797,8 +27097,7 @@ "tslib": { "version": "1.9.3", "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.3.tgz", - "integrity": "sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ==", - "dev": true + "integrity": "sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ==" }, "tty-browserify": { "version": "0.0.0", @@ -25842,14 +27141,41 @@ "typedarray": { "version": "0.0.6", "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", - "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=", - "dev": true + "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=" }, "typical": { "version": "2.6.1", "resolved": "https://registry.npmjs.org/typical/-/typical-2.6.1.tgz", "integrity": "sha1-XAgOXWYcu+OCWdLnCjxyU+hziB0=" }, + "ua-parser-js": { + "version": "0.7.19", + "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.19.tgz", + "integrity": "sha512-T3PVJ6uz8i0HzPxOF9SWzWAlfN/DavlpQqepn22xgve/5QecC+XMCAtmUNnY7C9StehaV6exjUCI801lOI7QlQ==" + }, + "uglify-js": { + "version": "2.6.4", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.6.4.tgz", + "integrity": "sha1-ZeovswWck5RpLxX+2HwrNsFrmt8=", + "requires": { + "async": "~0.2.6", + "source-map": "~0.5.1", + "uglify-to-browserify": "~1.0.0", + "yargs": "~3.10.0" + }, + "dependencies": { + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" + } + } + }, + "uglify-to-browserify": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz", + "integrity": "sha1-bgkk1r2mta/jSeOabWMoUKD4grc=" + }, "uglifyjs-webpack-plugin": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/uglifyjs-webpack-plugin/-/uglifyjs-webpack-plugin-1.3.0.tgz", @@ -25890,6 +27216,14 @@ } } }, + "uncontrollable": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/uncontrollable/-/uncontrollable-4.1.0.tgz", + "integrity": "sha1-4DWCkSUuGGUiLZCTmxny9J+Bwak=", + "requires": { + "invariant": "^2.1.0" + } + }, "underscore": { "version": "1.8.3", "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.8.3.tgz", @@ -26115,11 +27449,23 @@ "integrity": "sha512-bzpH/oBhoS/QI/YtbkqCg6VEiPYjSZtrHQM6/QnJS6OL9pKUFLqb3aFh4Scvwm45+7iAgiMkLhSbaZxUqmrprw==", "dev": true }, + "upper-case": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/upper-case/-/upper-case-1.1.3.tgz", + "integrity": "sha1-9rRQHC7EzdJrp4vnIilh3ndiFZg=" + }, + "upper-case-first": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/upper-case-first/-/upper-case-first-1.1.2.tgz", + "integrity": "sha1-XXm+3P8UQZUY/S7bCgUHybaFkRU=", + "requires": { + "upper-case": "^1.1.1" + } + }, "uri-js": { "version": "4.2.2", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", - "dev": true, "requires": { "punycode": "^2.1.0" } @@ -26169,11 +27515,18 @@ "integrity": "sha1-K1viOjK2Onyd640PKNSFcko98ZA=", "dev": true }, + "util": { + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/util/-/util-0.11.1.tgz", + "integrity": "sha512-HShAsny+zS2TZfaXxD9tYj4HQGlBezXZMZuM/S5PKLLoZkShZiGk9o5CzukI1LVHZvjdvZ2Sj1aW/Ndn2NB/HQ==", + "requires": { + "inherits": "2.0.3" + } + }, "util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", - "dev": true + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" }, "util.promisify": { "version": "1.0.0", @@ -26308,6 +27661,14 @@ "makeerror": "1.0.x" } }, + "warning": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/warning/-/warning-2.1.0.tgz", + "integrity": "sha1-ISINnGOvx3qMkhEeARr3Bc4MaQE=", + "requires": { + "loose-envify": "^1.0.0" + } + }, "watch": { "version": "0.18.0", "resolved": "https://registry.npmjs.org/watch/-/watch-0.18.0.tgz", @@ -27404,11 +28765,15 @@ "resolved": "http://registry.npmjs.org/winchan/-/winchan-0.1.4.tgz", "integrity": "sha1-iPoSQRzVQutiYBjDihlry7F5k7s=" }, + "window-size": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.0.tgz", + "integrity": "sha1-VDjNLqk7IC76Ohn+iIeu58lPnJ0=" + }, "wordwrap": { "version": "0.0.2", "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz", - "integrity": "sha1-t5Zpu0LstAn4PVg8rVLKF+qhZD8=", - "dev": true + "integrity": "sha1-t5Zpu0LstAn4PVg8rVLKF+qhZD8=" }, "wordwrapjs": { "version": "3.0.0", @@ -27463,8 +28828,7 @@ "wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", - "dev": true + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" }, "write": { "version": "0.2.1", @@ -27506,12 +28870,31 @@ "resolved": "https://registry.npmjs.org/x-xss-protection/-/x-xss-protection-1.1.0.tgz", "integrity": "sha512-rx3GzJlgEeZ08MIcDsU2vY2B1QEriUKJTSiNHHUIem6eg9pzVOr2TL3Y4Pd6TMAM5D5azGjcxqI62piITBDHVg==" }, + "xml-char-classes": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/xml-char-classes/-/xml-char-classes-1.0.0.tgz", + "integrity": "sha1-ZGV4SKIP/F31g6Qq2KJ3tFErvE0=" + }, "xml-name-validator": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-3.0.0.tgz", "integrity": "sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==", "dev": true }, + "xml2js": { + "version": "0.4.19", + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.19.tgz", + "integrity": "sha512-esZnJZJOiJR9wWKMyuvSE1y6Dq5LCuJanqhxslH2bxM6duahNZ+HMpCLhBQGZkbX6xRf8x1Y2eJlgt2q3qo49Q==", + "requires": { + "sax": ">=0.6.0", + "xmlbuilder": "~9.0.1" + } + }, + "xmlbuilder": { + "version": "9.0.7", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-9.0.7.tgz", + "integrity": "sha1-Ey7mPS7FVlxVfiD0wi35rKaGsQ0=" + }, "xmlcreate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/xmlcreate/-/xmlcreate-1.0.2.tgz", @@ -27544,6 +28927,17 @@ "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=", "dev": true }, + "yargs": { + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz", + "integrity": "sha1-9+572FfdfB0tOMDnTvvWgdFDH9E=", + "requires": { + "camelcase": "^1.0.2", + "cliui": "^2.1.0", + "decamelize": "^1.0.0", + "window-size": "0.1.0" + } + }, "yargs-parser": { "version": "9.0.2", "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-9.0.2.tgz", diff --git a/package.json b/package.json index d65f4707..b8be6639 100644 --- a/package.json +++ b/package.json @@ -31,7 +31,7 @@ "lint:js": "./node_modules/.bin/eslint --ext .js,.jsx .", "test": "npm run lint && npm run jest" }, - "version": "0.7.12", + "version": "0.7.13", "dependencies": { "auth0-js": "^6.8.4", "isomorphic-fetch": "^2.2.1", From 89780586ac052c463c0fae973d005a0f2f1bbe7a Mon Sep 17 00:00:00 2001 From: suppermancool Date: Fri, 3 May 2019 23:56:34 +0700 Subject: [PATCH 07/15] code 30090056 code 30090056 --- src/actions/challenge-listing.js | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/actions/challenge-listing.js b/src/actions/challenge-listing.js index ad872794..6011c2c9 100644 --- a/src/actions/challenge-listing.js +++ b/src/actions/challenge-listing.js @@ -191,10 +191,9 @@ function getActiveChallengesDone( user = decodeToken(tokenV3).handle; // Handle any errors on this endpoint so that the non-user specific challenges // will still be loaded. - calls.push(service.getUserChallenges(user, filter, { - limit: PAGE_SIZE, - offset: page * PAGE_SIZE, - }).catch(() => ({ challenges: [] }))); + calls.push(getAll( + params => service.getUserChallenges(user, filter, params).catch(() => ({ challenges: [] })), + )); } return Promise.all(calls).then(([ch, uch]) => { /* uch array contains challenges where the user is participating in From e95c91b693a7e1b90e6cba62bcfc56d8ad34930a Mon Sep 17 00:00:00 2001 From: suppermancool Date: Sat, 4 May 2019 00:00:24 +0700 Subject: [PATCH 08/15] update nap shot update nap shot --- __tests__/__snapshots__/index.js.snap | 3 +++ 1 file changed, 3 insertions(+) diff --git a/__tests__/__snapshots__/index.js.snap b/__tests__/__snapshots__/index.js.snap index e7ee51bc..0386661f 100644 --- a/__tests__/__snapshots__/index.js.snap +++ b/__tests__/__snapshots__/index.js.snap @@ -426,14 +426,17 @@ Object { "countReset": [Function], "debug": [Function], "dir": [Function], + "dirxml": [Function], "error": [Function], "group": [Function], "groupCollapsed": [Function], "groupEnd": [Function], "info": [Function], "log": [Function], + "table": [Function], "time": [Function], "timeEnd": [Function], + "timeLog": [Function], "trace": [Function], "warn": [Function], }, From c5828ccd2758438578783bd5d8b7eeee4fb1cc7b Mon Sep 17 00:00:00 2001 From: suppermancool Date: Sat, 4 May 2019 09:47:41 +0700 Subject: [PATCH 09/15] code 30090056 code 30090056 --- src/actions/challenge-listing.js | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/src/actions/challenge-listing.js b/src/actions/challenge-listing.js index 6011c2c9..2e9c0d08 100644 --- a/src/actions/challenge-listing.js +++ b/src/actions/challenge-listing.js @@ -191,31 +191,33 @@ function getActiveChallengesDone( user = decodeToken(tokenV3).handle; // Handle any errors on this endpoint so that the non-user specific challenges // will still be loaded. - calls.push(getAll( - params => service.getUserChallenges(user, filter, params).catch(() => ({ challenges: [] })), - )); + calls.push(service.getUserChallenges(user, filter, { + limit: PAGE_SIZE, + offset: page * PAGE_SIZE, + }).catch(() => ({ challenges: [] }))); } return Promise.all(calls).then(([ch, uch]) => { + let fullCH = ch; /* uch array contains challenges where the user is participating in * some role. The same challenge are already listed in res array, but they * are not attributed to the user there. This block of code marks user * challenges in an efficient way. */ if (uch) { const map = {}; - uch.challenges.forEach((item) => { map[item.id] = item; }); - ch.challenges.forEach((item) => { - if (map[item.id]) { - /* It is fine to reassing, as the array we modifying is created just - * above within the same function. */ - /* eslint-disable no-param-reassign */ - item.users[user] = true; - item.userDetails = map[item.id].userDetails; - /* eslint-enable no-param-reassign */ - } + uch.challenges.forEach((item) => { + map[item.id] = item; + /* eslint-disable no-param-reassign */ + item.users[user] = true; + item.userDetails = map[item.id].userDetails; + /* eslint-enable no-param-reassign */ }); } - let { challenges, meta } = ch; + if (uch) { + fullCH = uch; + } + let { challenges } = fullCH; + let { meta } = ch; // filter by date range and re-compute meta // we can safely remove the next two lines when backend support date range challenges = filterUtil.filterByDate(challenges, frontFilter); From b382b4a071c9d1513135630cedfb8eb34408d282 Mon Sep 17 00:00:00 2001 From: Vigneshkumar Chinnachamy M Date: Thu, 9 May 2019 00:29:09 +0530 Subject: [PATCH 10/15] update email preferences to use v5 api --- .../actions/__snapshots__/profile.js.snap | 1 + .../reducers/__snapshots__/profile.js.snap | 12 ++++---- __tests__/reducers/profile.js | 2 +- config/test.js | 1 + src/actions/profile.js | 2 +- src/reducers/profile.js | 4 +-- src/services/user.js | 28 +++++++++---------- 7 files changed, 26 insertions(+), 24 deletions(-) diff --git a/__tests__/actions/__snapshots__/profile.js.snap b/__tests__/actions/__snapshots__/profile.js.snap index b784f238..7cfcbef7 100644 --- a/__tests__/actions/__snapshots__/profile.js.snap +++ b/__tests__/actions/__snapshots__/profile.js.snap @@ -166,6 +166,7 @@ Object { }, }, "handle": "tcscoder", + "preferences": Object {}, }, "type": "PROFILE/SAVE_EMAIL_PREFERENCES_DONE", } diff --git a/__tests__/reducers/__snapshots__/profile.js.snap b/__tests__/reducers/__snapshots__/profile.js.snap index a76358ad..da9208ec 100644 --- a/__tests__/reducers/__snapshots__/profile.js.snap +++ b/__tests__/reducers/__snapshots__/profile.js.snap @@ -740,7 +740,7 @@ Object { "deletingPhoto": false, "deletingWebLink": false, "emailPreferences": Object { - "TOPCODER_NL_DATA": true, + "Dev Newsletter": true, }, "externalLinks": Array [ Object { @@ -939,7 +939,7 @@ Object { "deletingPhoto": false, "deletingWebLink": false, "emailPreferences": Object { - "TOPCODER_NL_DATA": true, + "Dev Newsletter": true, }, "externalLinks": Array [ Object { @@ -989,7 +989,7 @@ Object { "deletingPhoto": false, "deletingWebLink": false, "emailPreferences": Object { - "TOPCODER_NL_DATA": true, + "Dev Newsletter": true, }, "externalLinks": Array [ Object { @@ -1926,7 +1926,7 @@ Object { "deletingPhoto": false, "deletingWebLink": false, "emailPreferences": Object { - "TOPCODER_NL_DATA": true, + "Dev Newsletter": true, }, "externalLinks": Array [ Object { @@ -2125,7 +2125,7 @@ Object { "deletingPhoto": false, "deletingWebLink": false, "emailPreferences": Object { - "TOPCODER_NL_DATA": true, + "Dev Newsletter": true, }, "externalLinks": Array [ Object { @@ -2175,7 +2175,7 @@ Object { "deletingPhoto": false, "deletingWebLink": false, "emailPreferences": Object { - "TOPCODER_NL_DATA": true, + "Dev Newsletter": true, }, "externalLinks": Array [ Object { diff --git a/__tests__/reducers/profile.js b/__tests__/reducers/profile.js index 0992cf13..3e9c5ea8 100644 --- a/__tests__/reducers/profile.js +++ b/__tests__/reducers/profile.js @@ -33,7 +33,7 @@ const mockActions = { deleteWebLinkInit: mockAction('DELETE_WEB_LINK_INIT'), deleteWebLinkDone: mockAction('DELETE_WEB_LINK_DONE', { handle, data: webLink }), saveEmailPreferencesInit: mockAction('SAVE_EMAIL_PREFERENCES_INIT'), - saveEmailPreferencesDone: mockAction('SAVE_EMAIL_PREFERENCES_DONE', { handle, data: { subscriptions: { TOPCODER_NL_DATA: true } } }), + saveEmailPreferencesDone: mockAction('SAVE_EMAIL_PREFERENCES_DONE', { handle, preferences: { 'Dev Newsletter': true } }), linkExternalAccountInit: mockAction('LINK_EXTERNAL_ACCOUNT_INIT'), linkExternalAccountDone: mockAction('LINK_EXTERNAL_ACCOUNT_DONE', { handle, data: linkedAccount2 }), unlinkExternalAccountInit: mockAction('UNLINK_EXTERNAL_ACCOUNT_INIT'), diff --git a/config/test.js b/config/test.js index 30d81af5..919351db 100644 --- a/config/test.js +++ b/config/test.js @@ -2,6 +2,7 @@ module.exports = { API: { V2: 'https://api.topcoder-dev.com/v2', V3: 'https://api.topcoder-dev.com/v3', + V5: 'https://api.topcoder-dev.com/v5', }, dummyConfigKey: 'Dummy config value', }; diff --git a/src/actions/profile.js b/src/actions/profile.js index 21643c99..dd1a88c0 100644 --- a/src/actions/profile.js +++ b/src/actions/profile.js @@ -397,7 +397,7 @@ function saveEmailPreferencesInit() {} function saveEmailPreferencesDone(profile, tokenV3, preferences) { const service = getUserService(tokenV3); return service.saveEmailPreferences(profile, preferences) - .then(res => ({ data: res, handle: profile.handle })); + .then(res => ({ data: res, handle: profile.handle, preferences })); } /** diff --git a/src/reducers/profile.js b/src/reducers/profile.js index 8ca03fdf..1cfe741e 100644 --- a/src/reducers/profile.js +++ b/src/reducers/profile.js @@ -434,13 +434,13 @@ function onSaveEmailPreferencesDone(state, { payload, error }) { return newState; } - if (newState.profileForHandle !== payload.handle || !payload.data) { + if (newState.profileForHandle !== payload.handle) { return newState; } return { ...newState, - emailPreferences: payload.data.subscriptions, + emailPreferences: payload.preferences, }; } diff --git a/src/services/user.js b/src/services/user.js index 802f5af3..2fa7c0f6 100644 --- a/src/services/user.js +++ b/src/services/user.js @@ -114,6 +114,7 @@ class User { this.private = { api: getApi('V3', tokenV3), apiV2: getApi('V2', tokenV2), + apiV5: getApi('V5', tokenV3), tokenV2, tokenV3, }; @@ -174,10 +175,10 @@ class User { * @returns {Promise} Resolves to the email preferences result */ async getEmailPreferences(userId) { - const url = `/users/${userId}/preferences/email`; - const res = await this.private.api.get(url); - const x = (await res.json()).result; - return x.content; + const url = `/users/${userId}/preferences`; + const res = await this.private.apiV5.get(url); + const x = (await res.json()); + return x.email; } /** @@ -193,18 +194,17 @@ class User { const settings = { firstName, lastName, - subscriptions: {}, + createdBy: String(userId), + updatedBy: String(userId), + subscriptions: preferences, }; - if (!preferences) { - settings.subscriptions.TOPCODER_NL_GEN = true; - } else { - settings.subscriptions = preferences; - } - const url = `/users/${userId}/preferences/email`; - - const res = await this.private.api.putJson(url, { param: settings }); - return getApiResponsePayload(res); + const url = `/users/${userId}/preferences`; + const res = await this.private.apiV5.putJson( + url, + { email: settings, objectId: String(userId) }, + ); + return res; } /** From a78ae4d552ae0cacc910e9f814f2bdb586aa4c6a Mon Sep 17 00:00:00 2001 From: Thomas Kranitsas Date: Fri, 10 May 2019 12:24:55 +0300 Subject: [PATCH 11/15] Fix tests --- __tests__/__snapshots__/index.js.snap | 3 --- 1 file changed, 3 deletions(-) diff --git a/__tests__/__snapshots__/index.js.snap b/__tests__/__snapshots__/index.js.snap index 0386661f..e7ee51bc 100644 --- a/__tests__/__snapshots__/index.js.snap +++ b/__tests__/__snapshots__/index.js.snap @@ -426,17 +426,14 @@ Object { "countReset": [Function], "debug": [Function], "dir": [Function], - "dirxml": [Function], "error": [Function], "group": [Function], "groupCollapsed": [Function], "groupEnd": [Function], "info": [Function], "log": [Function], - "table": [Function], "time": [Function], "timeEnd": [Function], - "timeLog": [Function], "trace": [Function], "warn": [Function], }, From dbabfda8dbcab62af5f9de813a4f4fa68d89ab67 Mon Sep 17 00:00:00 2001 From: Sushil Date: Fri, 10 May 2019 16:13:07 +0530 Subject: [PATCH 12/15] bump npm version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index b8be6639..d0fa7e8c 100644 --- a/package.json +++ b/package.json @@ -31,7 +31,7 @@ "lint:js": "./node_modules/.bin/eslint --ext .js,.jsx .", "test": "npm run lint && npm run jest" }, - "version": "0.7.13", + "version": "0.7.14", "dependencies": { "auth0-js": "^6.8.4", "isomorphic-fetch": "^2.2.1", From ea9c3e35d157de6d7cd430da607896ffc5601568 Mon Sep 17 00:00:00 2001 From: Huan Li Date: Fri, 24 May 2019 11:56:48 +0800 Subject: [PATCH 13/15] Fixed issues of server-side filtering on challenge listings --- src/actions/challenge-listing.js | 30 +++++++++++++++++------------- src/reducers/challenge-listing.js | 23 ++++++++++++++++++----- 2 files changed, 35 insertions(+), 18 deletions(-) diff --git a/src/actions/challenge-listing.js b/src/actions/challenge-listing.js index 2e9c0d08..e60f10f0 100644 --- a/src/actions/challenge-listing.js +++ b/src/actions/challenge-listing.js @@ -150,7 +150,11 @@ function getAllActiveChallengesDone(uuid, tokenV3) { }); } - return { uuid, challenges: ch.challenges }; + return { + uuid, + challenges: ch.challenges, + handle: user, + }; }); } @@ -197,27 +201,27 @@ function getActiveChallengesDone( }).catch(() => ({ challenges: [] }))); } return Promise.all(calls).then(([ch, uch]) => { - let fullCH = ch; + // let fullCH = ch; /* uch array contains challenges where the user is participating in * some role. The same challenge are already listed in res array, but they * are not attributed to the user there. This block of code marks user * challenges in an efficient way. */ if (uch) { const map = {}; - uch.challenges.forEach((item) => { - map[item.id] = item; - /* eslint-disable no-param-reassign */ - item.users[user] = true; - item.userDetails = map[item.id].userDetails; - /* eslint-enable no-param-reassign */ + uch.challenges.forEach((item) => { map[item.id] = item; }); + ch.challenges.forEach((item) => { + if (map[item.id]) { + /* It is fine to reassing, as the array we modifying is created just + * above within the same function. */ + /* eslint-disable no-param-reassign */ + item.users[user] = true; + item.userDetails = map[item.id].userDetails; + /* eslint-enable no-param-reassign */ + } }); } - if (uch) { - fullCH = uch; - } - let { challenges } = fullCH; - let { meta } = ch; + let { challenges, meta } = ch; // filter by date range and re-compute meta // we can safely remove the next two lines when backend support date range challenges = filterUtil.filterByDate(challenges, frontFilter); diff --git a/src/reducers/challenge-listing.js b/src/reducers/challenge-listing.js index b1d87967..10d359c7 100644 --- a/src/reducers/challenge-listing.js +++ b/src/reducers/challenge-listing.js @@ -94,20 +94,33 @@ function onGetAllActiveChallengesDone(state, { error, payload }) { logger.error(payload); return state; } - const { uuid, challenges: loaded } = payload; + const { + uuid, challenges: loaded, handle, + } = payload; if (uuid !== state.loadingActiveChallengesUUID) return state; /* Once all active challenges are fetched from the API, we remove from the * store any active challenges stored there previously, and also any * challenges with IDs matching any challenges loaded now as active. */ const ids = new Set(); loaded.forEach(item => ids.add(item.id)); - const challenges = state.challenges - .filter(item => item.status !== 'ACTIVE' && !ids.has(item.id)) - .concat(loaded); + + const filter = item => !ids.has(item.id) && item.status !== 'ACTIVE'; + // BUCKET.MY + const my = processBucketData( + handle, state.challenges, loaded, BUCKETS.MY, null, null, filter, {}, + ); + // BUCKET.ALL + const all = processBucketData( + handle, state.challenges, loaded, BUCKETS.ALL, null, null, filter, {}, + ); + + const newChallenges = _.cloneDeep(state.challenges); + newChallenges[BUCKETS.ALL] = all; + newChallenges[BUCKETS.MY] = my; return { ...state, - challenges, + challenges: newChallenges, lastUpdateOfActiveChallenges: Date.now(), loadingActiveChallengesUUID: '', }; From 3a520fff5b8aa633c0a2f1d23f21adc83bb7c0f2 Mon Sep 17 00:00:00 2001 From: Sushil Shinde Date: Mon, 27 May 2019 16:20:44 +0530 Subject: [PATCH 14/15] Updating NPM version for prod release to 0.7.15 Updating NPM version for prod release to 0.7.15 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index d0fa7e8c..a8163f0b 100644 --- a/package.json +++ b/package.json @@ -31,7 +31,7 @@ "lint:js": "./node_modules/.bin/eslint --ext .js,.jsx .", "test": "npm run lint && npm run jest" }, - "version": "0.7.14", + "version": "0.7.15", "dependencies": { "auth0-js": "^6.8.4", "isomorphic-fetch": "^2.2.1", From 1c00f798fd0527a195de68b7275d39dc1e2476c5 Mon Sep 17 00:00:00 2001 From: rashmi73 Date: Fri, 7 Jun 2019 02:11:25 +0530 Subject: [PATCH 15/15] issue 2359 fix --- src/actions/members.js | 3 ++- src/services/challenges.js | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/actions/members.js b/src/actions/members.js index 38b25a1c..de57f20e 100644 --- a/src/actions/members.js +++ b/src/actions/members.js @@ -339,7 +339,8 @@ async function getUserMarathonDone( uuid, handle, tokenV3, pageNum, pageSize, refresh, ) { - const filter = { status: 'PAST', isRatedForMM: 'true' }; + const filter = { status: 'COMPLETED', track: 'DATA_SCIENCE', + subTrack: 'MARATHON_MATCH,DEVELOP_MARATHON_MATCH' }; const params = {}; params.orderBy = 'endDate desc'; params.limit = pageSize; diff --git a/src/services/challenges.js b/src/services/challenges.js index 04244726..f9271f75 100644 --- a/src/services/challenges.js +++ b/src/services/challenges.js @@ -461,7 +461,7 @@ class ChallengesService { * @return {Promise} Resolves to the api response. */ getUserMarathonMatches(username, filters, params) { - const endpoint = `/members/${username.toLowerCase()}/mms/`; + const endpoint = `/members/${username.toLowerCase()}/challenges/`; return this.private.getChallenges(endpoint, filters, params); }