diff --git a/Composer/cypress/integration/LuisDeploy.spec.ts b/Composer/cypress/integration/LuisDeploy.spec.ts index 867ae8ffa3..1c867ae39c 100644 --- a/Composer/cypress/integration/LuisDeploy.spec.ts +++ b/Composer/cypress/integration/LuisDeploy.spec.ts @@ -14,7 +14,7 @@ context('Luis Deploy', () => { it('can deploy luis success', () => { cy.visitPage('Project settings'); - cy.findByText('LUIS and QnA').click(); + cy.findByText('Development Resources').click(); cy.findAllByTestId('rootLUISAuthoringKey').type('12345678', { delay: 200 }); cy.findAllByTestId('rootLUISRegion').click(); cy.findByText('westus').click(); diff --git a/Composer/packages/client/src/components/ManageLuis/ManageLuis.tsx b/Composer/packages/client/src/components/ManageLuis/ManageLuis.tsx index 2e1183a4c8..9566ae21c0 100644 --- a/Composer/packages/client/src/components/ManageLuis/ManageLuis.tsx +++ b/Composer/packages/client/src/components/ManageLuis/ManageLuis.tsx @@ -53,6 +53,7 @@ export const ManageLuis = (props: ManageLuisProps) => { '1. Using the Azure portal, please create a Language Understanding resource on my behalf.\n2. Once provisioned, securely share the resulting credentials with me as described in the link below.\n\nDetailed instructions:\nhttps://aka.ms/bfcomposerhandoffluis' )} hidden={props.hidden} + learnMore={'https://aka.ms/composer-luis-learnmore'} regions={LUIS_REGIONS} serviceKeyType={'LUIS.Authoring'} serviceName={'LUIS'} diff --git a/Composer/packages/client/src/components/ManageQNA/ManageQNA.tsx b/Composer/packages/client/src/components/ManageQNA/ManageQNA.tsx index bdcb525661..769308d470 100644 --- a/Composer/packages/client/src/components/ManageQNA/ManageQNA.tsx +++ b/Composer/packages/client/src/components/ManageQNA/ManageQNA.tsx @@ -50,6 +50,7 @@ export const ManageQNA = (props: ManageQNAProps) => { '1. Using the Azure portal, please create a QnAMaker resource on my behalf.\n2. Once provisioned, securely share the resulting credentials with me as described in the link below.\n\nDetailed instructions:\nhttps://aka.ms/bfcomposerhandoffqnamaker' )} hidden={props.hidden} + learnMore={'https://aka.ms/composer-addqnamaker-learnmore'} regions={QNA_REGIONS} serviceKeyType={'QnAMaker'} serviceName={'QnA Maker'} diff --git a/Composer/packages/client/src/pages/botProject/AllowedCallers.tsx b/Composer/packages/client/src/pages/botProject/AllowedCallers.tsx index df5fa8e1a2..00c1a1c00d 100644 --- a/Composer/packages/client/src/pages/botProject/AllowedCallers.tsx +++ b/Composer/packages/client/src/pages/botProject/AllowedCallers.tsx @@ -2,8 +2,8 @@ // Licensed under the MIT License. /** @jsx jsx */ -import { css, jsx } from '@emotion/core'; -import React from 'react'; +import { jsx } from '@emotion/core'; +import React, { Fragment } from 'react'; import styled from '@emotion/styled'; import { useRecoilValue } from 'recoil'; import { ActionButton } from 'office-ui-fabric-react/lib/components/Button'; @@ -13,12 +13,12 @@ import { ITextField, TextField } from 'office-ui-fabric-react/lib/components/Tex import cloneDeep from 'lodash/cloneDeep'; import formatMessage from 'format-message'; import { MessageBar, MessageBarType } from 'office-ui-fabric-react/lib/MessageBar'; +import { Link } from 'office-ui-fabric-react/lib/Link'; import { dispatcherState, rootBotProjectIdSelector, settingsState } from '../../recoilModel'; import { mergePropertiesManagedByRootBot } from '../../recoilModel/dispatchers/utils/project'; -import { CollapsableWrapper } from '../../components/CollapsableWrapper'; -import { actionButton, subtitle as defaultSubtitle, title } from './styles'; +import { actionButton, subtext, title } from './styles'; const Input = styled(TextField)({ width: '100%', @@ -54,10 +54,6 @@ const ItemContainer = styled.div({ marginTop: '4px', }); -const subtitle = css` - padding: 8px 0; -`; - type ItemProps = { value: string; onBlur: () => void; @@ -144,10 +140,22 @@ export const AllowedCallers: React.FC = ({ projectId }) => { }, [runtimeSettings?.skills?.allowedCallers, updateAllowedCallers]); return ( - -
- {formatMessage( - 'Skills can be “called” by external bots. Allow other bots to call your skill by adding their App IDs to the list below.' + +
{formatMessage('Allowed Callers')}
+
+ {formatMessage.rich( + 'Skills can be “called” by external bots. Allow other bots to call your skill by adding their App IDs to the list below. Learn more.', + { + a: ({ children }) => ( + + {children} + + ), + } )}
@@ -157,9 +165,6 @@ export const AllowedCallers: React.FC = ({ projectId }) => { ); })} - - {formatMessage('Add caller')} - {!runtimeSettings?.skills?.allowedCallers?.length && ( {formatMessage('This bot cannot be called as a skill since the allowed caller list is empty')} @@ -168,6 +173,6 @@ export const AllowedCallers: React.FC = ({ projectId }) => { {formatMessage('Add new caller')} - +
); }; diff --git a/Composer/packages/client/src/pages/botProject/AppIdAndPassword.tsx b/Composer/packages/client/src/pages/botProject/AppIdAndPassword.tsx index e9fa34e65e..8bb9aa0c2d 100644 --- a/Composer/packages/client/src/pages/botProject/AppIdAndPassword.tsx +++ b/Composer/packages/client/src/pages/botProject/AppIdAndPassword.tsx @@ -2,23 +2,22 @@ // Licensed under the MIT License. /** @jsx jsx */ -import React, { useState, useEffect, useCallback } from 'react'; +import React, { useState, useEffect, useCallback, Fragment } from 'react'; import { jsx, css } from '@emotion/core'; import { useRecoilValue } from 'recoil'; import { TextField } from 'office-ui-fabric-react/lib/TextField'; import { TooltipHost } from 'office-ui-fabric-react/lib/Tooltip'; import { Icon } from 'office-ui-fabric-react/lib/Icon'; import formatMessage from 'format-message'; -import { mergeStyleSets } from '@uifabric/styling'; import { FontSizes } from 'office-ui-fabric-react/lib/Styling'; import { SharedColors } from '@uifabric/fluent-theme'; +import { Link } from 'office-ui-fabric-react/lib/Link'; import { dispatcherState, settingsState } from '../../recoilModel'; -import { CollapsableWrapper } from '../../components/CollapsableWrapper'; import { mergePropertiesManagedByRootBot } from '../../recoilModel/dispatchers/utils/project'; import { rootBotProjectIdSelector } from '../../recoilModel/selectors/project'; -import { title } from './styles'; +import { inputFieldStyles, subtext, title } from './styles'; // -------------------- Styles -------------------- // const labelContainer = css` @@ -50,16 +49,6 @@ const appIdAndPasswordStyle = css` flex-direction: column; `; -const customError = { - root: { - selectors: { - 'p > span': { - width: '100%', - }, - }, - }, -}; - // -------------------- AppIdAndPassword -------------------- // type AppIdAndPasswordProps = { @@ -114,14 +103,27 @@ export const AppIdAndPassword: React.FC = (props) => { }, [projectId, mergedSettings, localMicrosoftAppId]); return ( - + +
{formatMessage('Microsoft App ID')}
+
+ {formatMessage.rich( + 'A Microsoft App ID is required for your local Azure resources. If you’ve created an App ID already, you can add here. If not, your App ID and secret will be created when you provision resources for this bot. Learn more.', + { + a: ({ children }) => ( + + {children} + + ), + } + )} +
= (props) => { data-testid={'MicrosoftPassword'} label={formatMessage('Microsoft App Password')} placeholder={formatMessage('Enter Microsoft App Password')} - styles={mergeStyleSets({ root: { marginTop: 15 } }, customError)} + styles={inputFieldStyles} value={localMicrosoftAppPassword} onBlur={handleAppPasswordOnBlur} onChange={handleAppPasswordOnChange} onRenderLabel={onRenderLabel} />
-
+ ); }; diff --git a/Composer/packages/client/src/pages/botProject/BotLanguage.tsx b/Composer/packages/client/src/pages/botProject/BotLanguage.tsx index 904e645cd6..06f69f38be 100644 --- a/Composer/packages/client/src/pages/botProject/BotLanguage.tsx +++ b/Composer/packages/client/src/pages/botProject/BotLanguage.tsx @@ -12,14 +12,13 @@ import { FontSizes, FontWeights } from 'office-ui-fabric-react/lib/Styling'; import { NeutralColors, SharedColors } from '@uifabric/fluent-theme'; import { dispatcherState, settingsState } from '../../recoilModel'; -import { CollapsableWrapper } from '../../components/CollapsableWrapper'; import { languageListTemplates } from '../../components/MultiLanguage'; import { localeState, showAddLanguageModalState } from '../../recoilModel/atoms'; import { AddLanguageModal } from '../../components/MultiLanguage'; import { mergePropertiesManagedByRootBot } from '../../recoilModel/dispatchers/utils/project'; import { rootBotProjectIdSelector } from '../../recoilModel/selectors/project'; -import { title, subtitle } from './styles'; +import { subtitle } from './styles'; // -------------------- Styles -------------------- // const botLanguageContainerStyle = css` @@ -152,51 +151,49 @@ export const BotLanguage: React.FC = (props) => { return ( - -
-
- {formatMessage( - 'List of languages that bot will be able to understand (User input) and respond to (Bot responses). To make this bot available in other languages, click ‘Manage bot languages’ to create a copy of the default language, and translate the content into the new language.' - )} -
-
- {languageListOptions.map((l) => ( -
- {l.key === defaultLanguage && ( -
- {l.text} - {formatMessage('DEFAULT LANGUAGE')} -
- )} - {l.key !== defaultLanguage && ( -
-
{l.text}
-
- setDefaultLanguage(l.key)} - > - {formatMessage('Set it as default language')} - - deleteLanguages({ languages: [l.key], projectId: projectId })} - > - {formatMessage('Remove')} - -
+
+
+ {formatMessage( + 'List of languages that bot will be able to understand (User input) and respond to (Bot responses). To make this bot available in other languages, click ‘Manage languages’ to create a copy of the default language, and translate the content into the new language.' + )} +
+
+ {languageListOptions.map((l) => ( +
+ {l.key === defaultLanguage && ( +
+ {l.text} + {formatMessage('DEFAULT LANGUAGE')} +
+ )} + {l.key !== defaultLanguage && ( +
+
{l.text}
+
+ setDefaultLanguage(l.key)} + > + {formatMessage('Set it as default language')} + + deleteLanguages({ languages: [l.key], projectId: projectId })} + > + {formatMessage('Remove')} +
- )} -
- ))} -
- addLanguageDialogBegin(projectId, () => {})}> - {formatMessage('Manage bot languages')} - +
+ )} +
+ ))}
- + addLanguageDialogBegin(projectId, () => {})}> + {formatMessage('Manage bot languages')} + +
> = (props) => { - const { projectId = '' } = props; + const { projectId = '', isRootBot = false } = props; const botProjects = useRecoilValue(localBotsDataSelector); const botProject = botProjects.find((b) => b.projectId === projectId); const readme = useRecoilValue(projectReadmeState(projectId)); @@ -40,17 +45,16 @@ export const BotProjectInfo: React.FC -

{formatMessage('Bot Details')}

+

+ {botProject?.name} + {isRootBot && {formatMessage('(root)')}} +

- -
{formatMessage('Bot Name')}
-
{botProject?.name}
-
{formatMessage('File Location')}
{location}
- +
{formatMessage('Read Me')}
{readme && ( diff --git a/Composer/packages/client/src/pages/botProject/BotProjectSettings.tsx b/Composer/packages/client/src/pages/botProject/BotProjectSettings.tsx index 3df36e2426..5782b34626 100644 --- a/Composer/packages/client/src/pages/botProject/BotProjectSettings.tsx +++ b/Composer/packages/client/src/pages/botProject/BotProjectSettings.tsx @@ -146,8 +146,8 @@ const BotProjectSettings: React.FC { return { id: b.projectId, - name: b.name, - ariaLabel: formatMessage('Bot'), + name: `${b.name} ${b.isRootBot ? formatMessage('(root)') : ''}`, + ariaLabel: formatMessage('bot'), url: createBotSettingUrl(rootBotProjectId ?? '', b.projectId), isRootBot: b.isRootBot, }; diff --git a/Composer/packages/client/src/pages/botProject/BotProjectSettingsTableView.tsx b/Composer/packages/client/src/pages/botProject/BotProjectSettingsTableView.tsx deleted file mode 100644 index 81cfcd3976..0000000000 --- a/Composer/packages/client/src/pages/botProject/BotProjectSettingsTableView.tsx +++ /dev/null @@ -1,63 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -/** @jsx jsx */ -import { jsx, css } from '@emotion/core'; -import { useRecoilValue } from 'recoil'; -import React from 'react'; -import { RouteComponentProps } from '@reach/router'; - -import { localBotsDataSelector } from '../../recoilModel/selectors/project'; - -import { BotProjectInfo } from './BotProjectInfo'; -import { SkillHostEndPoint } from './SkillHostEndPoint'; -import { AppIdAndPassword } from './AppIdAndPassword'; -import { ExternalService } from './ExternalService'; -import { BotLanguage } from './BotLanguage'; -import { RuntimeSettings } from './RuntimeSettings'; -import { PublishTargets } from './PublishTargets'; -import AdapterSection from './adapters/AdapterSection'; -import { AllowedCallers } from './AllowedCallers'; - -// -------------------- Styles -------------------- // - -const container = css` - display: flex; - flex-direction: column; - max-width: 1000px; - height: 100%; -`; - -const publishTargetsWrap = (isLastComponent) => css` - margin-bottom: ${isLastComponent ? '120px' : 0}; -`; - -// -------------------- BotProjectSettingsTableView -------------------- // - -export const BotProjectSettingsTableView: React.FC> = (props) => { - const { projectId = '', scrollToSectionId = '' } = props; - const botProjects = useRecoilValue(localBotsDataSelector); - const botProject = botProjects.find((b) => b.projectId === projectId); - const isRootBot = !!botProject?.isRootBot; - - return ( -
- - {isRootBot && } - - - - {isRootBot && } - -
- -
- -
- ); -}; - -export default BotProjectSettingsTableView; diff --git a/Composer/packages/client/src/pages/botProject/BotProjectsSettingsTabView.tsx b/Composer/packages/client/src/pages/botProject/BotProjectsSettingsTabView.tsx index dfa52326ad..862208fcd8 100644 --- a/Composer/packages/client/src/pages/botProject/BotProjectsSettingsTabView.tsx +++ b/Composer/packages/client/src/pages/botProject/BotProjectsSettingsTabView.tsx @@ -16,13 +16,14 @@ import { DisableFeatureToolTip } from '../../components/DisableFeatureToolTip'; import { usePVACheck } from '../../hooks/usePVACheck'; import { navigateTo } from '../../utils/navigation'; -import { BotSkillConfiguration } from './BotSkillConfiguration'; import { BotProjectInfo } from './BotProjectInfo'; import { AppIdAndPassword } from './AppIdAndPassword'; import { ExternalService } from './ExternalService'; import { BotLanguage } from './BotLanguage'; import { RuntimeSettings } from './RuntimeSettings'; import AdapterSection from './adapters/AdapterSection'; +import { SkillHostEndPoint } from './SkillHostEndPoint'; +import { AllowedCallers } from './AllowedCallers'; // -------------------- Styles -------------------- // @@ -58,7 +59,7 @@ enum PivotItemKey { Language = 'Language', } -// -------------------- BotProjectSettingsTableView -------------------- // +// -------------------- BotProjectSettingsTabView -------------------- // export const BotProjectSettingsTabView: React.FC - - - + + { if (isPVABot) { - return {formatMessage('LUIS and QnA')}; + return ( + + {formatMessage('Development Resources')} + + ); } else { - return {formatMessage('LUIS and QnA')}; + return {formatMessage('Development Resources')}; } }} > + - { - if (isPVABot) { - return {formatMessage('Connections')}; - } else { - return {formatMessage('Connections')}; - } - }} - > - {isRootBot && } - + {isRootBot && ( + { + if (isPVABot) { + return ( + {formatMessage('Connections')} + ); + } else { + return {formatMessage('Connections')}; + } + }} + > + + + )} - {isRootBot && } + {isRootBot && } + - + diff --git a/Composer/packages/client/src/pages/botProject/BotSkillConfiguration.tsx b/Composer/packages/client/src/pages/botProject/BotSkillConfiguration.tsx deleted file mode 100644 index 4128437e6f..0000000000 --- a/Composer/packages/client/src/pages/botProject/BotSkillConfiguration.tsx +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -import React from 'react'; - -import { AllowedCallers } from './AllowedCallers'; -import { SkillHostEndPoint } from './SkillHostEndPoint'; - -type Props = { - projectId: string; -}; - -export const BotSkillConfiguration: React.FC = ({ projectId }) => { - return ( - - - - - ); -}; diff --git a/Composer/packages/client/src/pages/botProject/RootBotExternalService.tsx b/Composer/packages/client/src/pages/botProject/RootBotExternalService.tsx index 3ba9791068..5c2d1c367d 100644 --- a/Composer/packages/client/src/pages/botProject/RootBotExternalService.tsx +++ b/Composer/packages/client/src/pages/botProject/RootBotExternalService.tsx @@ -2,9 +2,8 @@ // Licensed under the MIT License. /** @jsx jsx */ -import React, { useState, useEffect, useRef } from 'react'; +import React, { useState, useEffect, useRef, Fragment } from 'react'; import { jsx, keyframes } from '@emotion/core'; -import { mergeStyleSets } from '@uifabric/styling'; import { BotIndexer } from '@bfc/indexers'; import { useRecoilValue } from 'recoil'; import { TextField } from 'office-ui-fabric-react/lib/TextField'; @@ -17,6 +16,7 @@ import { css } from '@emotion/core'; import { FontSizes } from 'office-ui-fabric-react/lib/Styling'; import { NeutralColors, SharedColors } from '@uifabric/fluent-theme'; import { PrimaryButton } from 'office-ui-fabric-react/lib/Button'; +import { Link } from 'office-ui-fabric-react/lib/Link'; import { dispatcherState, @@ -28,13 +28,12 @@ import { } from '../../recoilModel'; import settingStorage from '../../utils/dialogSettingStorage'; import { rootBotProjectIdSelector } from '../../recoilModel/selectors/project'; -import { CollapsableWrapper } from '../../components/CollapsableWrapper'; import { mergePropertiesManagedByRootBot } from '../../recoilModel/dispatchers/utils/project'; import { LUIS_REGIONS } from '../../constants'; import { ManageLuis } from '../../components/ManageLuis/ManageLuis'; import { ManageQNA } from '../../components/ManageQNA/ManageQNA'; -import { title } from './styles'; +import { inputFieldStyles, subtext, title } from './styles'; // -------------------- Styles -------------------- // @@ -76,16 +75,6 @@ const errorContainer = css` color: ${NeutralColors.black}; `; -const customError = { - root: { - selectors: { - 'p > span': { - width: '100%', - }, - }, - }, -}; - const errorIcon = { root: { color: '#A80000', @@ -341,7 +330,29 @@ export const RootBotExternalService: React.FC = (pr }; return ( - + +
{formatMessage('Language Understanding')}
+
+ {formatMessage.rich( + 'Language Understanding Intelligent Service (LUIS) is a machine learning-driven recognition service that enables advanced conversational capabilities. If you already have LUIS keys you’d like to use, you can paste them below. To fetch existing keys from Azure or create new keys, you can click “Get LUIS keys”. Learn more.', + { + a: ({ children }) => ( + + {children} + + ), + a2: ({ children }) => ( + + {children} + + ), + } + )} +
= (pr id={'luisName'} label={formatMessage('LUIS application name')} placeholder={formatMessage('Enter LUIS application name')} + styles={inputFieldStyles} value={localRootLuisName} onBlur={handleRootLUISNameOnBlur} onChange={handleRootLUISNameOnChange} @@ -363,7 +375,7 @@ export const RootBotExternalService: React.FC = (pr label={formatMessage('LUIS authoring key')} placeholder={formatMessage('Enter LUIS authoring key')} required={isLUISKeyNeeded} - styles={mergeStyleSets({ root: { marginTop: 10 } }, customError)} + styles={inputFieldStyles} value={localRootLuisKey} onBlur={handleRootLuisAuthoringKeyOnBlur} onChange={handleRootLUISKeyOnChange} @@ -380,7 +392,7 @@ export const RootBotExternalService: React.FC = (pr placeholder={formatMessage('Enter LUIS region')} required={isLUISKeyNeeded} selectedKey={localRootLuisRegion} - styles={mergeStyleSets({ root: { marginTop: 10 } }, customError)} + styles={inputFieldStyles} onBlur={handleRootLuisRegionOnBlur} onChange={handleRootLuisRegionOnChange} onRenderLabel={onRenderLabel} @@ -400,6 +412,28 @@ export const RootBotExternalService: React.FC = (pr setDisplayManageLuis(true); }} /> +
{formatMessage('QnA Maker')}
+
+ {formatMessage.rich( + 'Integrate with QnA Maker to provide bot content from easy-to-manage knowledge bases. If you already have a QnA key you’d like to use, you can paste it below. To fetch an existing key from Azure or create a new key, you can click “Get QnA key”. Learn more.', + { + a: ({ children }) => ( + + {children} + + ), + a2: ({ children }) => ( + + {children} + + ), + } + )} +
= (pr label={formatMessage('QnA Maker Subscription key')} placeholder={formatMessage('Enter QnA Maker Subscription key')} required={isQnAKeyNeeded} - styles={mergeStyleSets({ root: { marginTop: 10 } }, customError)} + styles={inputFieldStyles} value={localRootQnAKey} onBlur={handleRootQnAKeyOnBlur} onChange={handleRootQnAKeyOnChange} @@ -447,6 +481,6 @@ export const RootBotExternalService: React.FC = (pr onToggleVisibility={setDisplayManageQNA} />
- + ); }; diff --git a/Composer/packages/client/src/pages/botProject/RuntimeSettings.tsx b/Composer/packages/client/src/pages/botProject/RuntimeSettings.tsx index 754afd326e..50445114cb 100644 --- a/Composer/packages/client/src/pages/botProject/RuntimeSettings.tsx +++ b/Composer/packages/client/src/pages/botProject/RuntimeSettings.tsx @@ -2,12 +2,10 @@ // Licensed under the MIT License. /** @jsx jsx */ -import React, { useEffect, useRef } from 'react'; +import React, { Fragment, useEffect, useRef } from 'react'; import { jsx } from '@emotion/core'; import formatMessage from 'format-message'; -import { CollapsableWrapper } from '../../components/CollapsableWrapper'; - import { title } from './styles'; import { RuntimeSettings as Runtime } from './runtime-settings/RuntimeSettings'; @@ -30,10 +28,11 @@ export const RuntimeSettings: React.FC = (props) => { }, [scrollToSectionId]); return ( - + +
{formatMessage('Custom runtime')}
-
+ ); }; diff --git a/Composer/packages/client/src/pages/botProject/SkillBotExternalService.tsx b/Composer/packages/client/src/pages/botProject/SkillBotExternalService.tsx index a0e59d5a00..d55f08a576 100644 --- a/Composer/packages/client/src/pages/botProject/SkillBotExternalService.tsx +++ b/Composer/packages/client/src/pages/botProject/SkillBotExternalService.tsx @@ -2,7 +2,7 @@ // Licensed under the MIT License. /** @jsx jsx */ -import React, { useEffect, useState, useRef } from 'react'; +import React, { useEffect, useState, useRef, Fragment } from 'react'; import { jsx } from '@emotion/core'; import { BotIndexer } from '@bfc/indexers'; import { useRecoilValue } from 'recoil'; @@ -10,6 +10,7 @@ import formatMessage from 'format-message'; import get from 'lodash/get'; import { css } from '@emotion/core'; import { TextField } from 'office-ui-fabric-react/lib/TextField'; +import { Link } from 'office-ui-fabric-react/lib/Link'; import { dispatcherState, @@ -21,12 +22,11 @@ import { } from '../../recoilModel'; import settingStorage from '../../utils/dialogSettingStorage'; import { rootBotProjectIdSelector } from '../../recoilModel/selectors/project'; -import { CollapsableWrapper } from '../../components/CollapsableWrapper'; import { FieldWithCustomButton } from '../../components/FieldWithCustomButton'; import { mergePropertiesManagedByRootBot } from '../../recoilModel/dispatchers/utils/project'; import { LUIS_REGIONS } from '../../constants'; -import { title } from './styles'; +import { subtext, title } from './styles'; // -------------------- Styles -------------------- // const externalServiceContainerStyle = css` @@ -138,7 +138,25 @@ export const SkillBotExternalService: React.FC = ( }; return ( - + +
{formatMessage('Language Understanding')}
+
+ {formatMessage.rich( + 'Language Understanding Intelligent Service (LUIS) is a machine learning-driven recognition service that enables advanced conversational capabilities. If you already have LUIS keys you’d like to use, you can paste them below. To fetch existing keys from Azure or create new keys, you can click “Get LUIS keys”. Learn more.', + { + a: ({ children }) => ( + + {children} + + ), + a2: ({ children }) => ( + + {children} + + ), + } + )} +
= ( onBlur={handleLUISRegionOnBlur} />
+
{formatMessage('QnA Maker')}
+
+ {formatMessage.rich( + 'Integrate with QnA Maker to provide bot content from easy-to-manage knowledge bases. If you already have a QnA key you’d like to use, you can paste it below. To fetch an existing key from Azure or create a new key, you can click “Get QnA key”. Learn more.', + { + a: ({ children }) => ( + + {children} + + ), + a2: ({ children }) => ( + + {children} + + ), + } + )} +
= ( />
-
+
); }; diff --git a/Composer/packages/client/src/pages/botProject/SkillHostEndPoint.tsx b/Composer/packages/client/src/pages/botProject/SkillHostEndPoint.tsx index af003a01dc..b04d85d223 100644 --- a/Composer/packages/client/src/pages/botProject/SkillHostEndPoint.tsx +++ b/Composer/packages/client/src/pages/botProject/SkillHostEndPoint.tsx @@ -2,7 +2,7 @@ // Licensed under the MIT License. /** @jsx jsx */ -import React from 'react'; +import React, { Fragment } from 'react'; import { jsx, css } from '@emotion/core'; import { useRecoilValue } from 'recoil'; import { TextField } from 'office-ui-fabric-react/lib/TextField'; @@ -11,13 +11,13 @@ import { Icon } from 'office-ui-fabric-react/lib/Icon'; import formatMessage from 'format-message'; import { FontSizes } from 'office-ui-fabric-react/lib/Styling'; import { SharedColors } from '@uifabric/fluent-theme'; +import { Link } from 'office-ui-fabric-react/lib/components/Link'; import { dispatcherState, settingsState } from '../../recoilModel'; -import { CollapsableWrapper } from '../../components/CollapsableWrapper'; import { rootBotProjectIdSelector } from '../../recoilModel/selectors/project'; import { mergePropertiesManagedByRootBot } from '../../recoilModel/dispatchers/utils/project'; -import { title } from './styles'; +import { subtext, title } from './styles'; // -------------------- Styles -------------------- // const labelContainer = css` @@ -70,7 +70,20 @@ export const SkillHostEndPoint: React.FC = (props) => { const { skillHostEndpoint } = useRecoilValue(settingsState(projectId)); return ( - + +
{formatMessage('Call skills')}
+
+ {formatMessage.rich( + 'Add a skill host endpoint so your skills can reliably connect to your root bot. Learn more.', + { + a: ({ children }) => ( + + {children} + + ), + } + )} +
= (props) => { }} onRenderLabel={onRenderLabel} /> -
+ ); }; diff --git a/Composer/packages/client/src/pages/botProject/adapters/ABSChannels.tsx b/Composer/packages/client/src/pages/botProject/adapters/ABSChannels.tsx index 8ccc6f927b..c70f536a27 100644 --- a/Composer/packages/client/src/pages/botProject/adapters/ABSChannels.tsx +++ b/Composer/packages/client/src/pages/botProject/adapters/ABSChannels.tsx @@ -16,6 +16,8 @@ import { TokenCredentials } from '@azure/ms-rest-js'; import { Spinner } from 'office-ui-fabric-react/lib/Spinner'; import { Stack } from 'office-ui-fabric-react/lib/Stack'; import { OpenConfirmModal } from '@bfc/ui-shared'; +import { Callout } from 'office-ui-fabric-react/lib/Callout'; +import { Text } from 'office-ui-fabric-react/lib/Text'; import TelemetryClient from '../../../telemetry/TelemetryClient'; import { LoadingSpinner } from '../../../components/LoadingSpinner'; @@ -35,7 +37,8 @@ import { errorContainer, errorIcon, errorTextStyle, - columnSizes, + extendedColumnSizes, + teamsCallOutStyles, } from '../styles'; import { TeamsManifestGeneratorModal } from '../../../components/Adapters/TeamsManifestGeneratorModal'; import { ManageSpeech } from '../../../components/ManageSpeech/ManageSpeech'; @@ -92,6 +95,7 @@ export const ABSChannels: React.FC = (props) => { const [errorMessage, setErrorMessage] = useState(undefined); const [showSpeechModal, setShowSpeechModal] = useState(false); const [showTeamsManifestModal, setShowTeamsManifestModal] = useState(false); + const [showTeamsCallOut, setShowTeamsCallOut] = useState(false); const { setApplicationLevelError } = useRecoilValue(dispatcherState); /* Copied from Azure Publishing extension */ const getSubscriptions = async (token: string): Promise> => { @@ -243,7 +247,7 @@ export const ABSChannels: React.FC = (props) => { } await httpClient.put(url, data, { headers: { Authorization: `Bearer ${token}` } }); if (channelId === CHANNELS.TEAMS) { - setShowTeamsManifestModal(true); + setShowTeamsCallOut(true); } // success!! setChannelStatus({ @@ -491,29 +495,32 @@ export const ABSChannels: React.FC = (props) => { )} - {key === CHANNELS.TEAMS && channelStatus?.[key].enabled && !channelStatus?.[key].loading && ( - - { - setShowTeamsManifestModal(true); - }} - > - {formatMessage('Open Manifest')} - - - )}
); const absTableRow = (channel: string, name: string, link: string) => (
-
{name}
-
{absTableToggle(channel)}
-
- - {formatMessage('Learn more')} - +
{name}
+
{absTableToggle(channel)}
+
+ + + + {formatMessage('Learn more')} + + + {channel === CHANNELS.TEAMS && channelStatus?.[channel].enabled && !channelStatus?.[channel].loading && ( + + { + setShowTeamsManifestModal(true); + }} + > + {formatMessage('Open Manifest')} + + + )} +
); @@ -542,6 +549,30 @@ export const ABSChannels: React.FC = (props) => { }} onToggleVisibility={setShowSpeechModal} /> + {showTeamsCallOut && ( + { + setShowTeamsCallOut(false); + }} + > + + {formatMessage('Almost there!')} + + + {formatMessage( + 'Teams requires a few more steps to get your connection up and running. Follow the instructions on our documentation page to learn how.' + )} + + + {formatMessage('See instructions')} + + + )}
= (props) => { {currentResource && channelStatus && (
-
{formatMessage('Name')}
-
{formatMessage('Enabled')}
-
{formatMessage('Documentation')}
+
{formatMessage('Name')}
+
{formatMessage('Enabled')}
+
{formatMessage('Documentation')}
{absTableRow(CHANNELS.TEAMS, formatMessage('MS Teams'), teamsHelpLink)} {absTableRow(CHANNELS.WEBCHAT, formatMessage('Web Chat'), webchatHelpLink)} diff --git a/Composer/packages/client/src/pages/botProject/adapters/AdapterSection.tsx b/Composer/packages/client/src/pages/botProject/adapters/AdapterSection.tsx index 9da64b5627..b2845ee773 100644 --- a/Composer/packages/client/src/pages/botProject/adapters/AdapterSection.tsx +++ b/Composer/packages/client/src/pages/botProject/adapters/AdapterSection.tsx @@ -5,9 +5,10 @@ import { jsx } from '@emotion/core'; import { useEffect, useRef } from 'react'; import formatMessage from 'format-message'; import { Stack } from 'office-ui-fabric-react/lib/Stack'; +import { Link } from 'office-ui-fabric-react/lib/Link'; -import { CollapsableWrapper } from '../../../components/CollapsableWrapper'; -import { title, subtitle } from '../styles'; +import { title, subtitle, subtext } from '../styles'; +import { navigateTo } from '../../../utils/navigation'; import ExternalAdapterSettings from './ExternalAdapterSettings'; import ABSChannels from './ABSChannels'; @@ -27,26 +28,55 @@ const AdapterSection = ({ projectId, scrollToSectionId }: Props) => { return (
- - -
{formatMessage('Azure connections')}
-
- {formatMessage( - 'Connect your bot to Microsoft Teams and WebChat, or enable speech. Connections are added per bot (typically to the root bot, if your project contains multiple bots), as well as per publishing profile. Select a publishing profile to view, add, and enable Azure connections.' - )} -
- -
- -
{formatMessage('External connections')}
-
{formatMessage('Find and install more external services in the package manager.')}
- -
-
+
+ {formatMessage.rich( + 'Expand the reach of your bot by adding connections. Connections are added per bot (typically to the root bot, if your project contains multiple bots), as well as per publishing profile. Select a publishing profile to add and enable connections. Learn more.', + { + a: ({ children }) => ( + + {children} + + ), + } + )} +
+ +
{formatMessage('Azure connections')}
+
+ {formatMessage('Connect your bot to Microsoft Teams and WebChat, or enable DirectLine Speech.')} +
+ +
+ +
{formatMessage('External connections')}
+
+ {formatMessage.rich( + 'Find and install more external services to your bot project in package manager. For further guidance, see documentation for adding external connections.', + { + a: ({ children }) => ( + { + navigateTo(`/bot/${projectId}/plugin/package-manager/package-manager`); + }} + > + {children} + + ), + a2: ({ children }) => ( + + {children} + + ), + } + )} +
+ +
); }; diff --git a/Composer/packages/client/src/pages/botProject/runtime-settings/RuntimeSettings.tsx b/Composer/packages/client/src/pages/botProject/runtime-settings/RuntimeSettings.tsx index e17503b679..965e37bae6 100644 --- a/Composer/packages/client/src/pages/botProject/runtime-settings/RuntimeSettings.tsx +++ b/Composer/packages/client/src/pages/botProject/runtime-settings/RuntimeSettings.tsx @@ -5,7 +5,6 @@ import { jsx } from '@emotion/core'; import { useState, Fragment, useEffect, useMemo } from 'react'; import formatMessage from 'format-message'; -import { mergeStyleSets } from '@uifabric/styling'; import { Toggle } from 'office-ui-fabric-react/lib/Toggle'; import { DefaultButton } from 'office-ui-fabric-react/lib/Button'; import { TextField } from 'office-ui-fabric-react/lib/TextField'; @@ -28,7 +27,7 @@ import { } from '../../../recoilModel'; import { LoadingSpinner } from '../../../components/LoadingSpinner'; import TelemetryClient from '../../../telemetry/TelemetryClient'; -import { subtitle, errorContainer, errorTextStyle, errorIcon, customError } from '../styles'; +import { subtext, errorContainer, errorTextStyle, errorIcon, inputFieldStyles } from '../styles'; import { DisableFeatureToolTip } from '../../../components/DisableFeatureToolTip'; import { usePVACheck } from '../../../hooks/usePVACheck'; @@ -142,8 +141,10 @@ export const RuntimeSettings: React.FC ( -
- {formatMessage('Configure Composer to start your bot using runtime code you can customize and control.')} +
+ {formatMessage( + 'Configure Composer to start your bot using runtime code you can customize and control. Leave the field below blank if you don’t require customization.' + )}
); @@ -243,7 +244,7 @@ export const RuntimeSettings: React.FC handleRuntimeSettingOnBlur('path')} onChange={handleRuntimeSettingOnChange('path')} @@ -270,7 +271,7 @@ export const RuntimeSettings: React.FC handleRuntimeSettingOnBlur('command')} onChange={handleRuntimeSettingOnChange('command')} diff --git a/Composer/packages/client/src/pages/botProject/styles.ts b/Composer/packages/client/src/pages/botProject/styles.ts index 0e061fc146..7b755b4cc6 100644 --- a/Composer/packages/client/src/pages/botProject/styles.ts +++ b/Composer/packages/client/src/pages/botProject/styles.ts @@ -3,13 +3,19 @@ import { css } from '@emotion/core'; import { NeutralColors, SharedColors } from '@uifabric/fluent-theme'; -import { FontSizes, FontWeights } from 'office-ui-fabric-react/lib/Styling'; +import { FontSizes, FontWeights, mergeStyleSets } from 'office-ui-fabric-react/lib/Styling'; export const title = css` - font-size: ${FontSizes.mediumPlus}; + font-size: ${FontSizes.large}; font-weight: ${FontWeights.semibold}; - margin-top: 6px; - margin-bottom: 12px; + margin-top: 20px; + margin-bottom: 5px; +`; + +export const subtext = css` + color: ${NeutralColors.gray130}; + font-size: ${FontSizes.medium}; + padding-bottom: 5px; `; export const subtitle = css` @@ -127,6 +133,7 @@ export const unknownIconStyle = (required: boolean) => { }; export const columnSizes = ['300px', '150px', '150px']; +export const extendedColumnSizes = ['300px', '150px', '300px']; export const actionButton = { root: { @@ -137,3 +144,20 @@ export const actionButton = { marginLeft: 5, }, }; + +export const inputFieldStyles = mergeStyleSets({ root: { marginTop: 10, width: '75%' } }, customError); + +export const teamsCallOutStyles = mergeStyleSets({ + callout: { + width: 320, + padding: '20px 24px', + }, + title: { + marginBottom: 12, + fontWeight: FontWeights.semilight, + }, + link: { + display: 'block', + marginTop: 20, + }, +});