diff --git a/.circleci/config.yml b/.circleci/config.yml
index 0d96a6c9..ab65da7d 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -150,7 +150,7 @@ workflows:
context : org-global
filters: &filters-dev
branches:
- only: ['develop']
+ only: ['develop', 'Oct_2022_Release']
# Production builds are exectuted only on tagged commits to the
# master branch.
diff --git a/config/constants/development.js b/config/constants/development.js
index 2efc33f9..0d3f0d15 100644
--- a/config/constants/development.js
+++ b/config/constants/development.js
@@ -5,7 +5,7 @@ module.exports = {
ACCOUNTS_APP_CONNECTOR_URL: `https://accounts-auth0.${DOMAIN}`,
ACCOUNTS_APP_LOGIN_URL: `https://accounts-auth0.${DOMAIN}`,
COMMUNITY_APP_URL: `https://www.${DOMAIN}`,
- MEMBER_API_URL: `${DEV_API_HOSTNAME}/v4/members`,
+ MEMBER_API_URL: `${DEV_API_HOSTNAME}/v5/members`,
MEMBER_API_V3_URL: `${DEV_API_HOSTNAME}/v3/members`,
CHALLENGE_API_URL: `${DEV_API_HOSTNAME}/v5/challenges`,
CHALLENGE_TIMELINE_TEMPLATES_URL: `${DEV_API_HOSTNAME}/v5/timeline-templates`,
@@ -34,6 +34,7 @@ module.exports = {
DS_TRACK_ID: 'c0f5d461-8219-4c14-878a-c3a3f356466d',
QA_TRACK_ID: '36e6a8d0-7e1e-4608-a673-64279d99c115',
CHALLENGE_TYPE_ID: '927abff4-7af9-4145-8ba1-577c16e64e2e',
+ MARATHON_TYPE_ID: '929bc408-9cf2-4b3e-ba71-adfbf693046c',
SEGMENT_API_KEY: 'QBtLgV8vCiuRX1lDikbMjcoe9aCHkF6n',
CREATE_FORUM_TYPE_IDS: ['927abff4-7af9-4145-8ba1-577c16e64e2e', 'dc876fa4-ef2d-4eee-b701-b555fcc6544c', 'ecd58c69-238f-43a4-a4bb-d172719b9f31'],
FILE_PICKER_API_KEY: process.env.FILE_PICKER_API_KEY,
diff --git a/config/constants/production.js b/config/constants/production.js
index 4040c262..d90f7944 100644
--- a/config/constants/production.js
+++ b/config/constants/production.js
@@ -5,7 +5,7 @@ module.exports = {
ACCOUNTS_APP_CONNECTOR_URL: process.env.ACCOUNTS_APP_CONNECTOR_URL || `https://accounts-auth0.${DOMAIN}`,
ACCOUNTS_APP_LOGIN_URL: `https://accounts-auth0.${DOMAIN}`,
COMMUNITY_APP_URL: `https://www.${DOMAIN}`,
- MEMBER_API_URL: `${PROD_API_HOSTNAME}/v4/members`,
+ MEMBER_API_URL: `${PROD_API_HOSTNAME}/v5/members`,
MEMBER_API_V3_URL: `${PROD_API_HOSTNAME}/v3/members`,
CHALLENGE_API_URL: `${PROD_API_HOSTNAME}/v5/challenges`,
CHALLENGE_TIMELINE_TEMPLATES_URL: `${PROD_API_HOSTNAME}/v5/timeline-templates`,
@@ -34,6 +34,7 @@ module.exports = {
DS_TRACK_ID: 'c0f5d461-8219-4c14-878a-c3a3f356466d',
QA_TRACK_ID: '36e6a8d0-7e1e-4608-a673-64279d99c115',
CHALLENGE_TYPE_ID: '927abff4-7af9-4145-8ba1-577c16e64e2e',
+ MARATHON_TYPE_ID: '929bc408-9cf2-4b3e-ba71-adfbf693046c',
SEGMENT_API_KEY: 'QSQAW5BWmZfLoKFNRgNKaqHvLDLJoGqF',
CREATE_FORUM_TYPE_IDS: ['927abff4-7af9-4145-8ba1-577c16e64e2e', 'dc876fa4-ef2d-4eee-b701-b555fcc6544c', 'ecd58c69-238f-43a4-a4bb-d172719b9f31'],
FILE_PICKER_API_KEY: process.env.FILE_PICKER_API_KEY,
diff --git a/src/components/ChallengeEditor/ChallengeView/index.js b/src/components/ChallengeEditor/ChallengeView/index.js
index f4bed266..31578261 100644
--- a/src/components/ChallengeEditor/ChallengeView/index.js
+++ b/src/components/ChallengeEditor/ChallengeView/index.js
@@ -20,7 +20,12 @@ import AssignedMemberField from '../AssignedMember-Field'
import { getResourceRoleByName } from '../../../util/tc'
import { isBetaMode } from '../../../util/cookie'
import { loadGroupDetails } from '../../../actions/challenges'
-import { REVIEW_TYPES, CONNECT_APP_URL, PHASE_PRODUCT_CHALLENGE_ID_FIELD } from '../../../config/constants'
+import {
+ REVIEW_TYPES,
+ CONNECT_APP_URL,
+ PHASE_PRODUCT_CHALLENGE_ID_FIELD,
+ DS_TRACK_ID
+} from '../../../config/constants'
import PhaseInput from '../../PhaseInput'
const ChallengeView = ({
@@ -91,6 +96,9 @@ const ChallengeView = ({
const showTimeline = false // disables the timeline for time being https://github.com/topcoder-platform/challenge-engine-ui/issues/706
const isTask = _.get(challenge, 'task.isTask', false)
const phases = _.get(challenge, 'phases', [])
+ const isDataScience = challenge.trackId === DS_TRACK_ID
+ const useDashboardData = _.find(challenge.metadata, { name: 'show_data_dashboard' })
+ const useDashboard = useDashboardData ? useDashboardData.value : true
return (
@@ -133,6 +141,13 @@ const ChallengeView = ({
Challenge Name: {challenge.name}
+ {isDataScience && (
+
+
+ Show data dashboard: {useDashboard ? 'Yes' : 'No'}
+
+
+ )}
{isTask &&
}
{
+ types = _.sortBy(types, ['name'])
return (
<>
diff --git a/src/components/ChallengeEditor/index.js b/src/components/ChallengeEditor/index.js
index a64eb95b..84302707 100644
--- a/src/components/ChallengeEditor/index.js
+++ b/src/components/ChallengeEditor/index.js
@@ -20,11 +20,14 @@ import {
MESSAGE,
COMMUNITY_APP_URL,
DES_TRACK_ID,
+ DEV_TRACK_ID,
CHALLENGE_TYPE_ID,
+ MARATHON_TYPE_ID,
REVIEW_TYPES,
MILESTONE_STATUS,
PHASE_PRODUCT_CHALLENGE_ID_FIELD,
- QA_TRACK_ID
+ QA_TRACK_ID,
+ DS_TRACK_ID
} from '../../config/constants'
import { PrimaryButton, OutlineButton } from '../Buttons'
import TrackField from './Track-Field'
@@ -594,6 +597,8 @@ class ChallengeEditor extends Component {
submissionLimit.count = ''
}
existingMetadata.value = JSON.stringify(submissionLimit)
+ } else if (existingMetadata.name === 'show_data_dashboard') {
+ existingMetadata.value = Boolean(value)
} else {
existingMetadata.value = `${value}`
}
@@ -945,17 +950,14 @@ class ChallengeEditor extends Component {
async createNewChallenge () {
if (!this.props.isNew) return
const { metadata, createChallenge, projectDetail } = this.props
- const { showDesignChallengeWarningModel, challenge: { name, trackId, typeId, milestoneId } } = this.state
+ const { challenge: { name, trackId, typeId, milestoneId, challengeType, metadata: challengeMetadata } } = this.state
const { timelineTemplates } = metadata
const isDesignChallenge = trackId === DES_TRACK_ID
+ const isDataScience = trackId === DS_TRACK_ID
const isChallengeType = typeId === CHALLENGE_TYPE_ID
-
- if (!showDesignChallengeWarningModel && isDesignChallenge && isChallengeType) {
- this.setState({
- showDesignChallengeWarningModel: true
- })
- return
- }
+ const isDevChallenge = trackId === DEV_TRACK_ID
+ const isMM = typeId === MARATHON_TYPE_ID
+ const showDashBoard = (isDataScience && isChallengeType) || (isDevChallenge && isMM)
// indicate that creating process has started
this.setState({ isSaving: true })
@@ -967,6 +969,10 @@ class ChallengeEditor extends Component {
const defaultTemplate = avlTemplates && avlTemplates.length > 0 ? avlTemplates[0] : STD_DEV_TIMELINE_TEMPLATE
const isTask = _.find(metadata.challengeTypes, { id: typeId, isTask: true })
const tags = trackId === QA_TRACK_ID ? ['QA'] : []
+ if (challengeType) {
+ tags.push(challengeType)
+ }
+ let timelineTemplateId = defaultTemplate.id
const newChallenge = {
status: 'New',
@@ -979,7 +985,7 @@ class ChallengeEditor extends Component {
reviewType: isTask || isDesignChallenge ? REVIEW_TYPES.INTERNAL : REVIEW_TYPES.COMMUNITY
},
descriptionFormat: 'markdown',
- timelineTemplateId: defaultTemplate.id,
+ timelineTemplateId,
terms: [{ id: DEFAULT_TERM_UUID, roleId: SUBMITTER_ROLE_UUID }],
groups: [],
milestoneId,
@@ -1006,6 +1012,16 @@ class ChallengeEditor extends Component {
newChallenge.discussions = discussions
}
}
+ if (showDashBoard) {
+ if (!newChallenge.metadata) {
+ newChallenge.metadata = []
+ }
+ let useDashboard = _.find(challengeMetadata, { name: 'show_data_dashboard' })
+ if (useDashboard === undefined) {
+ useDashboard = { name: 'show_data_dashboard', value: true }
+ }
+ newChallenge.metadata.push(useDashboard)
+ }
try {
const action = await createChallenge(newChallenge, projectDetail.id)
if (isTask) {
@@ -1544,6 +1560,13 @@ class ChallengeEditor extends Component {
const currentChallengeId = this.getCurrentChallengeId()
const showTimeline = false // disables the timeline for time being https://github.com/topcoder-platform/challenge-engine-ui/issues/706
const copilotResources = metadata.members || challengeResources
+ const isDevChallenge = challenge.trackId === DEV_TRACK_ID
+ const isMM = challenge.typeId === MARATHON_TYPE_ID
+ const isChallengeType = challenge.typeId === CHALLENGE_TYPE_ID
+ const showDashBoard = (challenge.trackId === DS_TRACK_ID && isChallengeType) || (isDevChallenge && isMM)
+ const useDashboardData = _.find(challenge.metadata, { name: 'show_data_dashboard' })
+ const useDashboard = useDashboardData ? useDashboardData.value : true
+
const challengeForm = isNew
? (
@@ -1584,6 +1625,24 @@ class ChallengeEditor extends Component {
+ {
+ showDashBoard && (
+
+
+
+
+
+ this.onUpdateMetadata('show_data_dashboard', e.target.checked)}
+ />
+
+
+ )
+ }
{isTask && (
{!isBillingAccountLoading && !isBillingAccountLoadingFailed && !isBillingAccountExpired && (
- Billing Account: {activeProject.status} Start Date: {billingStartDate} End Date: {billingEndDate}
+ Billing Account: ACTIVE Start Date: {billingStartDate} End Date: {billingEndDate}
)}
{!isBillingAccountLoading && !isBillingAccountLoadingFailed && isBillingAccountExpired && (
diff --git a/src/components/SelectUserAutocomplete/index.js b/src/components/SelectUserAutocomplete/index.js
index 3217718f..7c87f729 100644
--- a/src/components/SelectUserAutocomplete/index.js
+++ b/src/components/SelectUserAutocomplete/index.js
@@ -7,7 +7,7 @@
import React, { useState, useCallback } from 'react'
import PropTypes from 'prop-types'
import Select from '../Select'
-import { suggestProfiles } from '../../services/user'
+import { suggestProfilesV5, fetchProfileV5 } from '../../services/user'
import _ from 'lodash'
import { AUTOCOMPLETE_MIN_LENGTH, AUTOCOMPLETE_DEBOUNCE_TIME_MS } from '../../config/constants'
@@ -27,13 +27,17 @@ export default function SelectUserAutocomplete (props) {
return
}
- suggestProfiles(inputValue).then((suggestions) => {
- const suggestedOptions = suggestions.map((user) => ({
- label: user.handle,
- value: user.userId.toString()
- }))
- setOptions(suggestedOptions)
- })
+ Promise.all([suggestProfilesV5(inputValue), fetchProfileV5(inputValue)]).then(
+ ([suggestions, user]) => {
+ const suggestedOptions = suggestions.map((u) => ({
+ label: u.handle,
+ value: u.userId.toString()
+ }))
+ if (user && !_.find(suggestions, u => u.userId === user.userId)) {
+ suggestedOptions.push({ label: user.handle, value: user.userId.toString() })
+ }
+ setOptions(suggestedOptions)
+ })
}, AUTOCOMPLETE_DEBOUNCE_TIME_MS), []) // debounce, to reduce API calling rate
return (
diff --git a/src/config/constants.js b/src/config/constants.js
index b05d9e10..73da22e9 100644
--- a/src/config/constants.js
+++ b/src/config/constants.js
@@ -17,6 +17,7 @@ export const {
DS_TRACK_ID,
QA_TRACK_ID,
CHALLENGE_TYPE_ID,
+ MARATHON_TYPE_ID,
SEGMENT_API_KEY
} = process.env
export const CREATE_FORUM_TYPE_IDS = typeof process.env.CREATE_FORUM_TYPE_IDS === 'string' ? process.env.CREATE_FORUM_TYPE_IDS.split(',') : process.env.CREATE_FORUM_TYPE_IDS
diff --git a/src/services/user.js b/src/services/user.js
index 8a42a9da..fc7bd888 100644
--- a/src/services/user.js
+++ b/src/services/user.js
@@ -1,6 +1,6 @@
import _ from 'lodash'
import { axiosInstance } from './axiosWithAuth'
-const { MEMBER_API_V3_URL } = process.env
+const { MEMBER_API_URL, MEMBER_API_V3_URL } = process.env
/**
* Api request for fetching user profile
@@ -11,6 +11,16 @@ export async function fetchProfile (handle) {
return _.get(response, 'data.result.content')
}
+/**
+ * Api request for fetching user profile v5
+ * @returns {Promise<*>}
+ */
+export async function fetchProfileV5 (handle) {
+ const response = await axiosInstance.get(`${MEMBER_API_URL}?handle=${handle}`)
+ const data = _.get(response, 'data')
+ return data.length ? data[0] : undefined
+}
+
/**
* Api request for fetching user profile
* @returns {Promise<*>}
@@ -49,3 +59,12 @@ export async function suggestProfiles (partialHandle) {
const response = await axiosInstance.get(`${MEMBER_API_V3_URL}/_suggest/${encodeURIComponent(partialHandle)}`)
return _.get(response, 'data.result.content')
}
+
+/**
+ * Api request for finding (suggesting) users by the part of the handle
+ * @returns {Promise<*>}
+ */
+export async function suggestProfilesV5 (partialHandle) {
+ const response = await axiosInstance.get(`${MEMBER_API_URL}/autocomplete?term=${encodeURIComponent(partialHandle)}`)
+ return _.get(response, 'data')
+}