diff --git a/Composer/packages/client/src/pages/botProject/CreatePublishProfileDialog.tsx b/Composer/packages/client/src/pages/botProject/CreatePublishProfileDialog.tsx index 7fe06d16e6..eec6cc74ba 100644 --- a/Composer/packages/client/src/pages/botProject/CreatePublishProfileDialog.tsx +++ b/Composer/packages/client/src/pages/botProject/CreatePublishProfileDialog.tsx @@ -2,10 +2,8 @@ // Licensed under the MIT License. /** @jsx jsx */ -import React, { Fragment, useState, useEffect } from 'react'; +import React, { Fragment } from 'react'; import { jsx } from '@emotion/core'; -import { useRecoilValue } from 'recoil'; -import { PublishTarget } from '@bfc/shared'; import formatMessage from 'format-message'; import { ActionButton, DefaultButton } from 'office-ui-fabric-react/lib/Button'; import { useBoolean } from '@uifabric/react-hooks'; @@ -13,17 +11,10 @@ import Dialog, { DialogFooter } from 'office-ui-fabric-react/lib/Dialog'; import { SharedColors } from '@uifabric/fluent-theme'; import { FontWeights } from 'office-ui-fabric-react/lib/Styling'; -import { dispatcherState, settingsState, publishTypesState } from '../../recoilModel'; -import { AuthDialog } from '../../components/Auth/AuthDialog'; -import { isShowAuthDialog } from '../../utils/auth'; - -import { PublishProfileDialog } from './create-publish-profile/PublishProfileDialog'; - // -------------------- CreatePublishProfileDialog -------------------- // type CreatePublishProfileDialogProps = { - projectId: string; - onUpdateIsCreateProfileFromSkill: (isCreateProfileFromSkill: boolean) => void; + onShowPublishProfileWrapperDialog: () => void; }; // -------------------- Style -------------------- // @@ -38,13 +29,8 @@ const actionButton = { }; export const CreatePublishProfileDialog: React.FC = (props) => { - const { projectId, onUpdateIsCreateProfileFromSkill } = props; - const { publishTargets } = useRecoilValue(settingsState(projectId)); - const { getPublishTargetTypes, setPublishTargets } = useRecoilValue(dispatcherState); - const publishTypes = useRecoilValue(publishTypesState(projectId)); + const { onShowPublishProfileWrapperDialog } = props; - const [dialogHidden, setDialogHidden] = useState(true); - const [showAuthDialog, setShowAuthDialog] = useState(false); const [hideDialog, { toggle: toggleHideDialog }] = useBoolean(false); const dialogTitle = { @@ -53,15 +39,6 @@ export const CreatePublishProfileDialog: React.FC( - null - ); - - useEffect(() => { - if (projectId) { - getPublishTargetTypes(projectId); - } - }, [projectId]); return ( @@ -83,8 +60,8 @@ export const CreatePublishProfileDialog: React.FC { - isShowAuthDialog(true) ? setShowAuthDialog(true) : setDialogHidden(false); toggleHideDialog(); + onShowPublishProfileWrapperDialog(); }} > {formatMessage('Create new publish profile')} @@ -94,31 +71,6 @@ export const CreatePublishProfileDialog: React.FC - {showAuthDialog && ( - { - setDialogHidden(false); - }} - onDismiss={() => { - setShowAuthDialog(false); - }} - /> - )} - {!dialogHidden ? ( - { - setDialogHidden(true); - setCurrentPublishProfile(null); - }} - current={currentPublishProfile} - projectId={projectId} - setPublishTargets={setPublishTargets} - targets={publishTargets || []} - types={publishTypes} - onUpdateIsCreateProfileFromSkill={onUpdateIsCreateProfileFromSkill} - /> - ) : null} ); }; diff --git a/Composer/packages/client/src/pages/botProject/PublishProfieWrapperDialog.tsx b/Composer/packages/client/src/pages/botProject/PublishProfieWrapperDialog.tsx new file mode 100644 index 0000000000..bf45576775 --- /dev/null +++ b/Composer/packages/client/src/pages/botProject/PublishProfieWrapperDialog.tsx @@ -0,0 +1,80 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +/** @jsx jsx */ +import React, { Fragment, useState, useEffect } from 'react'; +import { jsx } from '@emotion/core'; +import { useRecoilValue } from 'recoil'; +import { PublishTarget } from '@bfc/shared'; + +import { dispatcherState, settingsState, publishTypesState } from '../../recoilModel'; +import { AuthDialog } from '../../components/Auth/AuthDialog'; +import { isShowAuthDialog } from '../../utils/auth'; + +import { PublishProfileDialog } from './create-publish-profile/PublishProfileDialog'; + +// -------------------- CreatePublishProfileDialog -------------------- // + +type PublishProfileWrapperDialogProps = { + projectId: string; + onClose: () => void; + onOpen: () => void; + onUpdateIsCreateProfileFromSkill: (isCreateProfileFromSkill: boolean) => void; +}; + +export const PublishProfileWrapperDialog: React.FC = (props) => { + const { projectId, onClose, onOpen, onUpdateIsCreateProfileFromSkill } = props; + const { publishTargets } = useRecoilValue(settingsState(projectId)); + const { getPublishTargetTypes, setPublishTargets } = useRecoilValue(dispatcherState); + const publishTypes = useRecoilValue(publishTypesState(projectId)); + + const [showPublishProfileDialog, setShowPublishProfileDialog] = useState(false); + const [showAuthDialog, setShowAuthDialog] = useState(false); + + const [currentPublishProfile, setCurrentPublishProfile] = useState<{ index: number; item: PublishTarget } | null>( + null + ); + + useEffect(() => { + isShowAuthDialog(true) ? setShowAuthDialog(true) : setShowPublishProfileDialog(true); + }); + + useEffect(() => { + if (projectId) { + getPublishTargetTypes(projectId); + } + }, [projectId]); + + return ( + + {showAuthDialog && ( + { + setShowPublishProfileDialog(true); + onOpen(); + }} + onDismiss={() => { + setShowAuthDialog(false); + onClose(); + }} + /> + )} + {showPublishProfileDialog ? ( + { + setShowPublishProfileDialog(false); + setCurrentPublishProfile(null); + onClose(); + }} + current={currentPublishProfile} + projectId={projectId} + setPublishTargets={setPublishTargets} + targets={publishTargets || []} + types={publishTypes} + onUpdateIsCreateProfileFromSkill={onUpdateIsCreateProfileFromSkill} + /> + ) : null} + + ); +}; diff --git a/Composer/packages/client/src/pages/design/exportSkillModal/constants.tsx b/Composer/packages/client/src/pages/design/exportSkillModal/constants.tsx index daeb774f50..e8eb98471d 100644 --- a/Composer/packages/client/src/pages/design/exportSkillModal/constants.tsx +++ b/Composer/packages/client/src/pages/design/exportSkillModal/constants.tsx @@ -192,19 +192,15 @@ export const editorSteps: { [key in ManifestEditorSteps]: EditorStep } = { cancelButton, backButton, { - disabled: ({ publishTargets }) => { + disabled: ({ publishTarget }) => { try { - return ( - publishTargets.findIndex((item) => { - const config = JSON.parse(item.configuration); - return ( - config.settings && - config.settings.MicrosoftAppId && - config.hostname && - config.settings.MicrosoftAppId.length > 0 && - config.hostname.length > 0 - ); - }) < 0 + const config = JSON.parse(publishTarget.configuration); + return !( + config.settings && + config.settings.MicrosoftAppId && + config.hostname && + config.settings.MicrosoftAppId.length > 0 && + config.hostname.length > 0 ); } catch (err) { console.log(err.message); diff --git a/Composer/packages/client/src/pages/design/exportSkillModal/content/SelectProfile.tsx b/Composer/packages/client/src/pages/design/exportSkillModal/content/SelectProfile.tsx index ea1f8b5a65..6df44f3051 100644 --- a/Composer/packages/client/src/pages/design/exportSkillModal/content/SelectProfile.tsx +++ b/Composer/packages/client/src/pages/design/exportSkillModal/content/SelectProfile.tsx @@ -5,17 +5,21 @@ import { css, jsx } from '@emotion/core'; import { PublishTarget, SkillManifestFile } from '@bfc/shared'; import formatMessage from 'format-message'; -import React, { useEffect, useMemo, useState } from 'react'; +import React, { Fragment, useEffect, useMemo, useState } from 'react'; import { useRecoilValue } from 'recoil'; import { TooltipHost } from 'office-ui-fabric-react/lib/Tooltip'; import { Icon } from 'office-ui-fabric-react/lib/Icon'; import { Dropdown, IDropdownOption } from 'office-ui-fabric-react/lib/Dropdown'; import { TextField } from 'office-ui-fabric-react/lib/TextField'; +import { NeutralColors } from '@uifabric/fluent-theme'; +import { MessageBar, MessageBarType } from 'office-ui-fabric-react/lib/MessageBar'; +import { Link } from 'office-ui-fabric-react/lib/Link'; import { botDisplayNameState, dispatcherState, settingsState, skillManifestsState } from '../../../../recoilModel'; import { CreatePublishProfileDialog } from '../../../botProject/CreatePublishProfileDialog'; import { iconStyle } from '../../../botProject/runtime-settings/style'; import { ContentProps, VERSION_REGEX } from '../constants'; +import { PublishProfileWrapperDialog } from '../../../botProject/PublishProfieWrapperDialog'; const styles = { container: css` @@ -55,6 +59,21 @@ const onRenderLabel = (props) => { ); }; +const onRenderInvalidProfileWarning = (hasValidProfile, handleShowPublishProfileWrapperDialog) => { + return ( + + {formatMessage('Publish profile is missing App ID and host name. ')} + {hasValidProfile ? ( + formatMessage('Choose a valid publish profile to continue') + ) : ( + + {formatMessage('Create a valid publish profile to continue')} + + )} + + ); +}; + export const getManifestId = ( botName: string, skillManifests: SkillManifestFile[], @@ -76,6 +95,24 @@ export const getManifestId = ( return fileId; }; +const onRenderTitle = (options: IDropdownOption[] | undefined): JSX.Element | null => { + const option = options?.[0]; + + return option ? ( +
+ {option.text} + {option.data?.icon && ( +
+ ) : null; +}; + export const SelectProfile: React.FC = ({ manifest, setSkillManifest, @@ -94,6 +131,18 @@ export const SelectProfile: React.FC = ({ const botName = useRecoilValue(botDisplayNameState(projectId)); const skillManifests = useRecoilValue(skillManifestsState(projectId)); + const [showCreateProfileDialog, setShowCreateProfileDialog] = useState(true); + const [selectedKey, setSelectedKey] = useState(''); + const [showPublishProfileWrapperDialog, setShowPublishProfileWrapperDialog] = useState(false); + + const handleShowPublishProfileWrapperDialog = () => { + setShowPublishProfileWrapperDialog(true); + }; + + const handleHiddenPublishProfileWrapperDialog = () => { + setShowPublishProfileWrapperDialog(false); + }; + const handleCurrentProfileChange = useMemo( () => (_e, option?: IDropdownOption) => { const target = publishingTargets.find((t) => { @@ -127,43 +176,46 @@ export const SelectProfile: React.FC = ({ }, id: id, }); + setSelectedKey(currentTarget.name); } } catch (err) { console.log(err.message); } }, [currentTarget]); - const isProfileValid = useMemo(() => { + + const isValidProfile = (publishTarget) => { try { - if (!publishingTargets) { - return false; - } - const filteredProfile = publishingTargets.filter((item) => { - const config = JSON.parse(item.configuration); - return ( - config.settings && - config.settings.MicrosoftAppId && - config.hostname && - config.settings.MicrosoftAppId.length > 0 && - config.hostname.length > 0 - ); - }); - return filteredProfile.length > 0; + const config = JSON.parse(publishTarget.configuration); + + return ( + config.settings && + config.settings.MicrosoftAppId && + config.hostname && + config.settings.MicrosoftAppId.length > 0 && + config.hostname.length > 0 + ); } catch (err) { console.log(err.message); return false; } - }, [publishingTargets]); + }; + + const hasValidProfile = useMemo(() => !!publishingTargets.some((target) => isValidProfile(target)), [ + publishingTargets, + ]); const publishingOptions = useMemo(() => { return publishingTargets.map((t) => ({ key: t.name, text: t.name, + data: !isValidProfile(t) ? { icon: 'TriangleSolid', color: NeutralColors.gray60 } : undefined, })); }, [publishingTargets]); useEffect(() => { setPublishingTargets(settings.publishTargets || []); setCurrentTarget((settings.publishTargets || [])[0]); + setShowCreateProfileDialog(!settings.publishTargets || settings.publishTargets.length === 0); }, [settings]); useEffect(() => { @@ -173,45 +225,61 @@ export const SelectProfile: React.FC = ({ } }, [id]); - return isProfileValid ? ( -
- - - -
- ) : ( -
- -
+ return ( + + {!showCreateProfileDialog ? ( +
+ + {!isValidProfile(currentTarget) ? ( + onRenderInvalidProfileWarning(hasValidProfile, handleShowPublishProfileWrapperDialog) + ) : ( + + + + + )} +
+ ) : ( +
+ +
+ )} + {showPublishProfileWrapperDialog && ( + + )} +
); }; diff --git a/Composer/packages/client/src/pages/design/exportSkillModal/index.tsx b/Composer/packages/client/src/pages/design/exportSkillModal/index.tsx index 209ebf9d8c..b932cc9800 100644 --- a/Composer/packages/client/src/pages/design/exportSkillModal/index.tsx +++ b/Composer/packages/client/src/pages/design/exportSkillModal/index.tsx @@ -322,7 +322,8 @@ const ExportSkillModal: React.FC = ({ onSubmit, onDismiss {buttons.map(({ disabled, primary, text, onClick }, index) => { const Button = primary ? PrimaryButton : DefaultButton; - const isDisabled = typeof disabled === 'function' ? disabled({ publishTargets }) : !!disabled; + const isDisabled = + typeof disabled === 'function' ? disabled({ publishTarget: currentPublishTarget }) : !!disabled; return (