diff --git a/components/channel_view/channel_view.jsx b/components/channel_view/channel_view.jsx index 290ee8dab2c1..5c583d551666 100644 --- a/components/channel_view/channel_view.jsx +++ b/components/channel_view/channel_view.jsx @@ -10,6 +10,7 @@ import deferComponentRender from 'components/deferComponentRender'; import ChannelHeader from 'components/channel_header'; import CreatePost from 'components/create_post'; import FileUploadOverlay from 'components/file_upload_overlay'; +import NextStepsView from 'components/next_steps_view'; import PostView from 'components/post_view'; import TutorialView from 'components/tutorial'; import {clearMarks, mark, measure, trackEvent} from 'actions/diagnostics_actions.jsx'; @@ -27,6 +28,7 @@ export default class ChannelView extends React.PureComponent { }).isRequired, }).isRequired, showTutorial: PropTypes.bool.isRequired, + showNextSteps: PropTypes.bool.isRequired, channelIsArchived: PropTypes.bool.isRequired, viewArchivedChannels: PropTypes.bool.isRequired, actions: PropTypes.shape({ @@ -123,6 +125,12 @@ export default class ChannelView extends React.PureComponent { ); } + if (this.props.showNextSteps) { + return ( + + ); + } + let createPost; if (this.props.deactivatedChannel) { createPost = ( diff --git a/components/channel_view/channel_view.test.jsx b/components/channel_view/channel_view.test.jsx index 040da8a36775..21d372eee4cd 100644 --- a/components/channel_view/channel_view.test.jsx +++ b/components/channel_view/channel_view.test.jsx @@ -16,6 +16,7 @@ describe('components/channel_view', () => { params: {}, }, showTutorial: false, + showNextSteps: false, channelIsArchived: false, viewArchivedChannels: false, actions: { diff --git a/components/channel_view/index.js b/components/channel_view/index.js index 8a3d6d153233..b5969d26f08c 100644 --- a/components/channel_view/index.js +++ b/components/channel_view/index.js @@ -57,6 +57,7 @@ function mapStateToProps(state) { channelRolesLoading, deactivatedChannel: channel ? getDeactivatedChannel(state, channel.id) : false, showTutorial: enableTutorial && tutorialStep <= TutorialSteps.INTRO_SCREENS, + showNextSteps: true, channelIsArchived: channel ? channel.delete_at !== 0 : false, viewArchivedChannels, }; diff --git a/components/next_steps_view/__snapshots__/next_steps_view.test.tsx.snap b/components/next_steps_view/__snapshots__/next_steps_view.test.tsx.snap new file mode 100644 index 000000000000..edb632ad8d06 --- /dev/null +++ b/components/next_steps_view/__snapshots__/next_steps_view.test.tsx.snap @@ -0,0 +1,52 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`components/next_steps_view should match snapshot 1`] = ` +
+
+
+

+ +

+

+ +

+
+
+ +
+
+
+
+
+
+
+`; diff --git a/components/next_steps_view/index.ts b/components/next_steps_view/index.ts new file mode 100644 index 000000000000..1271c79bdff2 --- /dev/null +++ b/components/next_steps_view/index.ts @@ -0,0 +1,31 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import {connect} from 'react-redux'; +import {bindActionCreators, Dispatch} from 'redux'; + +import {getLicense} from 'mattermost-redux/selectors/entities/general'; + +import {GlobalState} from 'types/store'; + +import NextStepsView from './next_steps_view'; + +function mapStateToProps(state: GlobalState) { + const license = getLicense(state); + + return { + skuName: license.SkuShortName, + }; +} + +function mapDispatchToProps(dispatch: Dispatch) { + return { + actions: bindActionCreators({ + }, dispatch), + }; +} + +export default connect(mapStateToProps, mapDispatchToProps)(NextStepsView); diff --git a/components/next_steps_view/next_steps_view.scss b/components/next_steps_view/next_steps_view.scss new file mode 100644 index 000000000000..e31466aaa87d --- /dev/null +++ b/components/next_steps_view/next_steps_view.scss @@ -0,0 +1,88 @@ +#app-content.NextStepsView { + background-image: url(../../images/onboarding-bg.svg); + background-repeat: no-repeat; + background-position-y: bottom; + background-position-x: -305px; + background-size: calc(100% + 305px); +} + +.NextStepsView__header { + padding: 32px 40px; + display: flex; +} + +.NextStepsView__header-headerText { + flex-grow: 1; +} + +.NextStepsView__header-headerTopText { + font-weight: 600; + font-size: 32px; + line-height: 40px; + letter-spacing: -0.02em; + margin: 0; +} + +.NextStepsView__header-headerBottomText { + font-size: 16px; + line-height: 24px; + margin-top: 8px; + margin-bottom: 0; +} + +.NextStepsView__header-logo { + margin-top: 8px; +} + +.NextStepsView__body { + display: flex; + height: 100%; +} + +.NextStepsView__body-main { + flex: 3 0 75%; +} + +.NextStepsView__body-graphic { + background-image: url(../../images/getting-started.svg); + background-repeat: no-repeat; + background-size: 120%; + background-position-y: 33%; + flex: 1 0 25%; + z-index: 2; + min-width: 192px; +} + +@media screen and (max-width: 768px) { + #app-content.NextStepsView { + background-size: auto; + } + + .NextStepsView__header { + padding: 24px 28px; + } + + .NextStepsView__header-headerTopText { + font-size: 24px; + line-height: 32px; + } + + .NextStepsView__body-main { + flex: 1 1 100%; + } +} + +@media screen and (max-width: 1020px) { + .NextStepsView__header-logo { + display: none; + } + + .NextStepsView__body-graphic { + flex: 0 0 0%; + min-width: 0; + } + + .NextStepsView__body-main { + min-width: 536px; + } +} \ No newline at end of file diff --git a/components/next_steps_view/next_steps_view.test.tsx b/components/next_steps_view/next_steps_view.test.tsx new file mode 100644 index 000000000000..e0a470f32218 --- /dev/null +++ b/components/next_steps_view/next_steps_view.test.tsx @@ -0,0 +1,21 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import React from 'react'; +import {shallow} from 'enzyme'; + +import NextStepsView from 'components/next_steps_view/next_steps_view'; + +describe('components/next_steps_view', () => { + const baseProps = { + skuName: '', + }; + + test('should match snapshot', () => { + const wrapper = shallow( + , + ); + + expect(wrapper).toMatchSnapshot(); + }); +}); diff --git a/components/next_steps_view/next_steps_view.tsx b/components/next_steps_view/next_steps_view.tsx new file mode 100644 index 000000000000..f2f060496190 --- /dev/null +++ b/components/next_steps_view/next_steps_view.tsx @@ -0,0 +1,75 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import React from 'react'; +import {FormattedMessage} from 'react-intl'; + +import professionalLogo from 'images/cloud-logos/professional.svg'; + +import './next_steps_view.scss'; + +type Props = { + skuName: string; +}; + +export default class NextStepsView extends React.PureComponent { + getBottomText = () => { + // TODO: will be stored in user prefs at a later date + const {isFinished} = {isFinished: false}; + + if (isFinished) { + return ( + + ); + } + + return ( + + ); + } + + getLogo = () => { + // TODO: Switch logos based on edition once we have the other logos + + switch (this.props.skuName) { + default: + return professionalLogo; + } + } + + render() { + return ( +
+
+
+

+ +

+

+ {this.getBottomText()} +

+
+
+ +
+
+
+
+
+
+
+ ); + } +} diff --git a/i18n/en.json b/i18n/en.json index 35c781f643ef..b1488c3d8f8b 100644 --- a/i18n/en.json +++ b/i18n/en.json @@ -3070,6 +3070,9 @@ "navbar.toggle2": "Toggle sidebar", "navbar.viewInfo": "View Info", "navbar.viewPinnedPosts": "View Pinned Posts", + "next_steps_view.allSetToGo": "You're all set to go!", + "next_steps_view.hereAreSomeNextSteps": "Here are some recommended next steps to help you get started", + "next_steps_view.welcomeToMattermost": "Welcome to Mattermost", "no_results.channel_search.subtitle": "Check the spelling or try another search.", "no_results.channel_search.title": "No results for {channelName}", "no_results.flagged_posts.subtitle": "Flagged messages are only visible to you. Use flags to mark messages for follow-up or save something for later by clicking the {icon} to save them here.", diff --git a/images/cloud-logos/professional.svg b/images/cloud-logos/professional.svg new file mode 100644 index 000000000000..1dc55f32f30e --- /dev/null +++ b/images/cloud-logos/professional.svg @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/images/getting-started.svg b/images/getting-started.svg new file mode 100644 index 000000000000..e406c900bd18 --- /dev/null +++ b/images/getting-started.svg @@ -0,0 +1,88 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/images/onboarding-bg.svg b/images/onboarding-bg.svg new file mode 100644 index 000000000000..c6b036cd3fc7 --- /dev/null +++ b/images/onboarding-bg.svg @@ -0,0 +1,143 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/utils/constants.jsx b/utils/constants.jsx index e6134165ac7e..6c0f9de3f563 100644 --- a/utils/constants.jsx +++ b/utils/constants.jsx @@ -98,6 +98,7 @@ export const Preferences = { NAME_NAME_FORMAT: 'name_format', CATEGORY_SYSTEM_NOTICE: 'system_notice', TEAMS_ORDER: 'teams_order', + RECOMMENDED_NEXT_STEPS: 'recommended_next_steps', }; export const ActionTypes = keyMirror({ @@ -360,6 +361,13 @@ export const TutorialSteps = { FINISHED: 999, }; +export const RecommendedNextSteps = { + COMPLETE_PROFILE: 'complete_profile', + TEAM_SETUP: 'team_setup', + INVITE_MEMBERS: 'invite_members', + HIDE: 'hide', +}; + export const PostTypes = { JOIN_LEAVE: 'system_join_leave', JOIN_CHANNEL: 'system_join_channel',