From 3789e792b2d666f61f30e131c74b5e96579505b0 Mon Sep 17 00:00:00 2001 From: Justin Gasper Date: Sun, 21 May 2023 08:03:31 +1000 Subject: [PATCH 1/5] Allow for setting phase dates by duration *or* end date --- src/components/DateInput/index.js | 59 ++++++++++++++++++++++++++++++ src/components/PhaseInput/index.js | 44 +++++++++++++++++----- src/util/date.js | 12 ++++++ 3 files changed, 106 insertions(+), 9 deletions(-) create mode 100644 src/components/DateInput/index.js diff --git a/src/components/DateInput/index.js b/src/components/DateInput/index.js new file mode 100644 index 00000000..9ecf1fc8 --- /dev/null +++ b/src/components/DateInput/index.js @@ -0,0 +1,59 @@ +import React, { useEffect, useState, useImperativeHandle, forwardRef } from 'react' +import PropTypes from 'prop-types' +import 'react-day-picker/lib/style.css' +import 'rc-time-picker/assets/index.css' +import DateTime from '@nateradebaugh/react-datetime' +import '@nateradebaugh/react-datetime/scss/styles.scss' + +const DateInput = forwardRef(({ + onChange, + value, + isValidDate, + dateFormat, + timeFormat, + className +}, ref) => { + const [localValue, setLocalValue] = useState(value) + useEffect(() => { + setLocalValue(value) + }, [value]) + + useImperativeHandle(ref, () => ({ + forceReset: () => { + setLocalValue(value) + } + })) + + return ( + { + setLocalValue(newValue) + }} + onBlur={onChange} + isValidDate={isValidDate} + dateFormat={dateFormat} + timeFormat={timeFormat} + /> + ) +}) + +DateInput.defaultProps = { + onChange: () => {}, + isValidDate: () => true, + value: null, + dateFormat: null, + timeFormat: null, + className: null +} + +DateInput.propTypes = { + onChange: PropTypes.func, + isValidDate: PropTypes.func, + value: PropTypes.any, + dateFormat: PropTypes.string, + timeFormat: PropTypes.string, + className: PropTypes.string +} +export default DateInput diff --git a/src/components/PhaseInput/index.js b/src/components/PhaseInput/index.js index b1d883f8..7c362563 100644 --- a/src/components/PhaseInput/index.js +++ b/src/components/PhaseInput/index.js @@ -1,16 +1,13 @@ import moment from 'moment' -import React, { useEffect, useMemo } from 'react' +import React, { useEffect, useMemo, useRef } from 'react' import PropTypes from 'prop-types' import styles from './PhaseInput.module.scss' import cn from 'classnames' -import 'react-day-picker/lib/style.css' -import 'rc-time-picker/assets/index.css' -import DateTime from '@nateradebaugh/react-datetime' import isAfter from 'date-fns/isAfter' import subDays from 'date-fns/subDays' -import '@nateradebaugh/react-datetime/scss/styles.scss' import DurationInput from '../DurationInput' -import { getPhaseHoursMinutes, getPhaseEndDate } from '../../util/date' +import { getPhaseHoursMinutes, getPhaseEndDate, getPhaseDuration } from '../../util/date' +import DateInput from '../DateInput' const dateFormat = 'MM/DD/YYYY HH:mm' const inputDateFormat = 'MM/dd/yyyy' @@ -21,6 +18,7 @@ const PhaseInput = ({ onUpdatePhase, phase, readOnly, phaseIndex }) => { const { scheduledStartDate: startDate, scheduledEndDate: endDate, duration, isStartTimeActive, isDurationActive } = phase const durationHoursMinutes = useMemo(() => getPhaseHoursMinutes(duration), [duration]) + const endDateInputRef = useRef() const onStartDateChange = (e) => { let startDate = moment(e).format(dateFormat) @@ -32,6 +30,20 @@ const PhaseInput = ({ onUpdatePhase, phase, readOnly, phaseIndex }) => { }) } + const onEndDateChange = (e) => { + let endDate = moment(e).format(dateFormat) + let duration = getPhaseDuration(startDate, endDate) + if (duration > 0) { + onUpdatePhase({ + startDate, + endDate, + duration + }) + } else { + endDateInputRef.current.forceReset() + } + } + useEffect(() => { if (!startDate && onUpdatePhase) { let startDate = moment().format(dateFormat) @@ -71,9 +83,9 @@ const PhaseInput = ({ onUpdatePhase, phase, readOnly, phaseIndex }) => { {moment(startDate).format(dateFormat)} ) : ( - { const yesterday = subDays(new Date(), 1) @@ -87,7 +99,21 @@ const PhaseInput = ({ onUpdatePhase, phase, readOnly, phaseIndex }) => {
End Date:
- {moment(endDate).format(dateFormat)} + {(readOnly || !isDurationActive) ? ( + {moment(endDate).format(dateFormat)} + ) : ( + { + return isAfter(current, moment(startDate).toDate()) + }} + dateFormat={inputDateFormat} + timeFormat={inputTimeFormat} + /> + )}
diff --git a/src/util/date.js b/src/util/date.js index 06b4688d..4e39e31d 100644 --- a/src/util/date.js +++ b/src/util/date.js @@ -57,6 +57,18 @@ export const getPhaseEndDate = (startDate, duration) => { return moment(startDate).add(duration, 'minutes').format(dateFormat) } +/** + * Get phase duration + * @param {Date} startDate phase start date + * @param {Date} endDate phase end date + * @returns duration + */ +export const getPhaseDuration = (startDate, endDate) => { + const startDateMoment = moment(startDate).set({ second: 0, millisecond: 0 }) + const endDateMoment = moment(endDate).set({ second: 0, millisecond: 0 }) + return moment.duration(endDateMoment.diff(startDateMoment)).asMinutes() +} + /** * Get phase end date in date * @param {Date} startDate phase start date From c9cfcca38e323441ef990e8648c6c160f8fba235 Mon Sep 17 00:00:00 2001 From: Justin Gasper Date: Mon, 22 May 2023 14:28:10 +1000 Subject: [PATCH 2/5] https://topcoder.atlassian.net/browse/PROD-4266 --- src/assets/images/IconSquareDownload.svg | 3 + .../Submissions/Submissions.module.scss | 46 +++++++++++++--- .../ChallengeEditor/Submissions/index.js | 55 ++++++++++++++++--- 3 files changed, 88 insertions(+), 16 deletions(-) create mode 100644 src/assets/images/IconSquareDownload.svg diff --git a/src/assets/images/IconSquareDownload.svg b/src/assets/images/IconSquareDownload.svg new file mode 100644 index 00000000..e56fda65 --- /dev/null +++ b/src/assets/images/IconSquareDownload.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/components/ChallengeEditor/Submissions/Submissions.module.scss b/src/components/ChallengeEditor/Submissions/Submissions.module.scss index a1166050..0f87b0a8 100644 --- a/src/components/ChallengeEditor/Submissions/Submissions.module.scss +++ b/src/components/ChallengeEditor/Submissions/Submissions.module.scss @@ -287,6 +287,7 @@ $base-unit: 5px; display: flex; border-bottom: 1px solid $tc-gray-10; background: transparent; + gap: 20px; @include xs-to-sm { display: none; @@ -298,6 +299,7 @@ $base-unit: 5px; display: flex; border-bottom: 1px solid $tc-gray-10; font-size: 15px; + gap: 20px; } .col-1 { @@ -317,28 +319,54 @@ $base-unit: 5px; .col-2 { // width: 10%; display: flex; - width: 150px; + width: 50px; margin-left: 20px; justify-content: flex-start; display: flex; + flex-shrink: 0; } .col-3 { - width: 298px; display: flex; - // width: 26.6%; + width: 100px; + flex-shrink: 0; } .col-4 { display: flex; - // width: 26.6%; - width: 317px; + width: 150px; + flex-shrink: 0; } .col-5 { display: flex; - width: 351px; - // width: 26.6%; + width: 150px; + flex-shrink: 0; + } + + .col-6 { + display: flex; + width: 280px; + flex-shrink: 0; + } + + .col-7 { + display: flex; + width: 150px; + flex-shrink: 0; + } + + .col-8 { + display: flex; + width: 50px; + flex-shrink: 0; + + button { + padding: 0; + border: none; + background-color: transparent; + outline: none; + } } .handle { @@ -350,7 +378,9 @@ $base-unit: 5px; .submissionsContainer { display: flex; flex-direction: column; - max-width: 966px; + max-width: 1100px; + width: 100%; + overflow: auto; } .tooltip { diff --git a/src/components/ChallengeEditor/Submissions/index.js b/src/components/ChallengeEditor/Submissions/index.js index b400a752..c23308c7 100644 --- a/src/components/ChallengeEditor/Submissions/index.js +++ b/src/components/ChallengeEditor/Submissions/index.js @@ -27,6 +27,7 @@ import styles from './Submissions.module.scss' const assets = require.context('../../../assets/images', false, /svg/) const ArrowDown = './arrow-down.svg' const Lock = './lock.svg' +const Download = './IconSquareDownload.svg' class SubmissionsComponent extends React.Component { constructor (props) { @@ -211,6 +212,7 @@ class SubmissionsComponent extends React.Component { const revertSort = sort === 'desc' ? 'asc' : 'desc' const { sortedSubmissions, downloadingAll } = this.state + console.log('totest sortedSubmissions', sortedSubmissions) const renderSubmission = s => (
@@ -393,6 +395,21 @@ class SubmissionsComponent extends React.Component {
+
+ Submission ID (UUID) +
+
+ Legacy submission ID +
+
+ Actions +
{sortedSubmissions.map(s => (
+
+ {s.id} +
+
+ {s.legacySubmissionId} +
+
+ +
))}
-
- -
-
Date: Wed, 24 May 2023 11:00:56 +1000 Subject: [PATCH 3/5] Show challenge BA on challenge page, not project BA https://github.com/topcoder-platform/work-manager/issues/1533 https://github.com/topcoder-platform/work-manager/issues/1532 --- .../ChallengeEditor/ChallengeView/index.js | 3 ++- .../ChallengeEditor/Submissions/index.js | 20 +++++++++---------- src/config/constants.js | 8 ++++++++ src/util/tc.js | 12 ++++++++++- 4 files changed, 31 insertions(+), 12 deletions(-) diff --git a/src/components/ChallengeEditor/ChallengeView/index.js b/src/components/ChallengeEditor/ChallengeView/index.js index 39f6b1e8..6becff77 100644 --- a/src/components/ChallengeEditor/ChallengeView/index.js +++ b/src/components/ChallengeEditor/ChallengeView/index.js @@ -49,6 +49,7 @@ const ChallengeView = ({ showRejectChallengeModal, loggedInUser }) => { + const challengeBillingAccount = _.get(challenge, 'billing.billingAccountId') const selectedType = _.find(metadata.challengeTypes, { id: challenge.typeId }) const challengeTrack = _.find(metadata.challengeTracks, { id: challenge.trackId }) const selectedMilestone = challenge.milestoneId @@ -196,7 +197,7 @@ const ChallengeView = ({
Billing Account Id: - {projectDetail.billingAccountId} + {challengeBillingAccount} {isBillingAccountExpired && Expired}
diff --git a/src/components/ChallengeEditor/Submissions/index.js b/src/components/ChallengeEditor/Submissions/index.js index c23308c7..53ce239c 100644 --- a/src/components/ChallengeEditor/Submissions/index.js +++ b/src/components/ChallengeEditor/Submissions/index.js @@ -15,7 +15,8 @@ import { getRatingLevel, sortList, getProvisionalScore, - getFinalScore + getFinalScore, + checkManageRoles } from '../../../util/tc' import { getTopcoderReactLib @@ -207,12 +208,12 @@ class SubmissionsComponent extends React.Component { render () { const { challenge, token } = this.props const { checkpoints, track, type, tags } = challenge + const haveManagePermission = checkManageRoles(token) const { field, sort } = this.getSubmissionsSortParam() const revertSort = sort === 'desc' ? 'asc' : 'desc' const { sortedSubmissions, downloadingAll } = this.state - console.log('totest sortedSubmissions', sortedSubmissions) const renderSubmission = s => (
@@ -308,7 +309,7 @@ class SubmissionsComponent extends React.Component { return (
-
+ {haveManagePermission ? (
) : null}
{!isF2F && !isBugHunt && ( @@ -405,11 +406,11 @@ class SubmissionsComponent extends React.Component { > Legacy submission ID
-
Actions -
+
) : null}
{sortedSubmissions.map(s => (
{s.legacySubmissionId}
-
+ {haveManagePermission ? (
-
+
) : null}
))}
-
+ {haveManagePermission ? (
-
+
) : null}
) } diff --git a/src/config/constants.js b/src/config/constants.js index abc3c06b..e4c6ef1d 100644 --- a/src/config/constants.js +++ b/src/config/constants.js @@ -233,6 +233,14 @@ export const ALLOWED_USER_ROLES = [ 'topcoder user' ] +export const ALLOWED_MANAGE_ROLES = [ + 'copilot', + 'administrator', + 'connect admin', + 'connect manager', + 'connect copilot' +] + export const READ_ONLY_ROLES = [ 'topcoder user' ] diff --git a/src/util/tc.js b/src/util/tc.js index 862271c2..245570ca 100644 --- a/src/util/tc.js +++ b/src/util/tc.js @@ -7,7 +7,8 @@ import { ALLOWED_USER_ROLES, ADMIN_ROLES, SUBMITTER_ROLE_UUID, - READ_ONLY_ROLES + READ_ONLY_ROLES, + ALLOWED_MANAGE_ROLES } from '../config/constants' import _ from 'lodash' import { decodeToken } from 'tc-auth-lib' @@ -171,6 +172,15 @@ export const checkOnlyReadOnlyRoles = token => { return roles.some(val => READ_ONLY_ROLES.indexOf(val.toLowerCase()) > -1) } +/** + * Checks if this role can have manage permission + * @param token + */ +export const checkManageRoles = token => { + const roles = _.get(decodeToken(token), 'roles') + return roles.some(val => ALLOWED_MANAGE_ROLES.indexOf(val.toLowerCase()) > -1) +} + /** * Checks if token has any of the admin roles * @param token From 29beb91ced4cb6673ca8364d7e1d2d5e541776ec Mon Sep 17 00:00:00 2001 From: Gunasekar-K Date: Wed, 24 May 2023 11:50:19 +0530 Subject: [PATCH 4/5] read-only-root-file-system-fix --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 475e1361..27bdcd6a 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -36,7 +36,7 @@ install_test_dependency: &install_test_dependency install_deploysuite: &install_deploysuite name: Installation of install_deploysuite. command: | - git clone --branch v1.4 https://github.com/topcoder-platform/tc-deploy-scripts ../buildscript + git clone --branch v1.4.15 https://github.com/topcoder-platform/tc-deploy-scripts ../buildscript cp ./../buildscript/master_deploy.sh . cp ./../buildscript/buildenv.sh . cp ./../buildscript/awsconfiguration.sh . From 75c7bdd93f2c6f16109bf7f095f3efc3ff9f275a Mon Sep 17 00:00:00 2001 From: Justin Gasper Date: Wed, 24 May 2023 16:53:01 +1000 Subject: [PATCH 5/5] Use legacy submission ID when downloading --- src/components/ChallengeEditor/Submissions/index.js | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/components/ChallengeEditor/Submissions/index.js b/src/components/ChallengeEditor/Submissions/index.js index 53ce239c..b7f20abb 100644 --- a/src/components/ChallengeEditor/Submissions/index.js +++ b/src/components/ChallengeEditor/Submissions/index.js @@ -478,7 +478,7 @@ class SubmissionsComponent extends React.Component { const url = window.URL.createObjectURL(new Blob([blob])) const link = document.createElement('a') link.href = url - link.setAttribute('download', `submission-${s.id}.zip`) + link.setAttribute('download', `${s.legacySubmissionId}.zip`) document.body.appendChild(link) link.click() link.parentNode.removeChild(link) @@ -525,10 +525,9 @@ class SubmissionsComponent extends React.Component { } checkToCompressFiles() _.forEach(sortedSubmissions, (submission) => { - const mmSubmissionId = submission.id - submissionsService.downloadSubmission(mmSubmissionId) + submissionsService.downloadSubmission(submission.id) .then((blob) => { - const file = new window.File([blob], `submission-${mmSubmissionId}.zip`) + const file = new window.File([blob], `${submission.legacySubmissionId}.zip`) allFiles.push(file) downloadedFile += 1 checkToCompressFiles()