Skip to content

Commit aceb24f

Browse files
authored
Merge pull request #4286 from CDharmateja/project-defaults
project defaults page
2 parents 1dd46a1 + fa0aa99 commit aceb24f

File tree

12 files changed

+304
-6
lines changed

12 files changed

+304
-6
lines changed

config/constants/dev.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,5 +54,7 @@ module.exports = {
5454

5555
DASHBOARD_FAQ_CONTENT_ID : process.env.DASHBOARD_FAQ_CONTENT_ID,
5656
CONTENTFUL_DELIVERY_KEY : process.env.CONTENTFUL_DELIVERY_KEY,
57-
CONTENTFUL_SPACE_ID : process.env.CONTENTFUL_SPACE_ID
57+
CONTENTFUL_SPACE_ID : process.env.CONTENTFUL_SPACE_ID,
58+
59+
DEFAULT_NDA_UUID: 'e5811a7b-43d1-407a-a064-69e5015b4900'
5860
}

config/constants/master.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,5 +54,7 @@ module.exports = {
5454

5555
DASHBOARD_FAQ_CONTENT_ID : process.env.DASHBOARD_FAQ_CONTENT_ID,
5656
CONTENTFUL_DELIVERY_KEY : process.env.CONTENTFUL_DELIVERY_KEY,
57-
CONTENTFUL_SPACE_ID : process.env.CONTENTFUL_SPACE_ID
57+
CONTENTFUL_SPACE_ID : process.env.CONTENTFUL_SPACE_ID,
58+
59+
DEFAULT_NDA_UUID: 'c41e90e5-4d0e-4811-bd09-38ff72674490'
5860
}

config/constants/qa.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,5 +49,7 @@ module.exports = {
4949
TC_SYSTEM_USERID: process.env.QA_TC_SYSTEM_USERID,
5050
MAINTENANCE_MODE: process.env.QA_MAINTENANCE_MODE,
5151

52-
TC_CDN_URL: process.env.TC_CDN_URL
52+
TC_CDN_URL: process.env.TC_CDN_URL,
53+
54+
DEFAULT_NDA_UUID: 'e5811a7b-43d1-407a-a064-69e5015b4900'
5355
}

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/config/permissions.js

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -770,6 +770,18 @@ export const PERMISSIONS = {
770770
...TOPCODER_ADMINS,
771771
]
772772
},
773+
774+
VIEW_PROJECT_DEFAULTS: {
775+
meta: {
776+
},
777+
projectRoles: [
778+
..._.difference(PROJECT_ALL, PROJECT_ROLE_CUSTOMER)
779+
],
780+
topcoderRoles: [
781+
...TOPCODER_ADMINS,
782+
..._.difference(TOPCODER_ALL, ROLE_TOPCODER_USER)
783+
]
784+
}
773785
}
774786

775787
/**

src/helpers/projectHelper.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import _ from 'lodash'
22
import moment from 'moment'
33
import { findProduct } from '../config/projectWizard'
4+
import { PERMISSIONS } from 'config/permissions'
5+
import { hasPermission } from 'helpers/permissions'
46

57
import {
68
PHASE_STATUS_ACTIVE,
@@ -17,6 +19,7 @@ import MessagesIcon from '../assets/icons/v.2.5/icon-messages.svg'
1719
import ReportsIcon from '../assets/icons/v.2.5/icon-reports.svg'
1820
import AssetsLibraryIcon from '../assets/icons/v.2.5/icon-assets-library.svg'
1921
import FAQIcon from '../assets/icons/faq.svg'
22+
import AccountSecurityIcon from 'assets/icons/v.2.5/icon-account-security.svg'
2023
import InvisibleIcon from '../assets/icons/invisible.svg'
2124

2225
import { formatNumberWithCommas } from './format'
@@ -278,5 +281,12 @@ export function getProjectNavLinks(project, projectId, renderFAQs) {
278281
const faqTab = { label: 'FAQ', to: `/projects/${projectId}/faqs`, Icon: FAQIcon, iconClassName: 'fill' }
279282
navLinks.push(faqTab)
280283
}
284+
285+
const searchParams = new URLSearchParams(window.location.search)
286+
287+
if (searchParams.get('beta') === 'true' && hasPermission(PERMISSIONS.VIEW_PROJECT_DEFAULTS)) {
288+
navLinks.push({ label: 'Project Defaults', to: `/projects/${projectId}/projectDefaults`, Icon: AccountSecurityIcon, iconClassName: 'stroke' })
289+
}
290+
281291
return navLinks
282292
}
Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
import React from 'react'
2+
import {connect} from 'react-redux'
3+
import FormsyForm from 'appirio-tech-react-components/components/Formsy'
4+
const Formsy = FormsyForm.Formsy
5+
import RadioGroup from 'appirio-tech-react-components/components/Formsy/RadioGroup'
6+
import SpecQuestionList from '../SpecQuestionList/SpecQuestionList'
7+
import Accordion from '../Accordion/Accordion'
8+
import { updateProject } from '../../../actions/project'
9+
import { DEFAULT_NDA_UUID } from '../../../../../config/constants'
10+
11+
import './EditProjectDefaultsForm.scss'
12+
13+
class EditProjectDefaultsForm extends React.Component {
14+
constructor(props) {
15+
super(props)
16+
17+
this.state = {
18+
hasNda: false,
19+
enableButton: false,
20+
isLoading: true
21+
}
22+
23+
this.handleChange = this.handleChange.bind(this)
24+
this.handleSubmit = this.handleSubmit.bind(this)
25+
}
26+
27+
componentDidMount() {
28+
const {terms} = this.props.project
29+
if (terms.indexOf(DEFAULT_NDA_UUID) >= 0) {
30+
this.setState({hasNda: true})
31+
}
32+
this.setState({isLoading: false})
33+
}
34+
35+
handleChange({nda}) {
36+
if ((nda === 'yes') !== this.state.hasNda) {
37+
if (this.state.enableButton !== true) {
38+
this.setState({enableButton: true})
39+
}
40+
} else {
41+
if (this.state.enableButton !== false) {
42+
this.setState({enableButton: false})
43+
}
44+
}
45+
}
46+
47+
async handleSubmit() {
48+
const {updateProject} = this.props
49+
const {id, terms} = this.props.project
50+
const newHasNda = !this.state.hasNda
51+
if (newHasNda) {
52+
await updateProject(id, {
53+
terms: [...new Set([...terms, DEFAULT_NDA_UUID])]
54+
}, true)
55+
} else {
56+
const newTerms = [...terms]
57+
if (newTerms.indexOf(DEFAULT_NDA_UUID) >= 0) {
58+
newTerms.splice(newTerms.indexOf(DEFAULT_NDA_UUID), 1)
59+
await updateProject(id, {
60+
terms: newTerms
61+
}, true)
62+
}
63+
}
64+
this.setState({
65+
hasNda: this.props.project.terms.indexOf(DEFAULT_NDA_UUID) >= 0
66+
})
67+
}
68+
69+
render() {
70+
const opts = [
71+
{
72+
value: 'yes',
73+
label: 'Yes'
74+
},
75+
{
76+
value: 'no',
77+
label: 'No'
78+
}
79+
]
80+
81+
if (this.state.isLoading) return null;
82+
83+
return (
84+
<div className="edit-project-defaults-form">
85+
<Formsy.Form
86+
onValidSubmit={this.handleSubmit}
87+
onChange={this.handleChange}
88+
>
89+
<div className="container">
90+
<SpecQuestionList>
91+
<Accordion
92+
title="Enforce Topcoder NDA"
93+
type="radio-group"
94+
options={opts}
95+
>
96+
<SpecQuestionList.Item>
97+
<RadioGroup
98+
name="nda"
99+
options={opts}
100+
value={this.state.hasNda ? 'yes' : 'no'}
101+
/>
102+
</SpecQuestionList.Item>
103+
</Accordion>
104+
</SpecQuestionList>
105+
</div>
106+
<div className="section-footer section-footer-spec">
107+
<button
108+
className="tc-btn tc-btn-primary tc-btn-md"
109+
type="submit"
110+
disabled={!this.state.enableButton}
111+
>
112+
Submit
113+
</button>
114+
</div>
115+
</Formsy.Form>
116+
</div>
117+
)
118+
}
119+
}
120+
121+
const mapDispatchToProps = {
122+
updateProject
123+
}
124+
125+
export default connect(null, mapDispatchToProps)(EditProjectDefaultsForm)
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
@import '~tc-ui/src/styles/tc-includes';
2+
3+
:global {
4+
.edit-project-defaults-form {
5+
background-color: white;
6+
.container {
7+
padding: 50px 0 70px 0;
8+
margin: 0 50px 20px 50px;
9+
}
10+
.radio-group-options {
11+
flex-direction: column;
12+
}
13+
14+
.radio label {
15+
font-size: 15px;
16+
font-weight: 400;
17+
}
18+
19+
.radio-group-options .radio {
20+
margin: 0 0 10px 0;
21+
background-color: $tc-gray-neutral-light;
22+
padding: 10px;
23+
border-radius: 4px;
24+
display: block;
25+
26+
&:first-child {
27+
margin-top: 10px;
28+
}
29+
}
30+
31+
.radio-group-options .radio.selected {
32+
background-color: $tc-dark-blue-10;
33+
}
34+
35+
.radio-group-options .radio-option-price {
36+
float: right;
37+
font-size: 15px;
38+
color: $tc-gray-100;
39+
}
40+
41+
.radio-option-description {
42+
color: $tc-gray-70;
43+
font-size: 13px;
44+
margin-top: 10px;
45+
line-height: 20px;
46+
}
47+
}
48+
}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
import EditProjectDefaults from './EditProjectDefaultsForm'
2+
export default EditProjectDefaults
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
import React, { Component } from 'react'
2+
import MediaQuery from 'react-responsive'
3+
import Sticky from 'react-stickynode'
4+
import { connect } from 'react-redux'
5+
import { withRouter } from 'react-router-dom'
6+
import { SCREEN_BREAKPOINT_MD, PROJECT_FEED_TYPE_PRIMARY, PROJECT_FEED_TYPE_MESSAGES } from '../../../config/constants'
7+
import TwoColsLayout from '../../../components/TwoColsLayout'
8+
import ProjectInfoContainer from './ProjectInfoContainer'
9+
import EditProjectDefaultsForm from '../components/EditProjectDefaultsForm'
10+
import { hasPermission } from '../../../helpers/permissions'
11+
import { PERMISSIONS } from '../../../config/permissions'
12+
13+
class ProjectDefaultsContainer extends Component {
14+
render() {
15+
const {
16+
project,
17+
phases,
18+
isProcessing,
19+
feeds,
20+
isFeedsLoading,
21+
productsTimelines,
22+
phasesTopics,
23+
location,
24+
} = this.props
25+
26+
const leftArea = (
27+
<ProjectInfoContainer
28+
location={location}
29+
project={project}
30+
phases={phases}
31+
feeds={feeds}
32+
isFeedsLoading={isFeedsLoading}
33+
productsTimelines={productsTimelines}
34+
phasesTopics={phasesTopics}
35+
isProjectProcessing={isProcessing}
36+
/>
37+
)
38+
39+
return (
40+
<TwoColsLayout>
41+
<TwoColsLayout.Sidebar>
42+
<MediaQuery minWidth={SCREEN_BREAKPOINT_MD}>
43+
{(matches) => {
44+
if (matches) {
45+
return (
46+
<Sticky top={60} bottomBoundary="#wrapper-main">
47+
{leftArea}
48+
</Sticky>
49+
)
50+
} else {
51+
return leftArea
52+
}
53+
}}
54+
</MediaQuery>
55+
</TwoColsLayout.Sidebar>
56+
<TwoColsLayout.Content>
57+
<EditProjectDefaultsForm project={this.props.project} />
58+
</TwoColsLayout.Content>
59+
</TwoColsLayout>
60+
)
61+
}
62+
}
63+
const mapStateToProps = ({ loadUser, projectState, projectTopics, topics }) => {
64+
let allFeed = projectTopics.feeds[PROJECT_FEED_TYPE_PRIMARY].topics
65+
if (hasPermission(PERMISSIONS.ACCESS_PRIVATE_POST)) {
66+
allFeed = [...allFeed, ...projectTopics.feeds[PROJECT_FEED_TYPE_MESSAGES].topics]
67+
}
68+
69+
return {
70+
user: loadUser.user,
71+
isProcessing: projectState.processing,
72+
phases: projectState.phases,
73+
phasesNonDirty: projectState.phasesNonDirty,
74+
isLoadingPhases: projectState.isLoadingPhases,
75+
feeds: allFeed,
76+
isFeedsLoading: projectTopics.isLoading,
77+
phasesTopics: topics,
78+
}
79+
}
80+
81+
const mapDispatchToProps = {
82+
}
83+
84+
export default connect(mapStateToProps, mapDispatchToProps)(withRouter(ProjectDefaultsContainer))

0 commit comments

Comments
 (0)