diff --git a/src/components/AssetsLibrary/LinksGridView.jsx b/src/components/AssetsLibrary/LinksGridView.jsx index f58245a33..25fd4b45d 100644 --- a/src/components/AssetsLibrary/LinksGridView.jsx +++ b/src/components/AssetsLibrary/LinksGridView.jsx @@ -59,6 +59,7 @@ const LinksGridView = ({ renderLink={ renderLink } goBack={goBack} formatModifyDate={formatModifyDate} + isLinkSubFolder />)} {(!subFolderContent) && (
= 0 || linkToDelete >= 0)}, '')}> diff --git a/src/components/AssetsLibrary/SubFolder.jsx b/src/components/AssetsLibrary/SubFolder.jsx index 61f95e92f..e8680cbe4 100644 --- a/src/components/AssetsLibrary/SubFolder.jsx +++ b/src/components/AssetsLibrary/SubFolder.jsx @@ -54,7 +54,7 @@ class SubFolder extends React.Component { } render() { - const { link, renderLink, goBack, formatModifyDate } = this.props + const { link, renderLink, goBack, formatModifyDate, isLinkSubFolder } = this.props const { linkToDelete } = this.state return (
= 0)}, '')}> @@ -88,7 +88,7 @@ class SubFolder extends React.Component { } let iconPath try { - if (this.isURLValid(childLink.title)) { + if (isLinkSubFolder) { //Link Icon here iconPath = require('../../assets/icons/link-12.svg') } else { diff --git a/src/components/TopicCard/TopicCard.jsx b/src/components/TopicCard/TopicCard.jsx index a0a1ad261..aa531cb10 100644 --- a/src/components/TopicCard/TopicCard.jsx +++ b/src/components/TopicCard/TopicCard.jsx @@ -7,12 +7,24 @@ import cn from 'classnames' import UserTooltip from '../User/UserTooltip' import NotificationBellAvatar from './NotificationBellAvatar' +import { + CODER_BOT_USER_FNAME, + CODER_BOT_USER_LNAME, +} from '../../config/constants' +import { isSystemUser } from '../../helpers/tcHelpers' + import FileIcon from '../../assets/icons/file-12.svg' import LinkIcon from '../../assets/icons/link-12.svg' import InvisibleIcon from '../../assets/icons/invisible-12.svg' import styles from './TopicCard.scss' +const SYSTEM_USER = { + firstName: CODER_BOT_USER_FNAME, + lastName: CODER_BOT_USER_LNAME, + photoURL: require('../../assets/images/avatar-coder.svg') +} + /** * The topic card that shows the topic title, number of links, files, etc */ @@ -32,13 +44,15 @@ const TopicCard = ({ const pluralize = (name, num) => `${name}${num > 1 ? 's' : ''}` const lastMessageUserId = last(posts).userId - const lastMessageAuthor = get(allMembers, lastMessageUserId) + const lastMessageAuthor = isSystemUser(lastMessageUserId) ? SYSTEM_USER : get(allMembers, lastMessageUserId) const lastMessageDate = formatDate(lastActivityAt) const numNewMessages = get(notifications, 'length') const newMessagesFromDate = formatDate(get(notifications, '0.date')) const numFiles = sumBy(posts, p => get(p, 'attachments.length', 0)) const numLinks = sumBy(posts, p => get(p, 'links.length', 0)) + const authorUser = isSystemUser(get(author, 'userId')) ? SYSTEM_USER : author + return (
@@ -47,7 +61,7 @@ const TopicCard = ({ {numNewMessages && } {!numNewMessages && author && ( { // TODO: Replace hardcoded values with real data @@ -19,9 +12,6 @@ const UserSummary = ({user}) => { drafts: 7, delivered: 5 } */ - const powerUserRoles = [ROLE_CONNECT_COPILOT, ROLE_CONNECT_MANAGER, ROLE_ADMINISTRATOR, ROLE_CONNECT_ADMIN] - const isCustomer = _.intersection(user.roles, powerUserRoles).length === 0 - const role = isCustomer ? 'Customer' : 'Member' const userName = (user.firstName && user.lastName) && `${user.firstName} ${user.lastName}` const memberSince = moment(user.createdAt).format('MMM YYYY') return ( @@ -38,7 +28,7 @@ const UserSummary = ({user}) => { @{user.handle}
- {role} since {memberSince} + User since {memberSince}
diff --git a/src/helpers/projectHelper.js b/src/helpers/projectHelper.js index 0e30e43ec..87bfc1d40 100644 --- a/src/helpers/projectHelper.js +++ b/src/helpers/projectHelper.js @@ -340,7 +340,7 @@ export function getProjectNavLinks(project, projectId) { // choose set of menu links based on the project version const navLinks = project.version === 'v3' ? [ { label: 'Dashboard', to: `/projects/${projectId}`, Icon: DashboardIcon, iconClassName: 'stroke' }, - { label: 'Messages', to: `/projects/${projectId}/messages`, Icon: MessagesIcon, iconClassName: 'stroke' }, + { label: 'Messages', to: `/projects/${projectId}/messages`, Icon: MessagesIcon, iconClassName: 'stroke', exact: false }, { label: 'Scope', to: `/projects/${projectId}/scope`, Icon: ScopeIcon, iconClassName: 'fill' }, // { label: 'Reports', to: '#', Icon: ReportsIcon }, { label: 'Assets Library', to: `/projects/${projectId}/assets`, Icon: AssetsLibraryIcon, iconClassName: 'stroke' }, diff --git a/src/projects/detail/Messages.scss b/src/projects/detail/Messages.scss index 31c44b2ab..e5e1caaab 100644 --- a/src/projects/detail/Messages.scss +++ b/src/projects/detail/Messages.scss @@ -13,14 +13,14 @@ -ms-flex: $number; flex: $number; } - + // FIXME .messages-container { @include flexBox; max-width: 1110px; - margin: 20px auto; + margin: 20px; height: calc(100% - 80px);// 20px is for bottom margin, 60px for footer - + .left-area { @include flexWidth(1); max-width: 360px; @@ -31,28 +31,27 @@ @include flexWidth(2); margin-left: 4 * $base-unit; overflow-y: auto; - + .messaging-empty-state { margin-bottom: 4 * $base-unit; } - + .new-post-composer { margin-bottom: 4 * $base-unit; background-color: $tc-white; - + .modal-row:hover { background-color: $tc-white; } - + .btn-close { display: none; } } - + .feed-action-card + .feed-action-card { margin-top: 4 * $base-unit; } } } } - \ No newline at end of file diff --git a/src/projects/detail/containers/MessagesContainer.js b/src/projects/detail/containers/MessagesContainer.js index 1267a4d40..86f9d530f 100644 --- a/src/projects/detail/containers/MessagesContainer.js +++ b/src/projects/detail/containers/MessagesContainer.js @@ -3,14 +3,16 @@ import React from 'react' import { Prompt, withRouter } from 'react-router-dom' import { connect } from 'react-redux' import update from 'react-addons-update' +import MediaQuery from 'react-responsive' import MessageList from '../../../components/MessageList/MessageList' import MessagingEmptyState from '../../../components/MessageList/MessagingEmptyState' import MessageDetails from '../../../components/MessageDetails/MessageDetails' import NewPost from '../../../components/Feed/NewPost' import { loadProjectMessages, createProjectTopic, saveProjectTopic, deleteProjectTopic, loadFeedComments, addFeedComment, saveFeedComment, deleteFeedComment, getFeedComment } from '../../actions/projectTopics' import spinnerWhileLoading from '../../../components/LoadingSpinner' -import FullHeightContainer from 'appirio-tech-react-components/components/FullHeightContainer/FullHeightContainer' -import FooterV2 from '../../../components/FooterV2/FooterV2' +import TwoColsLayout from '../../../components/TwoColsLayout' +import Sticky from '../../../components/Sticky' +import ProjectInfoContainer from './ProjectInfoContainer' import { THREAD_MESSAGES_PAGE_SIZE, @@ -19,7 +21,8 @@ import { CODER_BOT_USERID, CODER_BOT_USER_FNAME, CODER_BOT_USER_LNAME, - TC_SYSTEM_USERID + TC_SYSTEM_USERID, + SCREEN_BREAKPOINT_MD, } from '../../../config/constants' const SYSTEM_USER = { @@ -417,9 +420,58 @@ class MessagesView extends React.Component { }) } + /** + * Returns the sidebar content + */ + getSidebarContent() { + const { + location, + currentMemberRole, + project, + phases, + isSuperUser, + isManageUser, + productsTimelines, + phasesTopics, + isProcessing + } = this.props + + const leftArea = ( + + ) + + return ( + + {matches => { + if (matches) { + return {leftArea} + } else { + return leftArea + } + }} + + ) + } + render() { const {threads, isCreateNewMessage, showEmptyState, scrollPosition} = this.state const { currentUser, isCreatingFeed, currentMemberRole, error } = this.props + const leftArea = this.getSidebarContent() const activeThread = threads.filter((item) => item.isActive)[0] const onLeaveMessage = this.onLeave() || '' const renderRightPanel = () => { @@ -460,34 +512,39 @@ class MessagesView extends React.Component { } return ( - - -
-
- - -
-
- { (showEmptyState && !threads.length) && - this.setState({showEmptyState: false})} - /> - } - { renderRightPanel() } + + + {leftArea} + + + + +
+
+ +
+
+ { (showEmptyState && !threads.length) && + this.setState({showEmptyState: false})} + /> + } + { renderRightPanel() } +
-
- + + ) } } diff --git a/src/projects/detail/containers/MessagesTabContainer.jsx b/src/projects/detail/containers/MessagesTabContainer.jsx index d01735da5..b730fc1ff 100644 --- a/src/projects/detail/containers/MessagesTabContainer.jsx +++ b/src/projects/detail/containers/MessagesTabContainer.jsx @@ -188,6 +188,19 @@ class MessagesTabContainer extends React.Component { this.props.createProjectTopic(project.id, newFeed) } + componentWillReceiveProps(nextProps) { + // reset title and content in the state after successful post creation + // so that we treat the post editor not changed, thus when we leave the page we don't get confirmation alert + if (this.props.isCreatingFeed && !nextProps.isCreatingFeed && !nextProps.error) { + this.setState({ + newPost: { + title: '', + content: '' + } + }) + } + } + isChanged() { const { newPost } = this.state const notEmpty = str => str && !!trim(str).length