Skip to content

Commit c41bc50

Browse files
authored
Merge pull request #4251 from appirio-tech/dev
[Connect] [Bug-Fixes] Release - 2.15.1
2 parents 961dca7 + 72967d7 commit c41bc50

File tree

16 files changed

+144
-52
lines changed

16 files changed

+144
-52
lines changed

docs/permissions.html

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -257,6 +257,23 @@ <h2 class="anchor-container">
257257
</div>
258258
</div>
259259
</div>
260+
<div class="row border-top">
261+
<div class="col py-2">
262+
<div class="permission-title anchor-container">
263+
<a href="#VIEW_COPILOTS" name="VIEW_COPILOTS" class="anchor"></a>View Copilot Team
264+
</div>
265+
<div class="permission-variable"><small><code>VIEW_COPILOTS</code></small></div>
266+
<div class="text-black-50 small-text">Who should view Copilot Team.</div>
267+
</div>
268+
<div class="col-9 py-2">
269+
<div>
270+
<span class="badge badge-primary" title="Allowed Project Role">copilot</span>
271+
</div>
272+
273+
<div>
274+
</div>
275+
</div>
276+
</div>
260277
<div class="row border-top">
261278
<div class="col py-2">
262279
<div class="permission-title anchor-container">

src/components/RichTextArea/RichTextArea.jsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -396,8 +396,10 @@ class RichTextArea extends React.Component {
396396
const {className, avatarUrl, authorName, titlePlaceholder, contentPlaceholder, editMode, isCreating,
397397
isGettingComment, disableTitle, disableContent, expandedTitlePlaceholder, editingTopic, hasPrivateSwitch, canUploadAttachment, attachments, textAreaOnly } = this.props
398398
const {editorExpanded, editorState, titleValue, oldMDContent, currentMDContent, uploading, isPrivate, isAddLinkOpen, rawFiles, files} = this.state
399+
const emptyStringRegex = /^[\s\n\u200B]*$/g // empty string with space, new line, zero width space character
400+
399401
let canSubmit = (disableTitle || titleValue.trim())
400-
&& (disableContent || editorState.getCurrentContent().hasText())
402+
&& (disableContent || !emptyStringRegex.test(currentMDContent))
401403
if (editMode && canSubmit) {
402404
canSubmit = (!disableTitle && titleValue !== this.props.oldTitle) || (!disableContent && oldMDContent !== currentMDContent)
403405
|| (rawFiles.length > 0 || (attachments && files.length < attachments.length))

src/components/TeamManagement/CopilotManagementDialog.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,7 @@ class ProjectManagementDialog extends React.Component {
139139
</span>
140140
</div>
141141
</div>
142-
{(canManageCopilots || canRemoveCopilots) && <div className="member-remove" onClick={remove}>
142+
{(canManageCopilots || canRemoveCopilots || (currentUser.userId === member.userId)) && <div className="member-remove" onClick={remove}>
143143
{(currentUser.userId === member.userId) ? 'Leave' : 'Remove'}
144144
</div>}
145145
</div>

src/components/TeamManagement/TeamManagement.jsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,7 @@ class TeamManagement extends React.Component {
112112
const topcoderTeamManageAction = hasPermission(PERMISSIONS.MANAGE_TOPCODER_TEAM)
113113
const copilotTeamManageAction = hasPermission(PERMISSIONS.MANAGE_COPILOTS)
114114
const copilotRemoveAction = hasPermission(PERMISSIONS.REMOVE_COPILOTS)
115+
const copilotViewAction = hasPermission(PERMISSIONS.VIEW_COPILOTS)
115116
const canRequestCopilot = hasPermission(PERMISSIONS.REQUEST_COPILOTS)
116117
const canJoinTopcoderTeam = !currentMember && hasPermission(PERMISSIONS.JOIN_TOPCODER_TEAM)
117118

@@ -192,9 +193,9 @@ class TeamManagement extends React.Component {
192193
<div className="projects-team">
193194
<div className="title">
194195
<span styleName="title-text">Copilot</span>
195-
{(copilotTeamManageAction || copilotRemoveAction) &&
196+
{(copilotTeamManageAction || copilotRemoveAction || copilotViewAction) &&
196197
<span className="title-action" onClick={() => onShowCopilotDialog(true)}>
197-
Manage
198+
{(copilotTeamManageAction || copilotRemoveAction) ? 'Manage' : 'View'}
198199
</span>
199200
}
200201
</div>

src/config/permissions.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -243,6 +243,15 @@ export const PERMISSIONS = {
243243
]
244244
},
245245

246+
VIEW_COPILOTS: {
247+
meta: {
248+
group: 'Project Members',
249+
title: 'View Copilot Team',
250+
description: 'Who should view Copilot Team.',
251+
},
252+
projectRoles: [PROJECT_ROLE_COPILOT],
253+
},
254+
246255
MANAGE_TOPCODER_TEAM: {
247256
meta: {
248257
group: 'Project Members',

src/helpers/markdownToState.js

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import {convertFromRaw} from 'draft-js'
22
import sanitizeHtml from 'sanitize-html'
3+
import Alert from 'react-s-alert'
34
const Remarkable = require('remarkable')
45

56
// Block level items, key is Remarkable's key for them, value returned is
@@ -257,6 +258,11 @@ function markdownToState(markdown, options = {}) {
257258
const BlockEntities = Object.assign({}, DefaultBlockEntities, options.blockEntities || {})
258259
const BlockStyles = Object.assign({}, DefaultBlockStyles, options.blockStyles || {})
259260

261+
// when there is no content, add empty paragraph
262+
if (parsedData.length === 0) {
263+
blocks.push(getNewBlock(BlockTypes['paragraph_open']()))
264+
}
265+
260266
parsedData.forEach((item) => {
261267

262268
if (item.type === 'bullet_list_open') {
@@ -347,10 +353,26 @@ function markdownToState(markdown, options = {}) {
347353
}, DefaultBlockType)
348354
}
349355

350-
return convertFromRaw({
351-
entityMap,
352-
blocks,
353-
})
356+
let result
357+
try {
358+
result = convertFromRaw({
359+
entityMap,
360+
blocks,
361+
})
362+
} catch(error) {
363+
// If any error occurs set value to plain text
364+
const plainTextBlock = getNewBlock(BlockTypes['paragraph_open']())
365+
plainTextBlock.text = markdown
366+
367+
result = convertFromRaw({
368+
entityMap: [],
369+
blocks: [plainTextBlock],
370+
})
371+
372+
Alert.warning('Some message could not be rendered properly, please contact Topcoder Support')
373+
}
374+
375+
return result
354376
}
355377

356378
export default markdownToState

src/projects/create/components/ProjectSubmitted.jsx

Lines changed: 13 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -2,48 +2,37 @@ import React from 'react'
22
import PT from 'prop-types'
33

44
require('./ProjectSubmitted.scss')
5-
import {
6-
CONNECT_DOMAIN
7-
} from '../../../config/constants'
8-
95
class ProjectSubmitted extends React.Component {
106
constructor(props) {
117
super(props)
12-
13-
this.copyToClipboard = this.copyToClipboard.bind(this)
148
this.state = {
15-
domain: `${CONNECT_DOMAIN}/`,
169
url: `projects/${props.params.status || props.projectId}`
1710
}
1811
}
1912

20-
copyToClipboard() {
21-
const textField = document.createElement('textarea')
22-
textField.innerText = `${this.state.domain}${this.state.url}`
23-
document.body.appendChild(textField)
24-
textField.select()
25-
document.execCommand('copy')
26-
textField.remove()
27-
}
28-
2913
render() {
3014
return (
3115
<div className="ProjectSubmitted flex column middle center tc-ui">
3216
<div className="container flex column middle center">
3317
<div className="title">Congratulations!</div>
34-
<div className="sub-title">Your project has been submitted</div>
18+
<div className="sub-title">Your project has been created</div>
3519
<div className="content">
36-
A member of our team will be reaching out to you shortly to finalize the scope and build your project plan.
20+
Topcoder will be contacting you soon to discuss your project proposal.
3721
<br />
3822
<br />
39-
Use the link below to share your project with members of your team. You can also access all your Topcoder projects in one place from your Connect project dashboard.
40-
</div>
41-
<div className="project-link-container flex row middle center">
42-
{ `${this.state.domain}${this.state.url}` }
23+
<span>
24+
In the meantime, get a jump on the process by inviting your coworkers to your project and securely share any detailed requirements documents you have inside your project.
25+
</span>
4326
</div>
4427
<div className="button-container flex row middle center">
45-
<a type="button" onClick={this.copyToClipboard} className="copy-link-btn tc-btn tc-btn-sm tc-btn-default flex middle center" disabled={false}>Copy link</a>
46-
<a href={this.state.url} type="button" className="go-to-project-dashboard-btn tc-btn tc-btn-sm tc-btn-primary flex middle center" disabled={false}>Go to project dashboard</a>
28+
<a type="button" href={this.state.url} className="go-to-project-btn tc-btn tc-btn-sm tc-btn-default flex middle center" disabled={false}>
29+
Go to Project
30+
<small>Invite your team members and share requirements</small>
31+
</a>
32+
<a href="projects" type="button" className="go-to-project-dashboard-btn tc-btn tc-btn-sm tc-btn-primary flex middle center" disabled={false}>
33+
All Projects
34+
<small>View all of your projects</small>
35+
</a>
4736
</div>
4837
</div>
4938
</div>

src/projects/create/components/ProjectSubmitted.scss

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,8 @@
3131

3232
.content {
3333
color: $tc-gray-100;
34-
font-size: 15px;
34+
text-align: center;
35+
font-size: 20px;
3536
font-weight: 400;
3637
margin-top: 50px;
3738
line-height: 25px;
@@ -56,13 +57,27 @@
5657
}
5758

5859
.button-container {
59-
margin-top: 30px;
60-
.copy-link-btn {
60+
display: flex;
61+
flex-direction: row;
62+
flex-wrap: wrap;
63+
a {
64+
display: flex;
65+
flex-direction: column;
66+
height: 60px;
67+
padding: 20px 10px;
68+
line-height: 20px;
69+
font-size: 20px;
70+
margin-top: 30px;
71+
small {
72+
font-size: 12px;
73+
}
74+
}
75+
.go-to-project-btn {
6176
margin-left: 10px;
6277
margin-right: 10px;
6378
}
6479
}
6580
}
6681
}
6782
}
68-
83+

src/projects/detail/ProjectDetail.jsx

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ const showCoderBotIfError = (hasError) => {
4242
return branch(
4343
(props) => {
4444
if (props.error.code === 403 && props.error.msg.includes('Copilot')) {
45-
const messageGenerator = `${props.error.msg.replace('Copilot: ', '')}. If things don’t work or you’re sure it is Coder’s fault, send us a note at <a href="[email protected]">[email protected]</a> and we’ll fix it for you.`
45+
const messageGenerator = `${props.error.msg.replace('Copilot: ', '')}. If things don’t work or you’re sure it is Coder’s fault, send us a note at <a href="mailto:[email protected]">[email protected]</a> and we’ll fix it for you.`
4646
component = compose(
4747
withProps({code:403, message: messageGenerator})
4848
)
@@ -52,6 +52,13 @@ const showCoderBotIfError = (hasError) => {
5252
component = compose(
5353
withProps({ code:props.error.code })
5454
)
55+
// also show error if project has `templateId` which points to the Project Template which is not found
56+
} else if (!_.isNil(props.project.templateId) && props.projectTemplate === null) {
57+
component = compose(
58+
withProps({ code: 400, message: `Project Template (id ${props.project.templateId}) for this project is not found. Please, send us a note at <a href="mailto:[email protected]">[email protected]</a> and we’ll fix it for you.` })
59+
)
60+
// mark as `hasError`
61+
return true
5562
}
5663
return hasError(props)
5764
},
@@ -69,7 +76,7 @@ const spinner = spinnerWhileLoading(props =>
6976
// first check that there are no error, before checking project properties
7077
props.error && props.error.type === LOAD_PROJECT_FAILURE || props.error.type === ACCEPT_OR_REFUSE_INVITE_FAILURE ||
7178
// old project or has projectTemplate loaded
72-
((props.project && props.project.version !== 'v3') || props.projectTemplate)
79+
((props.project && props.project.version !== 'v3') || !_.isUndefined(props.projectTemplate))
7380
// has all product templates loaded (earlier it was checking project specific product templates only
7481
// which can be empty when we have empty project plan config for the template)
7582
&& props.allProductTemplates.length > 0
@@ -302,8 +309,8 @@ const mapStateToProps = ({projectState, projectDashboard, loadUser, productsTime
302309
project: projectState.project,
303310
projectNonDirty: projectState.projectNonDirty,
304311
projectTemplate: (templateId && projectTemplates) ? (
305-
getProjectTemplateById(projectTemplates, templateId)
306-
) : null,
312+
getProjectTemplateById(projectTemplates, templateId) || null // `null` means project template is not found
313+
) : undefined, // `undefined` means the list of project templates is not yet loaded
307314
productTemplates: (projectTemplates && productTemplates) ? (
308315
getProjectProductTemplates(
309316
productTemplates,

src/projects/detail/components/ProjectStage.jsx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import { formatNumberWithCommas } from '../../../helpers/format'
1111
import { getPhaseActualData } from '../../../helpers/projectHelper'
1212
import {
1313
PROJECT_ATTACHMENTS_FOLDER,
14+
PHASE_PRODUCT_TEMPLATE_ID,
1415
} from '../../../config/constants'
1516
import { filterNotificationsByPosts, filterReadNotifications, filterNotificationsByCriteria } from '../../../routes/notifications/helpers/notifications'
1617
import { buildPhaseTimelineNotificationsCriteria, buildPhaseSpecifiationNotificationsCriteria } from '../../../routes/notifications/constants/notifications'
@@ -206,6 +207,10 @@ class ProjectStage extends React.Component{
206207
}
207208

208209
const hasAnyNotifications = _.some(_.values(hasNotifications), _.identity)
210+
// we don't want to show Specification tab anymore
211+
// we still show it for old phases created with various Product Templates
212+
// but all new phases created with one new Generic Product Template we don't show it anymore
213+
const isGenericPhase = product.templateId === PHASE_PRODUCT_TEMPLATE_ID
209214

210215
return (
211216
<PhaseCard
@@ -226,6 +231,7 @@ class ProjectStage extends React.Component{
226231
onTabClick={this.onTabClick}
227232
hasTimeline={hasTimeline}
228233
hasNotifications={hasNotifications}
234+
hideSpecTab={!hasPermission(PERMISSIONS.MANAGE_PROJECT_PLAN) || isGenericPhase}
229235
/>
230236

231237
{currentActiveTab === 'timeline' &&

0 commit comments

Comments
 (0)