From 4520669c437a95181315bc428524ee94b26c104f Mon Sep 17 00:00:00 2001 From: Soroush Date: Thu, 8 Oct 2020 16:03:57 -0700 Subject: [PATCH 1/8] progress --- Composer/packages/client/package.json | 3 +- Composer/packages/client/src/App.tsx | 6 +- .../CreateFormDialogSchemaModal.tsx | 95 ++++++++ .../src/pages/form-dialog/FormDialogPage.tsx | 155 +++++++++++++ .../form-dialog/FormDialogSchemaList.tsx | 215 ++++++++++++++++++ .../FormDialogSchemaListHeader.tsx | 78 +++++++ .../VisualFormDialogSchemaEditor.tsx | 130 +++++++++++ .../pages/notifications/useNotifications.tsx | 28 ++- .../src/recoilModel/DispatcherWrapper.tsx | 3 + .../client/src/recoilModel/atoms/botState.ts | 35 ++- .../recoilModel/dispatchers/formDialogs.ts | 97 ++++++++ .../src/recoilModel/dispatchers/index.ts | 2 + .../src/recoilModel/dispatchers/project.ts | 95 ++++---- .../persistence/FilePersistence.ts | 14 +- .../src/recoilModel/persistence/types.ts | 1 + Composer/packages/client/src/router.tsx | 3 + .../packages/client/src/utils/pageLinks.ts | 7 + .../src/FormDialogSchemaEditor.tsx | 17 +- .../components/FormDialogPropertiesEditor.tsx | 30 ++- .../components/common/CommandBarUpload.tsx | 7 +- .../form-dialogs/src/demo/DemoApp.tsx | 2 +- .../lib/indexers/src/utils/fileExtensions.ts | 1 + .../packages/lib/shared/src/types/indexers.ts | 11 + .../packages/server/src/locales/en-US.json | 66 +++--- .../server/src/models/bot/botProject.ts | 12 + Composer/yarn.lock | 40 ++-- 26 files changed, 1035 insertions(+), 118 deletions(-) create mode 100644 Composer/packages/client/src/pages/form-dialog/CreateFormDialogSchemaModal.tsx create mode 100644 Composer/packages/client/src/pages/form-dialog/FormDialogPage.tsx create mode 100644 Composer/packages/client/src/pages/form-dialog/FormDialogSchemaList.tsx create mode 100644 Composer/packages/client/src/pages/form-dialog/FormDialogSchemaListHeader.tsx create mode 100644 Composer/packages/client/src/pages/form-dialog/VisualFormDialogSchemaEditor.tsx create mode 100644 Composer/packages/client/src/recoilModel/dispatchers/formDialogs.ts diff --git a/Composer/packages/client/package.json b/Composer/packages/client/package.json index b9417f2435..86a4897c92 100644 --- a/Composer/packages/client/package.json +++ b/Composer/packages/client/package.json @@ -23,6 +23,7 @@ "@bfc/adaptive-form": "*", "@bfc/code-editor": "*", "@bfc/extension-client": "*", + "@bfc/form-dialogs": "*", "@bfc/indexers": "*", "@bfc/shared": "*", "@bfc/ui-plugin-composer": "*", @@ -49,13 +50,13 @@ "office-ui-fabric-react": "^7.121.11", "prop-types": "^15.7.2", "query-string": "^6.8.2", - "react-measure": "^2.3.0", "re-resizable": "^6.3.2", "react": "16.13.1", "react-app-polyfill": "^0.2.1", "react-dev-utils": "^7.0.3", "react-dom": "16.13.1", "react-frame-component": "^4.0.2", + "react-measure": "^2.3.0", "react-timeago": "^4.4.0", "recoil": "^0.0.13", "styled-components": "^4.1.3", diff --git a/Composer/packages/client/src/App.tsx b/Composer/packages/client/src/App.tsx index 3a1fd4bbeb..e9955277a1 100644 --- a/Composer/packages/client/src/App.tsx +++ b/Composer/packages/client/src/App.tsx @@ -20,10 +20,12 @@ export const App: React.FC = () => { loadLocale(appLocale); }, [appLocale]); - const { fetchExtensions } = useRecoilValue(dispatcherState); + const { fetchExtensions, loadFormDialogSchemaTemplates } = useRecoilValue(dispatcherState); + useEffect(() => { fetchExtensions(); - }); + loadFormDialogSchemaTemplates(); + }, []); return ( diff --git a/Composer/packages/client/src/pages/form-dialog/CreateFormDialogSchemaModal.tsx b/Composer/packages/client/src/pages/form-dialog/CreateFormDialogSchemaModal.tsx new file mode 100644 index 0000000000..d9bcd8a8a0 --- /dev/null +++ b/Composer/packages/client/src/pages/form-dialog/CreateFormDialogSchemaModal.tsx @@ -0,0 +1,95 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +import formatMessage from 'format-message'; +import { DefaultButton, PrimaryButton } from 'office-ui-fabric-react/lib/Button'; +import { DialogFooter } from 'office-ui-fabric-react/lib/Dialog'; +import { Stack } from 'office-ui-fabric-react/lib/Stack'; +import { TextField } from 'office-ui-fabric-react/lib/TextField'; +import React, { useCallback } from 'react'; +import { useRecoilValue } from 'recoil'; + +import { DialogTypes, DialogWrapper } from '../../components/DialogWrapper'; +import { nameRegex } from '../../constants'; +import { FieldConfig, useForm } from '../../hooks/useForm'; +import { dialogsState } from '../../recoilModel'; + +type FormDialogSchemaFormData = { + name: string; +}; + +type Props = { + projectId: string; + isOpen: boolean; + onSubmit: (formDialogName: string) => void; + onDismiss: () => void; +}; + +const CreateFormDialogSchemaModal: React.FC = (props) => { + const { isOpen, projectId, onSubmit, onDismiss } = props; + + const dialogs = useRecoilValue(dialogsState(projectId)); + + const formConfig: FieldConfig = { + name: { + required: true, + validate: (value) => { + if (!nameRegex.test(value)) { + return formatMessage('Spaces and special characters are not allowed. Use letters, numbers, -, or _.'); + } + if (dialogs.some((dialog) => dialog.id === value)) { + return formatMessage('Dialog with the name: {value} already exists.', { value }); + } + }, + }, + }; + + const { formData, formErrors, hasErrors, updateField } = useForm(formConfig); + + const handleSubmit = useCallback( + (e) => { + e.preventDefault(); + if (hasErrors) { + return; + } + + onSubmit(formData.name); + }, + [hasErrors, formData] + ); + + return ( + +
+ + + updateField('name', val)} + /> + + + + + + +
+
+ ); +}; + +export default CreateFormDialogSchemaModal; diff --git a/Composer/packages/client/src/pages/form-dialog/FormDialogPage.tsx b/Composer/packages/client/src/pages/form-dialog/FormDialogPage.tsx new file mode 100644 index 0000000000..8e09fc3c39 --- /dev/null +++ b/Composer/packages/client/src/pages/form-dialog/FormDialogPage.tsx @@ -0,0 +1,155 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +import styled from '@emotion/styled'; +import { RouteComponentProps } from '@reach/router'; +import { Stack } from 'office-ui-fabric-react/lib/Stack'; +import * as React from 'react'; +import { useRecoilValue } from 'recoil'; + +import { + dispatcherState, + formDialogGenerationProgressingState, + formDialogLibraryTemplatesState, + formDialogSchemasState, +} from '../../recoilModel'; +import { LeftRightSplit } from '../../components/Split/LeftRightSplit'; + +import { VisualFormDialogSchemaEditor } from './VisualFormDialogSchemaEditor'; +import { FormDialogSchemaList } from './FormDialogSchemaList'; +import CreateFormDialogSchemaModal from './CreateFormDialogSchemaModal'; + +const EmptyView = styled(Stack)({ + position: 'relative', + width: '100%', + ':after': { + fontSize: 20, + content: '"Select an schema to edit or create a new one"', + position: 'absolute', + textAlign: 'center', + top: '50%', + left: '50%', + transform: 'translate(-50%, -50%)', + opacity: 0.5, + }, +}); + +type Props = RouteComponentProps<{ projectId: string; schemaId: string }>; + +const FormDialogPage: React.FC = React.memo((props: Props) => { + const { projectId = '', schemaId = '' } = props; + const formDialogSchemas = useRecoilValue(formDialogSchemasState(projectId)); + const formDialogLibraryTemplates = useRecoilValue(formDialogLibraryTemplatesState); + const formDialogGenerationProgressing = useRecoilValue(formDialogGenerationProgressingState); + const { + loadFormDialogSchemaTemplates, + removeFormDialogSchema, + generateFormDialog, + createFormDialogSchema, + updateFormDialogSchema, + } = useRecoilValue(dispatcherState); + + React.useEffect(() => { + (async () => { + await loadFormDialogSchemaTemplates(); + })(); + }, []); + + const { 0: createSchemaDialogOpen, 1: setCreateSchemaDialogOpen } = React.useState(false); + const { 0: selectedFormDialogSchemaId, 1: setSelectedFormDialogSchemaId } = React.useState(schemaId); + + const availableTemplates = React.useMemo( + () => formDialogLibraryTemplates.filter((t) => !t.isGlobal).map((t) => t.name), + [formDialogLibraryTemplates] + ); + + const selectedFormDialogSchema = React.useMemo( + () => formDialogSchemas.find((fds) => fds.id === selectedFormDialogSchemaId), + [formDialogSchemas, selectedFormDialogSchemaId] + ); + + const createItemStart = React.useCallback(() => setCreateSchemaDialogOpen(true), [setCreateSchemaDialogOpen]); + + const selectItem = React.useCallback( + (id: string) => { + setSelectedFormDialogSchemaId(id); + }, + [setSelectedFormDialogSchemaId] + ); + + const deleteItem = React.useCallback( + (id: string) => { + removeFormDialogSchema({ id, projectId }); + if (selectedFormDialogSchemaId === id) { + selectItem(''); + } + }, + [selectItem, removeFormDialogSchema, selectedFormDialogSchemaId] + ); + + const generateFormDialogs = React.useCallback( + (schemaId: string) => { + if (schemaId) { + generateFormDialog({ projectId, schemaId }); + } + }, + [generateFormDialog, projectId] + ); + + const updateItem = React.useCallback( + (id: string, content: string) => { + if (id === selectedFormDialogSchemaId) { + updateFormDialogSchema({ id, content, projectId }); + } + }, + [updateFormDialogSchema, selectedFormDialogSchemaId] + ); + + const createItem = React.useCallback( + (formDialogName: string) => { + createFormDialogSchema({ id: formDialogName, projectId }); + setCreateSchemaDialogOpen(false); + }, + [createFormDialogSchema, setCreateSchemaDialogOpen] + ); + + return ( + <> + + + + {selectedFormDialogSchema ? ( + + ) : ( + + )} + + + {createSchemaDialogOpen ? ( + setCreateSchemaDialogOpen(false)} + onSubmit={createItem} + /> + ) : null} + + ); +}); + +export default FormDialogPage; diff --git a/Composer/packages/client/src/pages/form-dialog/FormDialogSchemaList.tsx b/Composer/packages/client/src/pages/form-dialog/FormDialogSchemaList.tsx new file mode 100644 index 0000000000..49a644f145 --- /dev/null +++ b/Composer/packages/client/src/pages/form-dialog/FormDialogSchemaList.tsx @@ -0,0 +1,215 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +import { FormDialogSchema } from '@bfc/shared'; +import styled from '@emotion/styled'; +import { DefaultPalette } from '@uifabric/styling'; +import formatMessage from 'format-message'; +import debounce from 'lodash/debounce'; +import { IconButton } from 'office-ui-fabric-react/lib/Button'; +import { FocusZone, FocusZoneDirection } from 'office-ui-fabric-react/lib/FocusZone'; +import { Icon } from 'office-ui-fabric-react/lib/Icon'; +import { List } from 'office-ui-fabric-react/lib/List'; +import { IStackItemProps, IStackItemStyles, Stack } from 'office-ui-fabric-react/lib/Stack'; +import { classNamesFunction } from 'office-ui-fabric-react/lib/Utilities'; +import * as React from 'react'; + +import { FormDialogSchemaListHeader } from './FormDialogSchemaListHeader'; + +const isEmptyObject = (objStr: string) => objStr === '{}'; + +const Root = styled(Stack)<{ + loading: boolean; +}>( + { + position: 'relative', + width: '100%', + height: '100%', + borderRight: '1px solid #c4c4c4', + boxSizing: 'border-box', + overflowY: 'auto', + overflowX: 'hidden', + '& .ms-List-cell': { + minHeight: '36px', + }, + }, + (props) => + props.loading + ? { + '&:after': { + content: '""', + position: 'absolute', + top: 0, + left: 0, + width: '100%', + height: '100%', + background: 'rgba(255,255,255, 0.6)', + zIndex: 1, + }, + } + : null +); + +const oneLinerStyles = classNamesFunction()({ + root: { + whiteSpace: 'nowrap', + overflow: 'hidden', + textOverflow: 'ellipsis', + }, +}); + +const EmptyView = styled(Stack)({ + opacity: 0.5, + padding: 8, + fontSize: 14, + textAlign: 'center', +}); + +type SchemaItemProps = { + id: string; + selected: boolean; + isEmpty?: boolean; + onClick: (name: string) => void; + onDelete: (name: string) => void; + onGenerate: (name: string) => void; +}; + +const FormDialogSchemaItem = React.memo((props: SchemaItemProps) => { + const { id, onClick, onDelete, onGenerate, selected = false, isEmpty = false } = props; + + const clickHandler = React.useCallback(() => { + onClick(id); + }, [id, onClick]); + + const deleteHandler = React.useCallback( + (e: React.MouseEvent) => { + e.preventDefault(); + e.stopPropagation(); + + onDelete(id); + }, + [id, onDelete] + ); + + const runHandler = React.useCallback( + (e: React.MouseEvent) => { + e.preventDefault(); + e.stopPropagation(); + + onGenerate(id); + }, + [id, onGenerate] + ); + + return ( + + + + {id} + + + + + + + ); +}); + +type FormDialogSchemaListProps = { + items: readonly FormDialogSchema[]; + selectedId: string | undefined; + onSelectItem: (schemaId: string) => void; + onDeleteItem: (schemaId: string) => void; + onGenerate: (schemaId: string) => void; + onCreateItem: () => void; + loading?: boolean; +}; + +export const FormDialogSchemaList: React.FC = React.memo((props) => { + const { selectedId, items, onDeleteItem, onSelectItem, onCreateItem, onGenerate, loading = false } = props; + + const { 0: query, 1: setQuery } = React.useState(''); + const delayedSetQuery = debounce((newValue) => setQuery(newValue), 300); + + const filteredItems = React.useMemo(() => { + return items.filter(({ id }) => id.toLowerCase().indexOf(query.toLowerCase()) !== -1); + }, [query, items, selectedId]); + + const onFilter = (_e?: React.ChangeEvent, newValue?: string): void => { + if (typeof newValue === 'string') { + delayedSetQuery(newValue); + } + }; + + const renderCell = React.useCallback( + (item) => ( + + ), + [selectedId, onSelectItem, onDeleteItem, onGenerate] + ); + + return ( + + + +
+ {filteredItems.length ? ( + items={filteredItems} onRenderCell={renderCell} /> + ) : ( + + {query + ? formatMessage('No form dialog schema matches your filtering criteria!') + : formatMessage('Create a new form dialog schema by clicking + above.')} + + )} + + + ); +}); diff --git a/Composer/packages/client/src/pages/form-dialog/FormDialogSchemaListHeader.tsx b/Composer/packages/client/src/pages/form-dialog/FormDialogSchemaListHeader.tsx new file mode 100644 index 0000000000..d3300c7ec2 --- /dev/null +++ b/Composer/packages/client/src/pages/form-dialog/FormDialogSchemaListHeader.tsx @@ -0,0 +1,78 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +import styled from '@emotion/styled'; +import formatMessage from 'format-message'; +import { IconButton } from 'office-ui-fabric-react/lib/Button'; +import { Label } from 'office-ui-fabric-react/lib/Label'; +import { ProgressIndicator } from 'office-ui-fabric-react/lib/ProgressIndicator'; +import { Stack } from 'office-ui-fabric-react/lib/Stack'; +import * as React from 'react'; +import { SearchBox, ISearchBoxProps, ISearchBoxStyles } from 'office-ui-fabric-react/lib/SearchBox'; +import { classNamesFunction } from 'office-ui-fabric-react/lib/Utilities'; + +const TitleBar = styled(Stack)({ + flex: 1, + height: 45, +}); + +const Title = styled(Label)({}); + +const LoadingIndicator = styled(ProgressIndicator)({ + position: 'absolute', + width: 'calc(100% - 16px)', + left: 8, + top: 25, + zIndex: 2, +}); + +const searchBoxStyles = classNamesFunction()({ + root: { + borderBottom: '1px solid #edebe9', + width: '100%', + }, + clearButton: { display: 'none' }, +}); + +type ListHeaFormDialogSchemaListHeaderProps = { + loading?: boolean; + searchDisabled?: boolean; + onChangeQuery: (e?: React.ChangeEvent | undefined, query?: string | undefined) => void; + onCreateItem: () => void; +}; + +export const FormDialogSchemaListHeader = (props: ListHeaFormDialogSchemaListHeaderProps) => { + const { loading = false, searchDisabled = false, onCreateItem, onChangeQuery } = props; + + const { 0: isFilterOn, 1: setFilterOn } = React.useState(false); + return ( + + + {isFilterOn ? ( + setFilterOn(false)} + /> + ) : ( + + {formatMessage('Schemas')} + {loading && <LoadingIndicator />} + + )} + + {isFilterOn ? ( + setFilterOn(false)} /> + ) : ( + <> + setFilterOn(true)} /> + + + )} + + ); +}; diff --git a/Composer/packages/client/src/pages/form-dialog/VisualFormDialogSchemaEditor.tsx b/Composer/packages/client/src/pages/form-dialog/VisualFormDialogSchemaEditor.tsx new file mode 100644 index 0000000000..edcb81881f --- /dev/null +++ b/Composer/packages/client/src/pages/form-dialog/VisualFormDialogSchemaEditor.tsx @@ -0,0 +1,130 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +import { JsonEditor } from '@bfc/code-editor'; +import { FormDialogSchemaEditor } from '@bfc/form-dialogs'; +import styled from '@emotion/styled'; +import { NeutralColors } from '@uifabric/fluent-theme'; +import formatMessage from 'format-message'; +import { ActionButton } from 'office-ui-fabric-react/lib/Button'; +import { IStackProps, IStackStyles, Stack } from 'office-ui-fabric-react/lib/Stack'; +import { classNamesFunction } from 'office-ui-fabric-react/lib/Utilities'; +import * as React from 'react'; + +const Root = styled(Stack)<{ + inProgress: boolean; +}>( + { + position: 'relative', + width: '100%', + backgroundColor: NeutralColors.gray20, + }, + (props) => + props.inProgress + ? { + '&:after': { + content: '""', + position: 'absolute', + top: 0, + left: 0, + width: '100%', + height: '100%', + background: 'rgba(255,255,255, 0.6)', + zIndex: 1, + }, + } + : null +); + +const noop = () => {}; +const defaultValue: object = {}; + +const editorTopBarStyles = classNamesFunction()({ + root: { backgroundColor: '#fff', height: '45px', marginBottom: 1 }, +}); + +type Props = { + projectId?: string; + generationInProgress?: boolean; + schema: { id: string; content: string }; + templates: string[]; + onChange: (id: string, content: string) => void; + onGenerate: (schemaId: string) => void; +}; + +export const VisualFormDialogSchemaEditor = React.memo((props: Props) => { + const { projectId, schema, templates, onChange, onGenerate, generationInProgress = false } = props; + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const editorRef = React.useRef(); + const getEditorValueRef = React.useRef<() => string>(() => schema.content || JSON.stringify(defaultValue, null, 2)); + const dialogSchemaContentRef = React.useRef(schema.content || JSON.stringify(defaultValue, null, 2)); + + const [showEditor, setShowEditor] = React.useState(false); + + React.useEffect(() => { + if (showEditor) { + dialogSchemaContentRef.current = schema.content || JSON.stringify(defaultValue, null, 2); + } + editorRef.current?.setValue(schema.content); + }, [schema.content]); + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const onEditorDidMount = (getValue: () => string, editor: any) => { + editorRef.current = editor; + getEditorValueRef.current = getValue; + editorRef.current.setValue(dialogSchemaContentRef.current); + }; + + const onSchemaUpdated = (id: string, content: string) => { + onChange(id, content); + + dialogSchemaContentRef.current = content; + editorRef.current?.setValue(content); + }; + + return ( + + + setShowEditor(!showEditor)}> + {showEditor ? formatMessage('Hide code') : formatMessage('Show code')} + + + + + {!showEditor ? ( + + ) : ( + + )} + + + ); +}); diff --git a/Composer/packages/client/src/pages/notifications/useNotifications.tsx b/Composer/packages/client/src/pages/notifications/useNotifications.tsx index bbe66112bd..eca1a3589a 100644 --- a/Composer/packages/client/src/pages/notifications/useNotifications.tsx +++ b/Composer/packages/client/src/pages/notifications/useNotifications.tsx @@ -1,33 +1,35 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +import { BotIndexer } from '@bfc/indexers'; +import { BotAssets } from '@bfc/shared'; +import get from 'lodash/get'; import { useMemo } from 'react'; import { useRecoilValue } from 'recoil'; -import get from 'lodash/get'; -import { BotIndexer } from '@bfc/indexers'; import { - validateDialogSelectorFamily, - luFilesState, - lgFilesState, botDiagnosticsState, - settingsState, - skillManifestsState, dialogSchemasState, + formDialogSchemasState, + lgFilesState, + luFilesState, qnaFilesState, + settingsState, + skillManifestsState, + validateDialogSelectorFamily, } from '../../recoilModel'; +import { getReferredLuFiles } from './../../utils/luUtil'; import { - Notification, DialogNotification, - SettingNotification, - LuNotification, LgNotification, + LuNotification, + Notification, QnANotification, ServerNotification, + SettingNotification, SkillNotification, } from './types'; -import { getReferredLuFiles } from './../../utils/luUtil'; export default function useNotifications(projectId: string, filter?: string) { const dialogs = useRecoilValue(validateDialogSelectorFamily(projectId)); @@ -38,8 +40,9 @@ export default function useNotifications(projectId: string, filter?: string) { const skillManifests = useRecoilValue(skillManifestsState(projectId)); const dialogSchemas = useRecoilValue(dialogSchemasState(projectId)); const qnaFiles = useRecoilValue(qnaFilesState(projectId)); + const formDialogSchemas = useRecoilValue(formDialogSchemasState(projectId)); - const botAssets = { + const botAssets: BotAssets = { projectId, dialogs, luFiles, @@ -48,6 +51,7 @@ export default function useNotifications(projectId: string, filter?: string) { skillManifests, setting, dialogSchemas, + formDialogSchemas, }; const memoized = useMemo(() => { diff --git a/Composer/packages/client/src/recoilModel/DispatcherWrapper.tsx b/Composer/packages/client/src/recoilModel/DispatcherWrapper.tsx index 892e7b7455..abb46ddba0 100644 --- a/Composer/packages/client/src/recoilModel/DispatcherWrapper.tsx +++ b/Composer/packages/client/src/recoilModel/DispatcherWrapper.tsx @@ -23,6 +23,7 @@ import { dialogSchemasState, settingsState, filePersistenceState, + formDialogSchemasState, } from './atoms'; const getBotAssets = async (projectId, snapshot: Snapshot): Promise => { @@ -34,6 +35,7 @@ const getBotAssets = async (projectId, snapshot: Snapshot): Promise = snapshot.getPromise(skillManifestsState(projectId)), snapshot.getPromise(settingsState(projectId)), snapshot.getPromise(dialogSchemasState(projectId)), + snapshot.getPromise(formDialogSchemasState(projectId)), ]); return { projectId, @@ -44,6 +46,7 @@ const getBotAssets = async (projectId, snapshot: Snapshot): Promise = skillManifests: result[4], setting: result[5], dialogSchemas: result[6], + formDialogSchemas: result[7], }; }; diff --git a/Composer/packages/client/src/recoilModel/atoms/botState.ts b/Composer/packages/client/src/recoilModel/atoms/botState.ts index 5880e99a3d..fb485d38bc 100644 --- a/Composer/packages/client/src/recoilModel/atoms/botState.ts +++ b/Composer/packages/client/src/recoilModel/atoms/botState.ts @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -import { atomFamily } from 'recoil'; +import { atom, atomFamily, selectorFamily } from 'recoil'; import { DialogInfo, DialogSchemaFile, @@ -12,6 +12,8 @@ import { BotSchemas, Skill, DialogSetting, + FormDialogSchema, + FormDialogSchemaTemplate, } from '@bfc/shared'; import { BotLoadError, DesignPageLocation, QnAAllUpViewStatus } from '../../recoilModel/types'; @@ -254,3 +256,34 @@ export const filePersistenceState = atomFamily({ default: {} as FilePersistence, dangerouslyAllowMutability: true, }); + +export const formDialogSchemaIdsState = atomFamily({ + key: getFullyQualifiedKey('formDialogSchemaIds'), + default: [], +}); + +export const formDialogSchemaState = atomFamily({ + key: getFullyQualifiedKey('formDialogSchema'), + default: { + id: '', + content: '', + } as FormDialogSchema, +}); + +export const formDialogSchemasState = selectorFamily({ + key: getFullyQualifiedKey('formDialogSchemas'), + get: (projectId: string) => ({ get }) => { + const formDialogSchemaIds = get(formDialogSchemaIdsState(projectId)); + return formDialogSchemaIds.map((schemaId) => get(formDialogSchemaState({ projectId, schemaId }))); + }, +}); + +export const formDialogLibraryTemplatesState = atom({ + key: getFullyQualifiedKey('formDialogLibraryTemplates'), + default: [], +}); + +export const formDialogGenerationProgressingState = atom({ + key: getFullyQualifiedKey('formDialogGenerationProgressing'), + default: false, +}); diff --git a/Composer/packages/client/src/recoilModel/dispatchers/formDialogs.ts b/Composer/packages/client/src/recoilModel/dispatchers/formDialogs.ts new file mode 100644 index 0000000000..f4a484f386 --- /dev/null +++ b/Composer/packages/client/src/recoilModel/dispatchers/formDialogs.ts @@ -0,0 +1,97 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +/* eslint-disable react-hooks/rules-of-hooks */ + +import { FormDialogSchemaTemplate } from '@bfc/shared'; +import formatMessage from 'format-message'; +import { CallbackInterface, useRecoilCallback } from 'recoil'; + +import httpClient from '../../utils/httpUtil'; +import { applicationErrorState } from '../atoms/appState'; +import { + formDialogGenerationProgressingState, + formDialogLibraryTemplatesState, + formDialogSchemaIdsState, + formDialogSchemaState, +} from '../atoms/botState'; +import { dispatcherState } from '../DispatcherWrapper'; + +export const formDialogsDispatcher = () => { + const createFormDialogSchema = useRecoilCallback(({ set }: CallbackInterface) => ({ id, projectId }) => { + set(formDialogSchemaIdsState(projectId), (formDialogSchemaIds) => { + return [...formDialogSchemaIds, id]; + }); + + set(formDialogSchemaState({ projectId, schemaId: id }), { id, content: JSON.stringify({}, null, 4) }); + }); + + const updateFormDialogSchema = useRecoilCallback(({ set }: CallbackInterface) => ({ id, content, projectId }) => + set(formDialogSchemaState({ projectId, schemaId: id }), { id, content }) + ); + + const removeFormDialogSchema = useRecoilCallback(({ set, reset }: CallbackInterface) => async ({ id, projectId }) => { + set(formDialogSchemaIdsState(projectId), (formDialogSchemas) => { + return formDialogSchemas.filter((fdId) => fdId !== id); + }); + reset(formDialogSchemaState({ projectId, schemaId: id })); + }); + + const loadFormDialogSchemaTemplates = useRecoilCallback((callbackHelpers: CallbackInterface) => async () => { + const { set, snapshot } = callbackHelpers; + const templates = await snapshot.getPromise(formDialogLibraryTemplatesState); + // If templates are already loaded, don't reload. + if (templates.length) { + return; + } + + try { + const { data } = await httpClient.get('/formDialogs/templateSchemas'); + const templates = Object.keys(data).map((key) => ({ + name: key, + isGlobal: data[key].$global, + })); + + set(formDialogLibraryTemplatesState, templates); + } catch (error) { + set(applicationErrorState, { + message: error.message, + summary: formatMessage('Load form dialog schema templates Error'), + }); + } + }); + + const generateFormDialog = useRecoilCallback( + (callbackHelpers: CallbackInterface) => async ({ projectId, schemaId }) => { + const { set, snapshot } = callbackHelpers; + const { reloadProject } = await snapshot.getPromise(dispatcherState); + try { + set(formDialogGenerationProgressingState, true); + + const formDialogSchema = await snapshot.getPromise(formDialogSchemaState({ projectId, schemaId })); + if (!formDialogSchema) { + return; + } + + const response = await httpClient.post(`/formDialogs/${projectId}/generate`, { + name: schemaId, + }); + reloadProject(callbackHelpers, response.data); + } catch (error) { + set(applicationErrorState, { + message: error.message, + summary: formatMessage('Generating form dialog using ${schemaId} schema failed.', { schemaId }), + }); + } finally { + set(formDialogGenerationProgressingState, false); + } + } + ); + + return { + createFormDialogSchema, + updateFormDialogSchema, + loadFormDialogSchemaTemplates, + removeFormDialogSchema, + generateFormDialog, + }; +}; diff --git a/Composer/packages/client/src/recoilModel/dispatchers/index.ts b/Composer/packages/client/src/recoilModel/dispatchers/index.ts index edbae77387..d74e94a025 100644 --- a/Composer/packages/client/src/recoilModel/dispatchers/index.ts +++ b/Composer/packages/client/src/recoilModel/dispatchers/index.ts @@ -20,6 +20,7 @@ import { userDispatcher } from './user'; import { multilangDispatcher } from './multilang'; import { notificationDispatcher } from './notification'; import { extensionsDispatcher } from './extensions'; +import { formDialogsDispatcher } from './formDialogs'; const createDispatchers = () => { return { @@ -42,6 +43,7 @@ const createDispatchers = () => { ...multilangDispatcher(), ...notificationDispatcher(), ...extensionsDispatcher(), + ...formDialogsDispatcher(), }; }; diff --git a/Composer/packages/client/src/recoilModel/dispatchers/project.ts b/Composer/packages/client/src/recoilModel/dispatchers/project.ts index 506b2a5ff9..b9f1cb99a2 100644 --- a/Composer/packages/client/src/recoilModel/dispatchers/project.ts +++ b/Composer/packages/client/src/recoilModel/dispatchers/project.ts @@ -1,71 +1,73 @@ /* eslint-disable react-hooks/rules-of-hooks */ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -import { useRecoilCallback, CallbackInterface } from 'recoil'; +import { indexer, validateDialog } from '@bfc/indexers'; import { + convertSkillsToDictionary, dereferenceDefinitions, + DialogInfo, + DialogSetting, + FileExtensions, LuFile, QnAFile, - DialogInfo, SensitiveProperties, - DialogSetting, - convertSkillsToDictionary, } from '@bfc/shared'; -import { indexer, validateDialog } from '@bfc/indexers'; +import formatMessage from 'format-message'; import objectGet from 'lodash/get'; import objectSet from 'lodash/set'; -import formatMessage from 'format-message'; +import { CallbackInterface, useRecoilCallback } from 'recoil'; -import lgWorker from '../parsers/lgWorker'; -import luWorker from '../parsers/luWorker'; -import qnaWorker from '../parsers/qnaWorker'; +import { BotStatus, QnABotTemplateId } from '../../constants'; +import settingStorage from '../../utils/dialogSettingStorage'; +import { getBaseName } from '../../utils/fileUtil'; import httpClient from '../../utils/httpUtil'; -import { BotStatus } from '../../constants'; -import { getReferredLuFiles } from '../../utils/luUtil'; +import languageStorage from '../../utils/languageStorage'; import luFileStatusStorage from '../../utils/luFileStatusStorage'; -import { getReferredQnaFiles } from '../../utils/qnaUtil'; -import qnaFileStatusStorage from '../../utils/qnaFileStatusStorage'; -import settingStorage from '../../utils/dialogSettingStorage'; -import filePersistence from '../persistence/FilePersistence'; +import { getReferredLuFiles } from '../../utils/luUtil'; import { navigateTo } from '../../utils/navigation'; -import languageStorage from '../../utils/languageStorage'; import { projectIdCache } from '../../utils/projectCache'; +import qnaFileStatusStorage from '../../utils/qnaFileStatusStorage'; +import { getReferredQnaFiles } from '../../utils/qnaUtil'; import { - designPageLocationState, botDiagnosticsState, botProjectsSpaceState, - projectMetaDataState, - filePersistenceState, currentProjectIdState, + designPageLocationState, + filePersistenceState, + formDialogSchemaIdsState, + formDialogSchemaState, + projectMetaDataState, } from '../atoms'; -import { QnABotTemplateId } from '../../constants'; -import FilePersistence from '../persistence/FilePersistence'; -import UndoHistory from '../undo/undoHistory'; +import lgWorker from '../parsers/lgWorker'; +import luWorker from '../parsers/luWorker'; +import qnaWorker from '../parsers/qnaWorker'; +import { default as filePersistence, default as FilePersistence } from '../persistence/FilePersistence'; import { undoHistoryState } from '../undo/history'; +import UndoHistory from '../undo/undoHistory'; import { - skillManifestsState, - settingsState, + announcementState, + applicationErrorState, + boilerplateVersionState, + botEnvironmentState, + botNameState, + botOpeningState, + botStatusState, + dialogSchemasState, + dialogsState, + lgFilesState, localeState, + locationState, luFilesState, qnaFilesState, - skillsState, - schemasState, - lgFilesState, - locationState, - botStatusState, - botNameState, - botEnvironmentState, - dialogsState, - botOpeningState, recentProjectsState, - templateProjectsState, runtimeTemplatesState, - applicationErrorState, + schemasState, + settingsState, + skillManifestsState, + skillsState, templateIdState, - announcementState, - boilerplateVersionState, - dialogSchemasState, + templateProjectsState, } from './../atoms'; import { logMessage, setError } from './../dispatchers/shared'; @@ -206,6 +208,8 @@ export const projectDispatcher = () => { await lgWorker.addProject(projectId, lgFiles); set(botProjectsSpaceState, []); + const formDialogSchemaFiles = files.filter((f) => f.name.endsWith(FileExtensions.FormDialogSchema)); + // Important: gotoSnapshot will wipe all states. const newSnapshot = snapshot.map(({ set }) => { set(skillManifestsState(projectId), skillManifestFiles); @@ -225,6 +229,16 @@ export const projectDispatcher = () => { set(localeState(projectId), locale); set(botDiagnosticsState(projectId), diagnostics); + // Form dialogs + set( + formDialogSchemaIdsState(projectId), + formDialogSchemaFiles.map((f) => getBaseName(f.name)) + ); + formDialogSchemaFiles.forEach((f) => { + const schemaId = getBaseName(f.name); + set(formDialogSchemaState({ projectId, schemaId }), { id: schemaId, content: f.content }); + }); + refreshLocalStorage(projectId, settings); set(settingsState(projectId), mergedSettings); set(filePersistenceState(projectId), new FilePersistence(projectId)); @@ -257,6 +271,10 @@ export const projectDispatcher = () => { } }; + const reloadProject = async (callbackHelpers: CallbackInterface, data: any) => { + await initBotState(callbackHelpers, data, false, ''); + }; + const removeRecentProject = async (callbackHelpers: CallbackInterface, path: string) => { try { const { @@ -505,5 +523,6 @@ export const projectDispatcher = () => { saveTemplateId, updateBoilerplate, getBoilerplateVersion, + reloadProject, }; }; diff --git a/Composer/packages/client/src/recoilModel/persistence/FilePersistence.ts b/Composer/packages/client/src/recoilModel/persistence/FilePersistence.ts index 16a098ea40..905bc9c04e 100644 --- a/Composer/packages/client/src/recoilModel/persistence/FilePersistence.ts +++ b/Composer/packages/client/src/recoilModel/persistence/FilePersistence.ts @@ -3,7 +3,7 @@ import keys from 'lodash/keys'; import differenceWith from 'lodash/differenceWith'; import isEqual from 'lodash/isEqual'; -import { DialogInfo, DialogSchemaFile, DialogSetting, SkillManifest, BotAssets } from '@bfc/shared'; +import { DialogInfo, DialogSchemaFile, DialogSetting, SkillManifest, BotAssets, FormDialogSchema } from '@bfc/shared'; import { LuFile, LgFile, QnAFile } from './../../../../lib/shared/src/types/indexers'; import * as client from './http'; @@ -198,6 +198,12 @@ class FilePersistence { return []; } + private getFormDialogSchemaFileChanges(current: FormDialogSchema[], previous: FormDialogSchema[]) { + const changeItems = this.getDifferenceItems(current, previous); + const changes = this.getFileChanges(FileExtensions.FormDialog, changeItems); + return changes; + } + private getAssetsChanges(currentAssets: BotAssets, previousAssets: BotAssets): IFileChange[] { const dialogChanges = this.getDialogChanges(currentAssets.dialogs, previousAssets.dialogs); const dialogSchemaChanges = this.getDialogSchemaChanges(currentAssets.dialogSchemas, previousAssets.dialogSchemas); @@ -209,6 +215,11 @@ class FilePersistence { previousAssets.skillManifests ); const settingChanges = this.getSettingsChanges(currentAssets.setting, previousAssets.setting); + const formDialogChanges = this.getFormDialogSchemaFileChanges( + currentAssets.formDialogSchemas, + previousAssets.formDialogSchemas + ); + const fileChanges: IFileChange[] = [ ...dialogChanges, ...dialogSchemaChanges, @@ -217,6 +228,7 @@ class FilePersistence { ...lgChanges, ...skillManifestChanges, ...settingChanges, + ...formDialogChanges, ]; return fileChanges; } diff --git a/Composer/packages/client/src/recoilModel/persistence/types.ts b/Composer/packages/client/src/recoilModel/persistence/types.ts index d1e344c4d0..3814eeeb14 100644 --- a/Composer/packages/client/src/recoilModel/persistence/types.ts +++ b/Composer/packages/client/src/recoilModel/persistence/types.ts @@ -9,6 +9,7 @@ export enum ChangeType { export enum FileExtensions { Dialog = '.dialog', + FormDialog = '.form-dialog', DialogSchema = '.dialog.schema', Manifest = '.json', Lu = '.lu', diff --git a/Composer/packages/client/src/router.tsx b/Composer/packages/client/src/router.tsx index f24f4013a8..7d179df49a 100644 --- a/Composer/packages/client/src/router.tsx +++ b/Composer/packages/client/src/router.tsx @@ -27,6 +27,7 @@ const Notifications = React.lazy(() => import('./pages/notifications/Notificatio const Publish = React.lazy(() => import('./pages/publish/Publish')); const Skills = React.lazy(() => import('./pages/skills')); const BotCreationFlowRouter = React.lazy(() => import('./components/CreationFlow/CreationFlow')); +const FormDialogPage = React.lazy(() => import('./pages/form-dialog/FormDialogPage')); const Routes = (props) => { const botOpening = useRecoilValue(botOpeningState); @@ -55,6 +56,8 @@ const Routes = (props) => { + + diff --git a/Composer/packages/client/src/utils/pageLinks.ts b/Composer/packages/client/src/utils/pageLinks.ts index 3573c6993a..d1f53b7fc9 100644 --- a/Composer/packages/client/src/utils/pageLinks.ts +++ b/Composer/packages/client/src/utils/pageLinks.ts @@ -63,6 +63,13 @@ export const topLinks = (projectId: string, openedDialogId: string, pluginPages: exact: true, disabled: !botLoaded, }, + { + to: `/bot/${projectId}/form-dialogs`, + iconName: 'OfficeChat', + labelName: formatMessage('Form Dialogs'), + exact: false, + disabled: !botLoaded, + }, ]; if (process.env.COMPOSER_AUTH_PROVIDER === 'abs-h') { diff --git a/Composer/packages/form-dialogs/src/FormDialogSchemaEditor.tsx b/Composer/packages/form-dialogs/src/FormDialogSchemaEditor.tsx index 132d6396ba..7d26de0d6f 100644 --- a/Composer/packages/form-dialogs/src/FormDialogSchemaEditor.tsx +++ b/Composer/packages/form-dialogs/src/FormDialogSchemaEditor.tsx @@ -25,6 +25,10 @@ export type FormDialogSchemaEditorProps = { * Record of available schema templates. */ templates?: string[]; + /** + * Indicates of caller is running generation logic. + */ + isGenerating?: boolean; /** * Callback for when the json schema update is updated. */ @@ -32,11 +36,19 @@ export type FormDialogSchemaEditorProps = { /** * Callback for generating dialog using current valid form dialog schema. */ - onGenerateDialog: (formDialogSchemaJson: string) => void; + onGenerateDialog: (schemaId: string) => void; }; const InternalFormDialogSchemaEditor = React.memo((props: FormDialogSchemaEditorProps) => { - const { editorId, schema, templates = [], schemaExtension = '.schema', onSchemaUpdated, onGenerateDialog } = props; + const { + editorId, + schema, + templates = [], + schemaExtension = '.schema', + isGenerating = false, + onSchemaUpdated, + onGenerateDialog, + } = props; const { setTemplates, reset, importSchemaString } = useHandlers(); @@ -63,6 +75,7 @@ const InternalFormDialogSchemaEditor = React.memo((props: FormDialogSchemaEditor return ( void; - onGenerateDialog: (formDialogSchemaJson: string) => void; + onGenerateDialog: (schemaId: string) => void; }; export const FormDialogPropertiesEditor = React.memo((props: Props) => { - const { onReset, onGenerateDialog, schemaExtension } = props; + const { onReset, onGenerateDialog, schemaExtension, isGenerating } = props; const schema = useRecoilValue(formDialogSchemaAtom); const propertyIds = useRecoilValue(allFormDialogPropertyIdsSelector); @@ -88,6 +97,10 @@ export const FormDialogPropertiesEditor = React.memo((props: Props) => { importSchema({ id: schema.id, file }); }; + const generateDialog = React.useCallback(() => { + onGenerateDialog(schema.name); + }, [onGenerateDialog, schema]); + const menuItems: ICommandBarItemProps[] = [ { key: 'add', @@ -95,13 +108,14 @@ export const FormDialogPropertiesEditor = React.memo((props: Props) => { iconProps: { iconName: 'Add' }, title: formatMessage('Add Property'), ariaLabel: formatMessage('Add Property'), + disabled: isGenerating, onClick: () => { addProperty(); }, }, { key: 'import', - onRender: () => , + onRender: () => , }, { key: 'export', @@ -109,7 +123,7 @@ export const FormDialogPropertiesEditor = React.memo((props: Props) => { text: formatMessage('Export JSON'), title: formatMessage('Export JSON'), ariaLabel: formatMessage('Export JSON'), - disabled: !propertyIds.length || !schemaValid, + disabled: isGenerating || !propertyIds.length || !schemaValid, onClick: () => { downloadFile(schema.name, schemaExtension, schemaJson); }, @@ -120,6 +134,7 @@ export const FormDialogPropertiesEditor = React.memo((props: Props) => { text: formatMessage('Clear all'), title: formatMessage('Clear all'), ariaLabel: formatMessage('Clear all'), + disabled: isGenerating, onClick: () => { if (confirm(formatMessage('Are you sure you want to start over? Your progress will be lost.'))) { onReset(); @@ -128,17 +143,13 @@ export const FormDialogPropertiesEditor = React.memo((props: Props) => { }, ]; - const generateDialog = React.useCallback(() => { - onGenerateDialog(schemaJson); - }, [schemaJson, onGenerateDialog]); - const farItems: ICommandBarItemProps[] = [ { key: 'generate', onRender: () => ( { {schema.name} + {isGenerating && } diff --git a/Composer/packages/form-dialogs/src/components/common/CommandBarUpload.tsx b/Composer/packages/form-dialogs/src/components/common/CommandBarUpload.tsx index d609fe482c..c0278bcb07 100644 --- a/Composer/packages/form-dialogs/src/components/common/CommandBarUpload.tsx +++ b/Composer/packages/form-dialogs/src/components/common/CommandBarUpload.tsx @@ -6,6 +6,10 @@ import { CommandBarButton } from 'office-ui-fabric-react/lib/Button'; import * as React from 'react'; type Props = { + /** + * Indicates if the button should be disabled. + */ + disabled: boolean; /** * Allowed files extension. */ @@ -17,7 +21,7 @@ type Props = { }; export const CommandBarUploadButton = (props: Props) => { - const { onUpload, accept } = props; + const { onUpload, accept, disabled } = props; const inputFileRef = React.useRef(); const onClickImport = () => { @@ -36,6 +40,7 @@ export const CommandBarUploadButton = (props: Props) => { <> { schemaExtension=".form-dialog" templates={templates} // eslint-disable-next-line no-console - onGenerateDialog={(schema) => console.log(schema)} + onGenerateDialog={() => {}} onSchemaUpdated={onUpdateItem} /> diff --git a/Composer/packages/lib/indexers/src/utils/fileExtensions.ts b/Composer/packages/lib/indexers/src/utils/fileExtensions.ts index bb77af52c8..0022053659 100644 --- a/Composer/packages/lib/indexers/src/utils/fileExtensions.ts +++ b/Composer/packages/lib/indexers/src/utils/fileExtensions.ts @@ -3,6 +3,7 @@ export enum FileExtensions { Dialog = '.dialog', + FormDialog = '.form-dialog', DialogSchema = '.schema', Lu = '.lu', QnA = '.qna', diff --git a/Composer/packages/lib/shared/src/types/indexers.ts b/Composer/packages/lib/shared/src/types/indexers.ts index 9416d09c0e..4152d445a3 100644 --- a/Composer/packages/lib/shared/src/types/indexers.ts +++ b/Composer/packages/lib/shared/src/types/indexers.ts @@ -182,6 +182,7 @@ export type BotAssets = { skillManifests: SkillManifest[]; setting: DialogSetting; dialogSchemas: DialogSchemaFile[]; + formDialogSchemas: FormDialogSchema[]; }; export interface BotInfo { @@ -189,3 +190,13 @@ export interface BotInfo { diagnostics: Diagnostic[]; name: string; } + +export type FormDialogSchema = { + id: string; + content: string; +}; + +export type FormDialogSchemaTemplate = { + name: string; + isGlobal: boolean; +}; diff --git a/Composer/packages/server/src/locales/en-US.json b/Composer/packages/server/src/locales/en-US.json index 2c0001ce63..fdd9a2ac27 100644 --- a/Composer/packages/server/src/locales/en-US.json +++ b/Composer/packages/server/src/locales/en-US.json @@ -17,6 +17,9 @@ "a_dialog_file_must_have_a_name_123ff67d": { "message": "a dialog file must have a name" }, + "a_form_dialog_enables_your_bot_to_collect_pieces_o_fdd3fe56": { + "message": "A form dialog enables your bot to collect pieces of information ." + }, "a_minimap_gives_an_overview_of_your_source_code_fo_9a897f4f": { "message": "A minimap gives an overview of your source code for quick navigation and code understanding." }, @@ -557,6 +560,9 @@ "create_a_new_dialog_21d84b82": { "message": "Create a new dialog" }, + "create_a_new_form_dialog_schema_by_clicking_above_34b80531": { + "message": "Create a new form dialog schema by clicking + above." + }, "create_a_new_skill_manifest_or_select_which_one_yo_a97e9616": { "message": "Create a new skill manifest or select which one you want to edit" }, @@ -572,6 +578,9 @@ "create_folder_error_38aa86f5": { "message": "Create Folder Error" }, + "create_form_dialog_bb98a4f2": { + "message": "Create form dialog" + }, "create_from_knowledge_base_qna_maker_7422486": { "message": "Create from knowledge base (QnA Maker)" }, @@ -740,6 +749,9 @@ "dialog_started_912507c": { "message": "Dialog started" }, + "dialog_with_the_name_value_already_exists_62838518": { + "message": "Dialog with the name: { value } already exists." + }, "dialogfactory_missing_schema_5c3255c4": { "message": "DialogFactory missing schema." }, @@ -788,12 +800,6 @@ "duplicated_intents_recognized_d3908424": { "message": "Duplicated intents recognized" }, - "each_page_of_pagesizestring_in_propstring_a7e23b00": { - "message": "Each page of { pageSizeString } in { propString }" - }, - "each_value_in_ae285cd9": { - "message": "Each value in" - }, "early_adopters_e8db7999": { "message": "Early adopters" }, @@ -920,9 +926,6 @@ "error_processing_schema_2c707cf3": { "message": "Error Processing Schema" }, - "event_48c2be6e": { - "message": " (Event)" - }, "event_activity_2067a94b": { "message": "Event activity" }, @@ -1028,6 +1031,9 @@ "for_each_page_3b4d4b69": { "message": "For Each Page" }, + "form_dialogs_85a31b65": { + "message": "Form Dialogs" + }, "form_editor_7c2b02f0": { "message": "form editor" }, @@ -1049,6 +1055,9 @@ "generate_dialog_b80a85b2": { "message": "Generate dialog" }, + "generating_form_dialog_using_schemaid_schema_faile_8062f953": { + "message": "Generating form dialog using ${ schemaId } schema failed." + }, "get_a_new_copy_of_the_runtime_code_84970bf": { "message": "Get a new copy of the runtime code" }, @@ -1088,9 +1097,6 @@ "home_351838cd": { "message": "Home" }, - "host_ef9acfae": { - "message": "Host " - }, "http_request_79847109": { "message": "HTTP Request" }, @@ -1295,6 +1301,9 @@ "list_view_e33843f0": { "message": "List view" }, + "load_form_dialog_schema_templates_error_6a348e76": { + "message": "Load form dialog schema templates Error" + }, "loading_25990131": { "message": "Loading..." }, @@ -1532,18 +1541,21 @@ "no_extensions_installed_4b925277": { "message": "No extensions installed" }, + "no_form_dialog_schema_matches_your_filtering_crite_a198cb62": { + "message": "No form dialog schema matches your filtering criteria!" + }, "no_lu_file_with_name_id_fb21315d": { "message": "NO LU FILE WITH NAME { id }" }, "no_lu_or_qna_file_with_name_id_21cfe9dc": { "message": "NO LU OR QNA FILE WITH NAME { id }" }, - "no_search_results_1ba50423": { - "message": "No search results" - }, "no_name_e082310e": { "message": "[no name]" }, + "no_search_results_1ba50423": { + "message": "No search results" + }, "no_updates_available_cecd904d": { "message": "No updates available" }, @@ -1922,15 +1934,6 @@ "restart_bot_34e36428": { "message": "Restart Bot" }, - "result_b1f14e91": { - "message": " = Result" - }, - "result_property_100ef34f": { - "message": " = Result property" - }, - "return_value_b386d1b": { - "message": "= Return value" - }, "review_and_generate_63dec712": { "message": "Review and generate" }, @@ -1964,6 +1967,12 @@ "schema_24739a48": { "message": "Schema" }, + "schemanum_plural_0_no_schemas_1_one_schema_other_s_b3523dc": { + "message": "{ schemaNum, plural,\n =0 {No schemas}\n =1 {One schema}\n other {# schemas}\n} have been found.\n { schemaNum, select,\n 0 {}\n other {Press down arrow key to navigate the search results}\n}" + }, + "schemas_74566170": { + "message": "Schemas" + }, "scripts_successfully_updated_3a75d57f": { "message": "Scripts successfully updated." }, @@ -2207,9 +2216,6 @@ "the_return_type_does_not_match_2ae72548": { "message": "the return type does not match" }, - "the_root_bot_is_not_a_bot_project_d1495cf6": { - "message": "The root bot is not a bot project" - }, "the_welcome_message_is_triggered_by_the_i_conversa_a3ff58f8": { "message": "The welcome message is triggered by the ConversationUpdate event. To add a new ConversationUpdate trigger:" }, @@ -2312,9 +2318,6 @@ "toggle_extension_e41de2d2": { "message": "Toggle extension" }, - "token_property_e53a19d9": { - "message": " = Token Property" - }, "toolbar_bafd4228": { "message": "toolbar" }, @@ -2354,6 +2357,9 @@ "type_dialog_name_14a50769": { "message": "Type dialog name" }, + "type_form_dialog_schema_name_b767985c": { + "message": "Type form dialog schema name" + }, "typing_activity_6b634ae": { "message": "Typing activity" }, diff --git a/Composer/packages/server/src/models/bot/botProject.ts b/Composer/packages/server/src/models/bot/botProject.ts index 2dacd7b9c4..2c1d2f3023 100644 --- a/Composer/packages/server/src/models/bot/botProject.ts +++ b/Composer/packages/server/src/models/bot/botProject.ts @@ -83,6 +83,17 @@ export class BotProject implements IBotProject { return files; } + public get formDialogSchemaFiles() { + const files: FileInfo[] = []; + this.files.forEach((file) => { + if (file.name.endsWith('.form-dialog')) { + files.push(file); + } + }); + + return files; + } + public get dialogSchemaFiles() { const files: FileInfo[] = []; this.files.forEach((file) => { @@ -673,6 +684,7 @@ export class BotProject implements IBotProject { const patterns = [ '**/*.dialog', '**/*.dialog.schema', + '**/*.form-dialog', '**/*.lg', '**/*.lu', '**/*.qna', diff --git a/Composer/yarn.lock b/Composer/yarn.lock index 61148f14ba..fc0f124b6f 100644 --- a/Composer/yarn.lock +++ b/Composer/yarn.lock @@ -882,8 +882,8 @@ "@babel/parser@^7.0.0", "@babel/parser@^7.1.0", "@babel/parser@^7.10.4", "@babel/parser@^7.10.5", "@babel/parser@^7.11.3", "@babel/parser@^7.11.5", "@babel/parser@^7.2.2", "@babel/parser@^7.3.4", "@babel/parser@^7.4.0", "@babel/parser@^7.7.0", "@babel/parser@^7.7.4", "@babel/parser@^7.7.5", "@babel/parser@^7.8.6", "@babel/parser@^7.9.0", "@babel/parser@^7.9.6": version "7.11.5" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.11.5.tgz#c7ff6303df71080ec7a4f5b8c003c58f1cf51037" - integrity sha512-X9rD8qqm695vgmeaQ4fvz/o3+Wk4ZzQvSHkDBgpYKxpD4qTAUm88ZKtHkVqIOsYFFbIQ6wQYhC6q7pjqVK0E0Q== + resolved "https://botbuilder.myget.org/F/botbuilder-v4-js-daily/npm/@babel/parser/-/@babel/parser-7.11.5.tgz#c7ff6303df71080ec7a4f5b8c003c58f1cf51037" + integrity sha1-x/9jA99xCA7HpPW4wAPFjxz1EDc= "@babel/plugin-proposal-async-generator-functions@^7.2.0": version "7.2.0" @@ -5579,8 +5579,8 @@ binary-extensions@^2.0.0: bl@^2.2.1, bl@^4.0.3: version "2.2.1" - resolved "https://registry.yarnpkg.com/bl/-/bl-2.2.1.tgz#8c11a7b730655c5d56898cdc871224f40fd901d5" - integrity sha512-6Pesp1w0DEX1N550i/uGV/TqucVL4AM/pgThFSN/Qq9si1/DF9aIHs1BxD8V/QU0HoeHO6cQRTAuYnLPKq1e4g== + resolved "https://botbuilder.myget.org/F/botbuilder-v4-js-daily/npm/bl/-/bl-2.2.1.tgz#8c11a7b730655c5d56898cdc871224f40fd901d5" + integrity sha1-jBGntzBlXF1WiYzchxIk9A/ZAdU= dependencies: readable-stream "^2.3.5" safe-buffer "^5.1.1" @@ -8625,8 +8625,8 @@ elegant-spinner@^1.0.1: elliptic@^6.0.0, elliptic@^6.5.3: version "6.5.3" - resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.3.tgz#cb59eb2efdaf73a0bd78ccd7015a62ad6e0f93d6" - integrity sha512-IMqzv5wNQf+E6aHeIqATs0tOLeOTwj1QKbRcS3jBbYkl5oLAserA8yJTT7/VyHUYG91PRmPyeQDObKLPpeS4dw== + resolved "https://botbuilder.myget.org/F/botbuilder-v4-js-daily/npm/elliptic/-/elliptic-6.5.3.tgz#cb59eb2efdaf73a0bd78ccd7015a62ad6e0f93d6" + integrity sha1-y1nrLv2vc6C9eMzXAVpirW4Pk9Y= dependencies: bn.js "^4.4.0" brorand "^1.0.1" @@ -12718,8 +12718,8 @@ killable@^1.0.1: kind-of@^2.0.1, kind-of@^3.0.2, kind-of@^3.0.3, kind-of@^3.2.0, kind-of@^4.0.0, kind-of@^5.0.0, kind-of@^6.0.0, kind-of@^6.0.2, kind-of@^6.0.3: version "6.0.3" - resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd" - integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw== + resolved "https://botbuilder.myget.org/F/botbuilder-v4-js-daily/npm/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd" + integrity sha1-B8BQNKbDSfoG4k+jWqdttFgM5N0= kleur@^3.0.2: version "3.0.2" @@ -13133,8 +13133,8 @@ lodash.uniq@^4.5.0: "lodash@>=3.5 <5", lodash@^4.0.0, lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.12, lodash@^4.17.13, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.3, lodash@^4.17.4, lodash@^4.17.5, lodash@^4.3.0: version "4.17.20" - resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.20.tgz#b44a9b6297bcb698f1c51a3545a2b3b368d59c52" - integrity sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA== + resolved "https://botbuilder.myget.org/F/botbuilder-v4-js-daily/npm/lodash/-/lodash-4.17.20.tgz#b44a9b6297bcb698f1c51a3545a2b3b368d59c52" + integrity sha1-tEqbYpe8tpjxxRo1RaKzs2jVnFI= log-driver@^1.2.7: version "1.2.7" @@ -13661,8 +13661,8 @@ mixin-object@^2.0.1: mkdirp@0.5.1, mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@^0.5.2, mkdirp@^0.5.3, mkdirp@^0.5.4, mkdirp@^1.0.3, mkdirp@~0.5.1: version "0.5.5" - resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def" - integrity sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ== + resolved "https://botbuilder.myget.org/F/botbuilder-v4-js-daily/npm/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def" + integrity sha1-2Rzv1i0UNsoPQWIOJRKI1CAJne8= dependencies: minimist "^1.2.5" @@ -16343,8 +16343,8 @@ read-text-file@^1.1.0: readable-stream@^2.3.5: version "2.3.7" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57" - integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw== + resolved "https://botbuilder.myget.org/F/botbuilder-v4-js-daily/npm/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57" + integrity sha1-Hsoc9xGu+BTAT2IlKjamL2yyO1c= dependencies: core-util-is "~1.0.0" inherits "~2.0.3" @@ -17156,8 +17156,8 @@ select-hose@^2.0.0: selfsigned@1.10.8, selfsigned@^1.10.7: version "1.10.8" - resolved "https://registry.yarnpkg.com/selfsigned/-/selfsigned-1.10.8.tgz#0d17208b7d12c33f8eac85c41835f27fc3d81a30" - integrity sha512-2P4PtieJeEwVgTU9QEcwIRDQ/mXJLX8/+I3ur+Pg16nS8oNbrGxEso9NyYWy8NAmXiNl4dlAp5MwoNeCWzON4w== + resolved "https://botbuilder.myget.org/F/botbuilder-v4-js-daily/npm/selfsigned/-/selfsigned-1.10.8.tgz#0d17208b7d12c33f8eac85c41835f27fc3d81a30" + integrity sha1-DRcgi30Swz+OrIXEGDXyf8PYGjA= dependencies: node-forge "^0.10.0" @@ -17300,8 +17300,8 @@ set-blocking@^2.0.0, set-blocking@~2.0.0: set-value@^0.4.3, set-value@^2.0.0, set-value@^3.0.2: version "3.0.2" - resolved "https://registry.yarnpkg.com/set-value/-/set-value-3.0.2.tgz#74e8ecd023c33d0f77199d415409a40f21e61b90" - integrity sha512-npjkVoz+ank0zjlV9F47Fdbjfj/PfXyVhZvGALWsyIYU/qrMzpi6avjKW3/7KeSU2Df3I46BrN1xOI1+6vW0hA== + resolved "https://botbuilder.myget.org/F/botbuilder-v4-js-daily/npm/set-value/-/set-value-3.0.2.tgz#74e8ecd023c33d0f77199d415409a40f21e61b90" + integrity sha1-dOjs0CPDPQ93GZ1BVAmkDyHmG5A= dependencies: is-plain-object "^2.0.4" @@ -18323,8 +18323,8 @@ terminal-link@^2.0.0: terser-webpack-plugin@2.3.7, terser-webpack-plugin@^1.4.3, terser-webpack-plugin@^2.3.7: version "2.3.8" - resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-2.3.8.tgz#894764a19b0743f2f704e7c2a848c5283a696724" - integrity sha512-/fKw3R+hWyHfYx7Bv6oPqmk4HGQcrWLtV3X6ggvPuwPNHSnzvVV51z6OaaCOus4YLjutYGOz3pEpbhe6Up2s1w== + resolved "https://botbuilder.myget.org/F/botbuilder-v4-js-daily/npm/terser-webpack-plugin/-/terser-webpack-plugin-2.3.8.tgz#894764a19b0743f2f704e7c2a848c5283a696724" + integrity sha1-iUdkoZsHQ/L3BOfCqEjFKDppZyQ= dependencies: cacache "^13.0.1" find-cache-dir "^3.3.1" From bbb13fece080dc5eba1a884f5557d4e255a58166 Mon Sep 17 00:00:00 2001 From: Soroush Date: Mon, 12 Oct 2020 13:33:33 -0700 Subject: [PATCH 2/8] form dialog UX --- .../src/pages/form-dialog/FormDialogPage.tsx | 105 +- .../form-dialog/FormDialogSchemaList.tsx | 208 +- .../FormDialogSchemaListHeader.tsx | 4 +- .../VisualFormDialogSchemaEditor.tsx | 9 +- .../pages/notifications/useNotifications.tsx | 6 +- .../src/recoilModel/DispatcherWrapper.tsx | 5 +- .../client/src/recoilModel/atoms/appState.ts | 12 +- .../client/src/recoilModel/atoms/botState.ts | 36 +- .../recoilModel/dispatchers/formDialogs.ts | 15 +- .../src/recoilModel/dispatchers/project.ts | 9 + .../recoilModel/dispatchers/utils/project.ts | 107 +- .../src/recoilModel/selectors/project.ts | 21 +- Composer/packages/client/src/router.tsx | 4 +- .../packages/client/src/utils/pageLinks.ts | 6 +- Composer/packages/form-dialogs/package.json | 3 +- .../components/FormDialogPropertiesEditor.tsx | 25 +- .../property/FormDialogPropertyCard.tsx | 13 +- .../indexers/src/formDialogSchemaIndexer.ts | 17 + Composer/packages/lib/indexers/src/index.ts | 3 + .../packages/server/src/locales/en-US.json | 2601 ++++++++++++++++- Composer/plugins/azurePublish/yarn.lock | 54 +- 21 files changed, 3017 insertions(+), 246 deletions(-) create mode 100644 Composer/packages/lib/indexers/src/formDialogSchemaIndexer.ts diff --git a/Composer/packages/client/src/pages/form-dialog/FormDialogPage.tsx b/Composer/packages/client/src/pages/form-dialog/FormDialogPage.tsx index 8e09fc3c39..e0096cd1f9 100644 --- a/Composer/packages/client/src/pages/form-dialog/FormDialogPage.tsx +++ b/Composer/packages/client/src/pages/form-dialog/FormDialogPage.tsx @@ -2,92 +2,78 @@ // Licensed under the MIT License. import styled from '@emotion/styled'; -import { RouteComponentProps } from '@reach/router'; +import { navigate, RouteComponentProps } from '@reach/router'; +import formatMessage from 'format-message'; import { Stack } from 'office-ui-fabric-react/lib/Stack'; +import { Text } from 'office-ui-fabric-react/lib/Text'; import * as React from 'react'; import { useRecoilValue } from 'recoil'; +import { OpenConfirmModal } from '../../components/Modal/ConfirmDialog'; +import { LeftRightSplit } from '../../components/Split/LeftRightSplit'; import { dispatcherState, formDialogGenerationProgressingState, formDialogLibraryTemplatesState, - formDialogSchemasState, + formDialogSchemaIdsState, } from '../../recoilModel'; -import { LeftRightSplit } from '../../components/Split/LeftRightSplit'; -import { VisualFormDialogSchemaEditor } from './VisualFormDialogSchemaEditor'; -import { FormDialogSchemaList } from './FormDialogSchemaList'; import CreateFormDialogSchemaModal from './CreateFormDialogSchemaModal'; +import { FormDialogSchemaList } from './FormDialogSchemaList'; +import { VisualFormDialogSchemaEditor } from './VisualFormDialogSchemaEditor'; const EmptyView = styled(Stack)({ - position: 'relative', width: '100%', - ':after': { - fontSize: 20, - content: '"Select an schema to edit or create a new one"', - position: 'absolute', - textAlign: 'center', - top: '50%', - left: '50%', - transform: 'translate(-50%, -50%)', - opacity: 0.5, - }, + opacity: 0.5, }); type Props = RouteComponentProps<{ projectId: string; schemaId: string }>; const FormDialogPage: React.FC = React.memo((props: Props) => { const { projectId = '', schemaId = '' } = props; - const formDialogSchemas = useRecoilValue(formDialogSchemasState(projectId)); + const formDialogSchemaIds = useRecoilValue(formDialogSchemaIdsState(projectId)); const formDialogLibraryTemplates = useRecoilValue(formDialogLibraryTemplatesState); const formDialogGenerationProgressing = useRecoilValue(formDialogGenerationProgressingState); const { - loadFormDialogSchemaTemplates, removeFormDialogSchema, generateFormDialog, createFormDialogSchema, updateFormDialogSchema, + navigateToGeneratedDialog, } = useRecoilValue(dispatcherState); - React.useEffect(() => { - (async () => { - await loadFormDialogSchemaTemplates(); - })(); - }, []); - const { 0: createSchemaDialogOpen, 1: setCreateSchemaDialogOpen } = React.useState(false); - const { 0: selectedFormDialogSchemaId, 1: setSelectedFormDialogSchemaId } = React.useState(schemaId); const availableTemplates = React.useMemo( () => formDialogLibraryTemplates.filter((t) => !t.isGlobal).map((t) => t.name), [formDialogLibraryTemplates] ); - const selectedFormDialogSchema = React.useMemo( - () => formDialogSchemas.find((fds) => fds.id === selectedFormDialogSchemaId), - [formDialogSchemas, selectedFormDialogSchemaId] - ); + const validSchemaId = React.useMemo(() => formDialogSchemaIds.includes(schemaId), [formDialogSchemaIds, schemaId]); const createItemStart = React.useCallback(() => setCreateSchemaDialogOpen(true), [setCreateSchemaDialogOpen]); - const selectItem = React.useCallback( - (id: string) => { - setSelectedFormDialogSchemaId(id); - }, - [setSelectedFormDialogSchemaId] - ); + const selectItem = React.useCallback((id: string) => { + navigate(`/bot/${projectId}/forms/${id}`); + }, []); const deleteItem = React.useCallback( - (id: string) => { - removeFormDialogSchema({ id, projectId }); - if (selectedFormDialogSchemaId === id) { - selectItem(''); + async (id: string) => { + const res = await OpenConfirmModal( + formatMessage('Delete form dialog schema'), + formatMessage('Are you sure you want to remove form dialog schema "{id}"?', { id }) + ); + if (res) { + removeFormDialogSchema({ id, projectId }); + if (schemaId === id) { + selectItem(''); + } } }, - [selectItem, removeFormDialogSchema, selectedFormDialogSchemaId] + [selectItem, removeFormDialogSchema, schemaId] ); - const generateFormDialogs = React.useCallback( + const generateDialog = React.useCallback( (schemaId: string) => { if (schemaId) { generateFormDialog({ projectId, schemaId }); @@ -96,13 +82,22 @@ const FormDialogPage: React.FC = React.memo((props: Props) => { [generateFormDialog, projectId] ); + const viewDialog = React.useCallback( + (schemaId: string) => { + if (schemaId) { + navigateToGeneratedDialog({ projectId, schemaId }); + } + }, + [navigateToGeneratedDialog, projectId] + ); + const updateItem = React.useCallback( (id: string, content: string) => { - if (id === selectedFormDialogSchemaId) { + if (id === schemaId) { updateFormDialogSchema({ id, content, projectId }); } }, - [updateFormDialogSchema, selectedFormDialogSchemaId] + [updateFormDialogSchema, schemaId] ); const createItem = React.useCallback( @@ -118,25 +113,35 @@ const FormDialogPage: React.FC = React.memo((props: Props) => { - {selectedFormDialogSchema ? ( + {validSchemaId ? ( ) : ( - + + + {schemaId + ? formatMessage(`{schemaId} doesn't exists, select an schema to edit or create a new one`, { + schemaId, + }) + : formatMessage('Select an schema to edit or create a new one')} + + )} diff --git a/Composer/packages/client/src/pages/form-dialog/FormDialogSchemaList.tsx b/Composer/packages/client/src/pages/form-dialog/FormDialogSchemaList.tsx index 49a644f145..710a6d436e 100644 --- a/Composer/packages/client/src/pages/form-dialog/FormDialogSchemaList.tsx +++ b/Composer/packages/client/src/pages/form-dialog/FormDialogSchemaList.tsx @@ -1,18 +1,21 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -import { FormDialogSchema } from '@bfc/shared'; import styled from '@emotion/styled'; +import { NeutralColors } from '@uifabric/fluent-theme'; import { DefaultPalette } from '@uifabric/styling'; import formatMessage from 'format-message'; import debounce from 'lodash/debounce'; -import { IconButton } from 'office-ui-fabric-react/lib/Button'; +import { CommandBarButton, IconButton } from 'office-ui-fabric-react/lib/Button'; import { FocusZone, FocusZoneDirection } from 'office-ui-fabric-react/lib/FocusZone'; -import { Icon } from 'office-ui-fabric-react/lib/Icon'; -import { List } from 'office-ui-fabric-react/lib/List'; +import { IOverflowSetItemProps, OverflowSet } from 'office-ui-fabric-react/lib/OverflowSet'; import { IStackItemProps, IStackItemStyles, Stack } from 'office-ui-fabric-react/lib/Stack'; +import { DirectionalHint, TooltipHost } from 'office-ui-fabric-react/lib/Tooltip'; import { classNamesFunction } from 'office-ui-fabric-react/lib/Utilities'; import * as React from 'react'; +import { useRecoilValue } from 'recoil'; + +import { formDialogSchemaDialogExistsSelector, formDialogSchemaState } from '../../recoilModel'; import { FormDialogSchemaListHeader } from './FormDialogSchemaListHeader'; @@ -58,6 +61,12 @@ const oneLinerStyles = classNamesFunction()({ }, }); +const ItemRoot = styled(Stack)(({ selected }: { selected: boolean }) => ({ + padding: '0 12px', + cursor: 'pointer', + background: selected ? DefaultPalette.neutralLighter : 'transparent', +})); + const EmptyView = styled(Stack)({ opacity: 0.5, padding: 8, @@ -65,93 +74,148 @@ const EmptyView = styled(Stack)({ textAlign: 'center', }); -type SchemaItemProps = { - id: string; +type FormDialogSchemaItemProps = { + projectId: string; + schemaId: string; selected: boolean; - isEmpty?: boolean; - onClick: (name: string) => void; - onDelete: (name: string) => void; - onGenerate: (name: string) => void; + onClick: (schemaId: string) => void; + onDelete: (schemaId: string) => void; + onGenerate: (schemaId: string) => void; + onViewDialog: (schemaId: string) => void; }; -const FormDialogSchemaItem = React.memo((props: SchemaItemProps) => { - const { id, onClick, onDelete, onGenerate, selected = false, isEmpty = false } = props; +const FormDialogSchemaItem = React.memo((props: FormDialogSchemaItemProps) => { + const { projectId, schemaId, onClick, onDelete, onGenerate, selected = false, onViewDialog } = props; + + const item = useRecoilValue(formDialogSchemaState({ projectId, schemaId })); + const viewDialogActionDisabled = !useRecoilValue(formDialogSchemaDialogExistsSelector({ projectId, schemaId })); + const generateActionDisabled = item?.content === '' || isEmptyObject(item?.content); const clickHandler = React.useCallback(() => { - onClick(id); - }, [id, onClick]); + onClick(schemaId); + }, [schemaId, onClick]); const deleteHandler = React.useCallback( (e: React.MouseEvent) => { - e.preventDefault(); e.stopPropagation(); - onDelete(id); + onDelete(schemaId); + }, + [schemaId, onDelete] + ); + + const generateDialog = React.useCallback( + (e: React.MouseEvent) => { + e.stopPropagation(); + + onGenerate(schemaId); }, - [id, onDelete] + [schemaId, onGenerate] ); - const runHandler = React.useCallback( + const viewDialog = React.useCallback( (e: React.MouseEvent) => { - e.preventDefault(); e.stopPropagation(); - onGenerate(id); + onViewDialog(schemaId); }, - [id, onGenerate] + [schemaId, onViewDialog] + ); + + const renderOverflowItem = React.useCallback( + (item: IOverflowSetItemProps) => , + [] + ); + + const renderOverflowButton = React.useCallback( + (overflowItems?: IOverflowSetItemProps[]) => ( + + + + ), + [] ); return ( - - - {id} + {schemaId} - - - - - + + ); }); type FormDialogSchemaListProps = { - items: readonly FormDialogSchema[]; + projectId: string; + items: readonly string[]; selectedId: string | undefined; + loading?: boolean; onSelectItem: (schemaId: string) => void; onDeleteItem: (schemaId: string) => void; onGenerate: (schemaId: string) => void; + onViewDialog: (schemaId: string) => void; onCreateItem: () => void; - loading?: boolean; }; export const FormDialogSchemaList: React.FC = React.memo((props) => { - const { selectedId, items, onDeleteItem, onSelectItem, onCreateItem, onGenerate, loading = false } = props; + const { + projectId, + selectedId, + items, + onDeleteItem, + onSelectItem, + onCreateItem, + onGenerate, + onViewDialog, + loading = false, + } = props; const { 0: query, 1: setQuery } = React.useState(''); const delayedSetQuery = debounce((newValue) => setQuery(newValue), 300); const filteredItems = React.useMemo(() => { - return items.filter(({ id }) => id.toLowerCase().indexOf(query.toLowerCase()) !== -1); - }, [query, items, selectedId]); + return items.filter((item) => item.toLowerCase().indexOf(query.toLowerCase()) !== -1); + }, [query, items]); const onFilter = (_e?: React.ChangeEvent, newValue?: string): void => { if (typeof newValue === 'string') { @@ -159,16 +223,17 @@ export const FormDialogSchemaList: React.FC = React.m } }; - const renderCell = React.useCallback( - (item) => ( + const renderItem = React.useCallback( + (itemId) => ( ), [selectedId, onSelectItem, onDeleteItem, onGenerate] @@ -182,34 +247,35 @@ export const FormDialogSchemaList: React.FC = React.m onChangeQuery={onFilter} onCreateItem={onCreateItem} /> - -
- {filteredItems.length ? ( - items={filteredItems} onRenderCell={renderCell} /> - ) : ( - - {query - ? formatMessage('No form dialog schema matches your filtering criteria!') - : formatMessage('Create a new form dialog schema by clicking + above.')} - + { itemCount: items.length } )} - + aria-live="polite" + /> + {filteredItems.length ? ( + + {filteredItems.map(renderItem)} + + ) : ( + + {query + ? formatMessage('No form dialog schema matches your filtering criteria!') + : formatMessage('Create a new form dialog schema by clicking + above.')} + + )} ); }); diff --git a/Composer/packages/client/src/pages/form-dialog/FormDialogSchemaListHeader.tsx b/Composer/packages/client/src/pages/form-dialog/FormDialogSchemaListHeader.tsx index d3300c7ec2..b991de0d21 100644 --- a/Composer/packages/client/src/pages/form-dialog/FormDialogSchemaListHeader.tsx +++ b/Composer/packages/client/src/pages/form-dialog/FormDialogSchemaListHeader.tsx @@ -6,10 +6,10 @@ import formatMessage from 'format-message'; import { IconButton } from 'office-ui-fabric-react/lib/Button'; import { Label } from 'office-ui-fabric-react/lib/Label'; import { ProgressIndicator } from 'office-ui-fabric-react/lib/ProgressIndicator'; +import { ISearchBoxProps, ISearchBoxStyles, SearchBox } from 'office-ui-fabric-react/lib/SearchBox'; import { Stack } from 'office-ui-fabric-react/lib/Stack'; -import * as React from 'react'; -import { SearchBox, ISearchBoxProps, ISearchBoxStyles } from 'office-ui-fabric-react/lib/SearchBox'; import { classNamesFunction } from 'office-ui-fabric-react/lib/Utilities'; +import * as React from 'react'; const TitleBar = styled(Stack)({ flex: 1, diff --git a/Composer/packages/client/src/pages/form-dialog/VisualFormDialogSchemaEditor.tsx b/Composer/packages/client/src/pages/form-dialog/VisualFormDialogSchemaEditor.tsx index edcb81881f..9f8747eaa3 100644 --- a/Composer/packages/client/src/pages/form-dialog/VisualFormDialogSchemaEditor.tsx +++ b/Composer/packages/client/src/pages/form-dialog/VisualFormDialogSchemaEditor.tsx @@ -10,6 +10,9 @@ import { ActionButton } from 'office-ui-fabric-react/lib/Button'; import { IStackProps, IStackStyles, Stack } from 'office-ui-fabric-react/lib/Stack'; import { classNamesFunction } from 'office-ui-fabric-react/lib/Utilities'; import * as React from 'react'; +import { useRecoilValue } from 'recoil'; + +import { formDialogSchemaState } from '../../recoilModel'; const Root = styled(Stack)<{ inProgress: boolean; @@ -46,14 +49,16 @@ const editorTopBarStyles = classNamesFunction()({ type Props = { projectId?: string; generationInProgress?: boolean; - schema: { id: string; content: string }; + schemaId: string; templates: string[]; onChange: (id: string, content: string) => void; onGenerate: (schemaId: string) => void; }; export const VisualFormDialogSchemaEditor = React.memo((props: Props) => { - const { projectId, schema, templates, onChange, onGenerate, generationInProgress = false } = props; + const { projectId, schemaId, templates, onChange, onGenerate, generationInProgress = false } = props; + + const schema = useRecoilValue(formDialogSchemaState({ projectId, schemaId })); // eslint-disable-next-line @typescript-eslint/no-explicit-any const editorRef = React.useRef(); diff --git a/Composer/packages/client/src/pages/notifications/useNotifications.tsx b/Composer/packages/client/src/pages/notifications/useNotifications.tsx index f85663b78e..fc14f6611d 100644 --- a/Composer/packages/client/src/pages/notifications/useNotifications.tsx +++ b/Composer/packages/client/src/pages/notifications/useNotifications.tsx @@ -9,15 +9,15 @@ import { useRecoilValue } from 'recoil'; import { botDiagnosticsState, + botProjectFileState, dialogSchemasState, - formDialogSchemasState, + formDialogSchemasSelectorFamily, lgFilesState, luFilesState, qnaFilesState, settingsState, skillManifestsState, validateDialogSelectorFamily, - botProjectFileState, } from '../../recoilModel'; import { getReferredLuFiles } from './../../utils/luUtil'; @@ -41,7 +41,7 @@ export default function useNotifications(projectId: string, filter?: string) { const skillManifests = useRecoilValue(skillManifestsState(projectId)); const dialogSchemas = useRecoilValue(dialogSchemasState(projectId)); const qnaFiles = useRecoilValue(qnaFilesState(projectId)); - const formDialogSchemas = useRecoilValue(formDialogSchemasState(projectId)); + const formDialogSchemas = useRecoilValue(formDialogSchemasSelectorFamily(projectId)); const botProjectFile = useRecoilValue(botProjectFileState(projectId)); const botAssets: BotAssets = { diff --git a/Composer/packages/client/src/recoilModel/DispatcherWrapper.tsx b/Composer/packages/client/src/recoilModel/DispatcherWrapper.tsx index 379ca5c2b0..8e02c39105 100644 --- a/Composer/packages/client/src/recoilModel/DispatcherWrapper.tsx +++ b/Composer/packages/client/src/recoilModel/DispatcherWrapper.tsx @@ -22,10 +22,9 @@ import { dialogSchemasState, settingsState, filePersistenceState, - formDialogSchemasState, botProjectFileState, } from './atoms'; -import { botsForFilePersistenceSelector } from './selectors'; +import { botsForFilePersistenceSelector, formDialogSchemasSelectorFamily } from './selectors'; const getBotAssets = async (projectId, snapshot: Snapshot): Promise => { const result = await Promise.all([ @@ -37,7 +36,7 @@ const getBotAssets = async (projectId, snapshot: Snapshot): Promise = snapshot.getPromise(settingsState(projectId)), snapshot.getPromise(dialogSchemasState(projectId)), snapshot.getPromise(botProjectFileState(projectId)), - snapshot.getPromise(formDialogSchemasState(projectId)), + snapshot.getPromise(formDialogSchemasSelectorFamily(projectId)), ]); return { projectId, diff --git a/Composer/packages/client/src/recoilModel/atoms/appState.ts b/Composer/packages/client/src/recoilModel/atoms/appState.ts index 1926d46b6a..906ffd8832 100644 --- a/Composer/packages/client/src/recoilModel/atoms/appState.ts +++ b/Composer/packages/client/src/recoilModel/atoms/appState.ts @@ -2,7 +2,7 @@ // Licensed under the MIT License. import { atom, atomFamily } from 'recoil'; -import { ProjectTemplate, UserSettings } from '@bfc/shared'; +import { FormDialogSchemaTemplate, ProjectTemplate, UserSettings } from '@bfc/shared'; import { ExtensionMetadata } from '@bfc/extension-client'; import { @@ -189,3 +189,13 @@ export const botOpeningState = atom({ key: getFullyQualifiedKey('botOpeningState'), default: false, }); + +export const formDialogLibraryTemplatesState = atom({ + key: getFullyQualifiedKey('formDialogLibraryTemplates'), + default: [], +}); + +export const formDialogGenerationProgressingState = atom({ + key: getFullyQualifiedKey('formDialogGenerationProgressing'), + default: false, +}); diff --git a/Composer/packages/client/src/recoilModel/atoms/botState.ts b/Composer/packages/client/src/recoilModel/atoms/botState.ts index abec4f27ed..adb1400a1e 100644 --- a/Composer/packages/client/src/recoilModel/atoms/botState.ts +++ b/Composer/packages/client/src/recoilModel/atoms/botState.ts @@ -1,28 +1,28 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -import { atom, atomFamily, selectorFamily } from 'recoil'; import { + BotProjectFile, + BotProjectSpace, + BotSchemas, + Diagnostic, DialogInfo, DialogSchemaFile, - Diagnostic, + DialogSetting, + FormDialogSchema, LgFile, LuFile, QnAFile, - BotSchemas, Skill, - DialogSetting, - FormDialogSchema, - FormDialogSchemaTemplate, - BotProjectSpace, - BotProjectFile, } from '@bfc/shared'; +import { atomFamily } from 'recoil'; import { BotLoadError, DesignPageLocation, QnAAllUpViewStatus } from '../../recoilModel/types'; import FilePersistence from '../persistence/FilePersistence'; -import { PublishType, BreadcrumbItem } from './../../recoilModel/types'; import { BotStatus } from './../../constants'; +import { BreadcrumbItem, PublishType } from './../../recoilModel/types'; + const getFullyQualifiedKey = (value: string) => { return `Bot_${value}_State`; }; @@ -275,24 +275,6 @@ export const formDialogSchemaState = atomFamily({ - key: getFullyQualifiedKey('formDialogSchemas'), - get: (projectId: string) => ({ get }) => { - const formDialogSchemaIds = get(formDialogSchemaIdsState(projectId)); - return formDialogSchemaIds.map((schemaId) => get(formDialogSchemaState({ projectId, schemaId }))); - }, -}); - -export const formDialogLibraryTemplatesState = atom({ - key: getFullyQualifiedKey('formDialogLibraryTemplates'), - default: [], -}); - -export const formDialogGenerationProgressingState = atom({ - key: getFullyQualifiedKey('formDialogGenerationProgressing'), - default: false, -}); - export const botProjectFileState = atomFamily({ key: getFullyQualifiedKey('botProjectFile'), default: { diff --git a/Composer/packages/client/src/recoilModel/dispatchers/formDialogs.ts b/Composer/packages/client/src/recoilModel/dispatchers/formDialogs.ts index f4a484f386..10de63d905 100644 --- a/Composer/packages/client/src/recoilModel/dispatchers/formDialogs.ts +++ b/Composer/packages/client/src/recoilModel/dispatchers/formDialogs.ts @@ -3,17 +3,17 @@ /* eslint-disable react-hooks/rules-of-hooks */ import { FormDialogSchemaTemplate } from '@bfc/shared'; +import { navigate } from '@reach/router'; import formatMessage from 'format-message'; import { CallbackInterface, useRecoilCallback } from 'recoil'; import httpClient from '../../utils/httpUtil'; -import { applicationErrorState } from '../atoms/appState'; import { + applicationErrorState, formDialogGenerationProgressingState, formDialogLibraryTemplatesState, - formDialogSchemaIdsState, - formDialogSchemaState, -} from '../atoms/botState'; +} from '../atoms/appState'; +import { formDialogSchemaIdsState, formDialogSchemaState } from '../atoms/botState'; import { dispatcherState } from '../DispatcherWrapper'; export const formDialogsDispatcher = () => { @@ -75,7 +75,7 @@ export const formDialogsDispatcher = () => { const response = await httpClient.post(`/formDialogs/${projectId}/generate`, { name: schemaId, }); - reloadProject(callbackHelpers, response.data); + reloadProject(callbackHelpers, response); } catch (error) { set(applicationErrorState, { message: error.message, @@ -87,11 +87,16 @@ export const formDialogsDispatcher = () => { } ); + const navigateToGeneratedDialog = ({ projectId, schemaId }) => { + navigate(`/bot/${projectId}/dialogs/${schemaId}`); + }; + return { createFormDialogSchema, updateFormDialogSchema, loadFormDialogSchemaTemplates, removeFormDialogSchema, generateFormDialog, + navigateToGeneratedDialog, }; }; diff --git a/Composer/packages/client/src/recoilModel/dispatchers/project.ts b/Composer/packages/client/src/recoilModel/dispatchers/project.ts index 4161322a8a..32c1e4f51d 100644 --- a/Composer/packages/client/src/recoilModel/dispatchers/project.ts +++ b/Composer/packages/client/src/recoilModel/dispatchers/project.ts @@ -34,6 +34,8 @@ import { flushExistingTasks, getSkillNameIdentifier, handleProjectFailure, + initBotState, + loadProjectData, navigateToBot, openLocalSkill, openRemoteSkill, @@ -340,6 +342,12 @@ export const projectDispatcher = () => { } }); + const reloadProject = async (callbackHelpers: CallbackInterface, response: any) => { + const { projectData, botFiles } = loadProjectData(response); + + await initBotState(callbackHelpers, projectData, botFiles); + }; + return { openProject, createNewBot, @@ -356,5 +364,6 @@ export const projectDispatcher = () => { addExistingSkillToBotProject, addRemoteSkillToBotProject, replaceSkillInBotProject, + reloadProject, }; }; diff --git a/Composer/packages/client/src/recoilModel/dispatchers/utils/project.ts b/Composer/packages/client/src/recoilModel/dispatchers/utils/project.ts index 4a1718dbe9..018cc8fb2d 100644 --- a/Composer/packages/client/src/recoilModel/dispatchers/utils/project.ts +++ b/Composer/packages/client/src/recoilModel/dispatchers/utils/project.ts @@ -1,75 +1,75 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -import { v4 as uuid } from 'uuid'; +import { indexer, validateDialog } from '@bfc/indexers'; import { - SensitiveProperties, + BotProjectFile, + BotProjectSpace, + BotProjectSpaceSkill, + convertFileProtocolToPath, convertSkillsToDictionary, - DialogSetting, dereferenceDefinitions, DialogInfo, + DialogSetting, LuFile, QnAFile, - BotProjectSpace, - BotProjectFile, - BotProjectSpaceSkill, - convertFileProtocolToPath, + SensitiveProperties, } from '@bfc/shared'; +import formatMessage from 'format-message'; +import camelCase from 'lodash/camelCase'; import objectGet from 'lodash/get'; import objectSet from 'lodash/set'; -import { indexer, validateDialog } from '@bfc/indexers'; -import { CallbackInterface } from 'recoil'; import { stringify } from 'query-string'; -import formatMessage from 'format-message'; -import camelCase from 'lodash/camelCase'; +import { CallbackInterface } from 'recoil'; +import { v4 as uuid } from 'uuid'; -import * as botstates from '../../atoms/botState'; -import UndoHistory from '../../undo/undoHistory'; -import languageStorage from '../../../utils/languageStorage'; +import { BotStatus, QnABotTemplateId } from '../../../constants'; import settingStorage from '../../../utils/dialogSettingStorage'; +import { getUniqueName } from '../../../utils/fileUtil'; +import httpClient from '../../../utils/httpUtil'; +import languageStorage from '../../../utils/languageStorage'; +import luFileStatusStorage from '../../../utils/luFileStatusStorage'; +import { getReferredLuFiles } from '../../../utils/luUtil'; +import { navigateTo } from '../../../utils/navigation'; +import qnaFileStatusStorage from '../../../utils/qnaFileStatusStorage'; +import { getReferredQnaFiles } from '../../../utils/qnaUtil'; import { botDiagnosticsState, + botDisplayNameState, + botEnvironmentState, + botErrorState, + botNameIdentifierState, botProjectFileState, - botProjectSpaceLoadedState, botProjectIdsState, + botProjectSpaceLoadedState, + botStatusState, currentProjectIdState, + dialogSchemasState, + dialogsState, filePersistenceState, + formDialogSchemaIdsState, + formDialogSchemaState, + lgFilesState, + localeState, + locationState, + luFilesState, projectMetaDataState, qnaFilesState, recentProjectsState, - botErrorState, - botNameIdentifierState, + schemasState, + settingsState, + skillManifestsState, + skillsState, } from '../../atoms'; +import * as botstates from '../../atoms/botState'; import lgWorker from '../../parsers/lgWorker'; import luWorker from '../../parsers/luWorker'; import qnaWorker from '../../parsers/qnaWorker'; import FilePersistence from '../../persistence/FilePersistence'; -import { navigateTo } from '../../../utils/navigation'; -import { BotStatus, QnABotTemplateId } from '../../../constants'; -import httpClient from '../../../utils/httpUtil'; -import { getReferredLuFiles } from '../../../utils/luUtil'; -import luFileStatusStorage from '../../../utils/luFileStatusStorage'; -import { getReferredQnaFiles } from '../../../utils/qnaUtil'; -import qnaFileStatusStorage from '../../../utils/qnaFileStatusStorage'; -import { logMessage, setError } from '../shared'; -import { - skillManifestsState, - settingsState, - localeState, - luFilesState, - skillsState, - schemasState, - lgFilesState, - locationState, - botStatusState, - botDisplayNameState, - botEnvironmentState, - dialogsState, - dialogSchemasState, -} from '../../atoms'; -import { undoHistoryState } from '../../undo/history'; import { rootBotProjectIdSelector } from '../../selectors'; -import { getUniqueName } from '../../../utils/fileUtil'; +import { undoHistoryState } from '../../undo/history'; +import UndoHistory from '../../undo/undoHistory'; +import { logMessage, setError } from '../shared'; export const resetBotStates = async ({ reset }: CallbackInterface, projectId: string) => { const botStates = Object.keys(botstates); @@ -153,7 +153,7 @@ export const navigateToBot = ( } }; -const loadProjectData = (response) => { +export const loadProjectData = (response) => { const { files, botName, settings, skills: skillContent, id: projectId } = response.data; const mergedSettings = getMergedSettings(projectId, settings); const storedLocale = languageStorage.get(botName)?.locale; @@ -257,7 +257,17 @@ export const initQnaFilesStatus = (projectId: string, qnaFiles: QnAFile[], dialo export const initBotState = async (callbackHelpers: CallbackInterface, data: any, botFiles: any) => { const { snapshot, set } = callbackHelpers; const { botName, botEnvironment, location, schemas, settings, id: projectId, diagnostics } = data; - const { dialogs, dialogSchemas, luFiles, lgFiles, qnaFiles, skillManifestFiles, skills, mergedSettings } = botFiles; + const { + dialogs, + dialogSchemas, + formDialogSchemas, + luFiles, + lgFiles, + qnaFiles, + skillManifestFiles, + skills, + mergedSettings, + } = botFiles; const curLocation = await snapshot.getPromise(locationState(projectId)); const storedLocale = languageStorage.get(botName)?.locale; const locale = settings.languages.includes(storedLocale) ? storedLocale : settings.defaultLanguage; @@ -281,6 +291,15 @@ export const initBotState = async (callbackHelpers: CallbackInterface, data: any await lgWorker.addProject(projectId, lgFiles); + // Form dialogs + set( + formDialogSchemaIdsState(projectId), + formDialogSchemas.map((f) => f.id) + ); + formDialogSchemas.forEach(({ id, content }) => { + set(formDialogSchemaState({ projectId, schemaId: id }), { id, content }); + }); + set(skillManifestsState(projectId), skillManifestFiles); set(luFilesState(projectId), initLuFilesStatus(botName, luFiles, dialogs)); set(lgFilesState(projectId), lgFiles); diff --git a/Composer/packages/client/src/recoilModel/selectors/project.ts b/Composer/packages/client/src/recoilModel/selectors/project.ts index 7600eb326d..cebb4de1a9 100644 --- a/Composer/packages/client/src/recoilModel/selectors/project.ts +++ b/Composer/packages/client/src/recoilModel/selectors/project.ts @@ -1,8 +1,9 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -import { selector } from 'recoil'; +import { selector, selectorFamily } from 'recoil'; import isEmpty from 'lodash/isEmpty'; +import { FormDialogSchema } from '@bfc/shared'; import { botErrorState, @@ -12,6 +13,8 @@ import { dialogsState, projectMetaDataState, botNameIdentifierState, + formDialogSchemaIdsState, + formDialogSchemaState, } from '../atoms'; // Actions @@ -57,3 +60,19 @@ export const rootBotProjectIdSelector = selector({ } }, }); + +export const formDialogSchemasSelectorFamily = selectorFamily({ + key: 'formDialogSchemasSelector', + get: (projectId: string) => ({ get }) => { + const formDialogSchemaIds = get(formDialogSchemaIdsState(projectId)); + return formDialogSchemaIds.map((schemaId) => get(formDialogSchemaState({ projectId, schemaId }))); + }, +}); + +export const formDialogSchemaDialogExistsSelector = selectorFamily({ + key: 'formDialogSchemasSelector', + get: ({ projectId, schemaId }: { projectId: string; schemaId: string }) => ({ get }) => { + const dialogs = get(dialogsState(projectId)); + return !!dialogs.find((d) => d.id === schemaId); + }, +}); diff --git a/Composer/packages/client/src/router.tsx b/Composer/packages/client/src/router.tsx index 11aac856a0..c915c7b28f 100644 --- a/Composer/packages/client/src/router.tsx +++ b/Composer/packages/client/src/router.tsx @@ -56,8 +56,8 @@ const Routes = (props) => { - - + + diff --git a/Composer/packages/client/src/utils/pageLinks.ts b/Composer/packages/client/src/utils/pageLinks.ts index 9b317e236e..4f5e580312 100644 --- a/Composer/packages/client/src/utils/pageLinks.ts +++ b/Composer/packages/client/src/utils/pageLinks.ts @@ -65,9 +65,9 @@ export const topLinks = (projectId: string, openedDialogId: string, pluginPages: disabled: !botLoaded, }, { - to: `/bot/${projectId}/form-dialogs`, - iconName: 'OfficeChat', - labelName: formatMessage('Form Dialogs'), + to: `/bot/${projectId}/forms`, + iconName: 'Table', + labelName: formatMessage('Forms'), exact: false, disabled: !botLoaded, }, diff --git a/Composer/packages/form-dialogs/package.json b/Composer/packages/form-dialogs/package.json index 2b077a956c..d477d07517 100644 --- a/Composer/packages/form-dialogs/package.json +++ b/Composer/packages/form-dialogs/package.json @@ -8,8 +8,7 @@ "typings": "./lib/VisualSchemaEditor.d.ts", "files": [ "lib/*index*", - "lib/VisualSchemaEditor.d.ts", - "LICENSE" + "lib/FormDialogSchemaEditor.d.ts" ], "scripts": { "clean": "rimraf lib dist", diff --git a/Composer/packages/form-dialogs/src/components/FormDialogPropertiesEditor.tsx b/Composer/packages/form-dialogs/src/components/FormDialogPropertiesEditor.tsx index 413b88db78..ca6dbcba76 100644 --- a/Composer/packages/form-dialogs/src/components/FormDialogPropertiesEditor.tsx +++ b/Composer/packages/form-dialogs/src/components/FormDialogPropertiesEditor.tsx @@ -14,7 +14,6 @@ import { useRecoilValue } from 'recoil'; import { allFormDialogPropertyIdsSelector, formDialogSchemaAtom, - formDialogSchemaJsonSelector, formDialogSchemaValidSelector, } from 'src/atoms/appState'; import { useHandlers } from 'src/atoms/handlers'; @@ -23,17 +22,6 @@ import { FormDialogSchemaDetails } from 'src/components/property/FormDialogSchem import { useUndo } from 'src/undo/useUndo'; import { useUndoKeyBinding } from 'src/utils/hooks/useUndoKeyBinding'; -const downloadFile = async (fileName: string, schemaExtension: string, content: string) => { - const blob = new Blob([content], { type: 'application/json' }); - const href = URL.createObjectURL(blob); - const link = document.createElement('a'); - link.href = href; - link.download = `${fileName}.${schemaExtension}`; - document.body.appendChild(link); - link.click(); - document.body.removeChild(link); -}; - const Root = styled(Stack)({ backgroundColor: NeutralColors.gray20, position: 'relative', @@ -84,7 +72,7 @@ export const FormDialogPropertiesEditor = React.memo((props: Props) => { const schema = useRecoilValue(formDialogSchemaAtom); const propertyIds = useRecoilValue(allFormDialogPropertyIdsSelector); const schemaValid = useRecoilValue(formDialogSchemaValidSelector); - const schemaJson = useRecoilValue(formDialogSchemaJsonSelector); + const { importSchema, addProperty } = useHandlers(); const schemaIdRef = React.useRef(schema.id); @@ -146,17 +134,6 @@ export const FormDialogPropertiesEditor = React.memo((props: Props) => { key: 'import', onRender: () => , }, - { - key: 'export', - iconProps: { iconName: 'Export' }, - text: formatMessage('Export JSON'), - title: formatMessage('Export JSON'), - ariaLabel: formatMessage('Export JSON'), - disabled: isGenerating || !propertyIds.length || !schemaValid, - onClick: () => { - downloadFile(schema.name, schemaExtension, schemaJson); - }, - }, { key: 'reset', iconProps: { iconName: 'Clear' }, diff --git a/Composer/packages/form-dialogs/src/components/property/FormDialogPropertyCard.tsx b/Composer/packages/form-dialogs/src/components/property/FormDialogPropertyCard.tsx index 864fd215d7..26d2a854e3 100644 --- a/Composer/packages/form-dialogs/src/components/property/FormDialogPropertyCard.tsx +++ b/Composer/packages/form-dialogs/src/components/property/FormDialogPropertyCard.tsx @@ -187,17 +187,22 @@ export const FormDialogPropertyCard = React.memo((props: FormDialogPropertyCardP { + return formDialogSchemaFiles.map((file) => { + const { content, lastModified, name } = file; + return { content, id: getBaseName(name, '.form-dialog'), lastModified }; + }); +}; + +export const formDialogSchemaIndexer = { + index, +}; diff --git a/Composer/packages/lib/indexers/src/index.ts b/Composer/packages/lib/indexers/src/index.ts index bbf1b42848..37da55842f 100644 --- a/Composer/packages/lib/indexers/src/index.ts +++ b/Composer/packages/lib/indexers/src/index.ts @@ -12,6 +12,7 @@ import { skillManifestIndexer } from './skillManifestIndexer'; import { botProjectSpaceIndexer } from './botProjectSpaceIndexer'; import { FileExtensions } from './utils/fileExtensions'; import { getExtension, getBaseName } from './utils/help'; +import { formDialogSchemaIndexer } from './formDialogSchemaIndexer'; class Indexer { private classifyFile(files: FileInfo[]) { @@ -26,6 +27,7 @@ class Indexer { { [FileExtensions.lg]: [], [FileExtensions.Lu]: [], + [FileExtensions.FormDialog]: [], [FileExtensions.QnA]: [], [FileExtensions.Dialog]: [], [FileExtensions.DialogSchema]: [], @@ -57,6 +59,7 @@ class Indexer { skillManifestFiles: skillManifestIndexer.index(result[FileExtensions.Manifest]), skills: skillIndexer.index(skillContent, settings.skill), botProjectSpaceFiles: botProjectSpaceIndexer.index(result[FileExtensions.BotProjectSpace]), + formDialogSchemas: formDialogSchemaIndexer.index(result[FileExtensions.FormDialog]), }; } } diff --git a/Composer/packages/server/src/locales/en-US.json b/Composer/packages/server/src/locales/en-US.json index 0967ef424b..4a436e4779 100644 --- a/Composer/packages/server/src/locales/en-US.json +++ b/Composer/packages/server/src/locales/en-US.json @@ -1 +1,2600 @@ -{} +{ + "0_bytes_a1e1cdb3": { + "message": "0 Bytes" + }, + "5_minute_intro_7ea06d2b": { + "message": "5 Minute Intro" + }, + "ErrorInfo_part1": { + "message": "An error occured in the form editor!" + }, + "ErrorInfo_part2": { + "message": "This is likely due to malformed data or missing functionality in Composer." + }, + "ErrorInfo_part3": { + "message": "Try navigating to another node in the visual editor." + }, + "a_dialog_file_must_have_a_name_123ff67d": { + "message": "a dialog file must have a name" + }, + "a_form_dialog_enables_your_bot_to_collect_pieces_o_fdd3fe56": { + "message": "A form dialog enables your bot to collect pieces of information ." + }, + "a_minimap_gives_an_overview_of_your_source_code_fo_9a897f4f": { + "message": "A minimap gives an overview of your source code for quick navigation and code understanding." + }, + "a_newer_version_of_the_provisioning_scripts_has_be_85d3ad94": { + "message": "A newer version of the provisioning scripts has been found, and this project can be updated to the latest." + }, + "a_profile_with_that_name_already_exists_7c559958": { + "message": "A profile with that name already exists." + }, + "a_subscription_key_is_created_when_you_create_a_qn_37a6926f": { + "message": "A subscription key is created when you create a QnA Maker resource." + }, + "a_valid_url_should_start_with_http_or_https_d24b3591": { + "message": "A valid url should start with http:// or https://" + }, + "about_70c18bba": { + "message": "About" + }, + "accepted_values_b4224bcd": { + "message": "Accepted values" + }, + "accepts_multiple_values_73658f63": { + "message": "Accepts multiple values" + }, + "access_external_resources_7e37fe21": { + "message": "Access external resources" + }, + "action_created_f80af9a0": { + "message": "Action created" + }, + "action_deleted_a10c002b": { + "message": "Action deleted" + }, + "action_focused_400ee4af": { + "message": "Action focused" + }, + "action_unfocused_18a2800e": { + "message": "Action unfocused" + }, + "actions_copied_2821ab27": { + "message": "Actions copied" + }, + "actions_created_6c5acc11": { + "message": "Actions created" + }, + "actions_cut_929f4c37": { + "message": "Actions cut" + }, + "actions_define_b_how_the_bot_responds_b_to_a_certa_890a71f4": { + "message": "Actions define how the bot responds to a certain trigger." + }, + "actions_deleted_355c359a": { + "message": "Actions deleted" + }, + "actions_disabled_c1accfb6": { + "message": "Actions disabled" + }, + "actions_efcde10d": { + "message": "Actions" + }, + "actions_enabled_8e9ec02d": { + "message": "Actions enabled" + }, + "actions_moved_d7777b29": { + "message": "Actions moved" + }, + "activities_125193f7": { + "message": "Activities" + }, + "activity_13915493": { + "message": "Activity" + }, + "activity_received_2f20fa9d": { + "message": "Activity received" + }, + "adaptive_dialog_61a05dde": { + "message": "Adaptive dialog" + }, + "add_8523c19b": { + "message": "Add" + }, + "add_a_new_key_5c208c29": { + "message": "Add a new key" + }, + "add_a_new_skill_dialog_aaaafa9c": { + "message": "Add a new Skill Dialog" + }, + "add_a_new_trigger_a82d3db8": { + "message": "Add a new trigger" + }, + "add_a_new_value_24ca14ac": { + "message": "Add a new value" + }, + "add_a_publish_profile_fca2263a": { + "message": "Add a publish profile" + }, + "add_a_skill_46d2b71c": { + "message": "Add a skill" + }, + "add_a_welcome_message_9e1480b2": { + "message": "Add a welcome message" + }, + "add_additional_url_bdfac25d": { + "message": "Add additional URL" + }, + "add_alternative_phrasing_47234f77": { + "message": "add alternative phrasing" + }, + "add_an_intent_trigger_a9acc149": { + "message": "Add an intent trigger" + }, + "add_custom_runtime_6b73dc44": { + "message": "Add custom runtime" + }, + "add_language_75dee49e": { + "message": "Add language" + }, + "add_multiple_comma_separated_synonyms_2639283f": { + "message": "Add multiple comma-separated synonyms" + }, + "add_new_dialog_25f12c9": { + "message": "Add new dialog" + }, + "add_new_extension_19b82b77": { + "message": "Add new extension" + }, + "add_new_knowledge_base_on_displayname_7f44609c": { + "message": " Add new knowledge base on { displayName }" + }, + "add_new_profile_47b225e6": { + "message": "Add new profile" + }, + "add_new_propertyname_bedf7dc6": { + "message": "Add new { propertyName }" + }, + "add_new_trigger_on_displayname_2aaf6e4c": { + "message": "Add new trigger on { displayName }" + }, + "add_new_validation_rule_here_eb675ccf": { + "message": "Add new validation rule here" + }, + "add_property_d381eba3": { + "message": "Add Property" + }, + "add_qna_pair_bcb1624": { + "message": "Add QnA Pair" + }, + "add_some_example_phrases_to_trigger_this_intent_pl_568eaf51": { + "message": "> add some example phrases to trigger this intent:\n> - please tell me the weather\n> - what is the weather like in '{'city=Seattle'}'\n\n> entity definitions:\n> @ ml city" + }, + "add_some_expected_user_responses_please_remind_me__31dc5c07": { + "message": "> add some expected user responses:\n> - Please remind me to '{'itemTitle=buy milk'}'\n> - remind me to '{'itemTitle'}'\n> - add '{'itemTitle'}' to my todo list\n>\n> entity definitions:\n> @ ml itemTitle\n" + }, + "add_welcome_message_49d9ded9": { + "message": "Add welcome message" + }, + "advanced_events_2cbfa47d": { + "message": "Advanced Events" + }, + "advanced_options_4dcc8385": { + "message": "Advanced options" + }, + "all_4321c3a1": { + "message": "All" + }, + "all_language_generation_files_ef778f24": { + "message": "all language generation files" + }, + "all_language_understanding_files_714b33eb": { + "message": "all language understanding files" + }, + "all_profiles_8321b1aa": { + "message": "All profiles" + }, + "all_qna_files_349d7fe1": { + "message": "all qna files" + }, + "an_authoring_key_is_created_automatically_when_you_21cf77aa": { + "message": "An authoring key is created automatically when you create a LUIS account." + }, + "answer_4620913f": { + "message": "Answer" + }, + "answer_is_answer_799483f0": { + "message": "Answer is { answer }" + }, + "any_constant_or_expression_to_evaluate_ba2017b1": { + "message": "Any constant or expression to evaluate." + }, + "any_or_expression_acad7d37": { + "message": "Any or expression" + }, + "any_string_f22dc2e1": { + "message": "any string" + }, + "append_choices_35c45a2d": { + "message": "Append choices" + }, + "application_language_87691b6": { + "message": "Application Language" + }, + "application_language_f100f3e0": { + "message": "Application language" + }, + "application_settings_39e840c6": { + "message": "Application Settings" + }, + "application_updates_bdf5f8b6": { + "message": "Application Updates" + }, + "apr_9_2020_3c8b47d7": { + "message": "Apr 9, 2020" + }, + "are_you_sure_you_want_to_exit_the_onboarding_produ_c2de1b23": { + "message": "Are you sure you want to exit the Onboarding Product Tour? You can restart the tour in the onboarding settings." + }, + "are_you_sure_you_want_to_remove_form_dialog_schema_67c927ce": { + "message": "Are you sure you want to remove form dialog schema \"{ id }\"?" + }, + "are_you_sure_you_want_to_remove_propertyname_8a793e4f": { + "message": "Are you sure you want to remove \"{ propertyName }\"?" + }, + "are_you_sure_you_want_to_start_over_your_progress__3b7cde9b": { + "message": "Are you sure you want to start over? Your progress will be lost." + }, + "are_you_sure_you_want_to_stop_current_runtime_and__a5f883b2": { + "message": "Are you sure you want to stop current runtime and replace them?" + }, + "are_you_sure_you_want_to_uninstall_these_extension_cf6265e8": { + "message": "Are you sure you want to uninstall these extensions?" + }, + "array_643947ee": { + "message": "Array" + }, + "array_constant_8829b2ec": { + "message": "Array constant." + }, + "array_or_expression_c52b2ecc": { + "message": "Array or expression" + }, + "array_or_expression_to_evaluate_87d9328b": { + "message": "Array or expression to evaluate." + }, + "ask_a_question_92ef7e0c": { + "message": "Ask a question" + }, + "attachment_input_e0ece49c": { + "message": "Attachment Input" + }, + "australia_6a25c95b": { + "message": "australia" + }, + "authoring_canvas_18802e39": { + "message": "Authoring canvas" + }, + "authoring_region_to_use_e_g_westus_westeurope_aust_d43d5245": { + "message": "Authoring region to use (e.g. westus, westeurope, australiaeast)" + }, + "authoring_region_to_use_westus_qna_maker_resource__4588c2f9": { + "message": "Authoring region to use (westus) (QnA maker resource location)" + }, + "auto_update_86eb33b0": { + "message": "Auto update" + }, + "available_skills_95c114ac": { + "message": "Available Skills" + }, + "been_used_5daccdb2": { + "message": "Been used" + }, + "begin_a_new_dialog_60249bd8": { + "message": "Begin a new dialog" + }, + "begin_a_remote_skill_dialog_93e47189": { + "message": "Begin a remote skill dialog." + }, + "begin_dialog_12e2becf": { + "message": "Begin Dialog" + }, + "begin_dialog_event_285bc650": { + "message": "Begin dialog event" + }, + "begindialog_a5594562": { + "message": "BeginDialog" + }, + "ben_brown_99c12d19": { + "message": "Ben Brown" + }, + "boolean_6000988a": { + "message": "Boolean" + }, + "boolean_condition_b65450ca": { + "message": "Boolean condition" + }, + "boolean_constant_7d3fcbf6": { + "message": "Boolean constant" + }, + "boolean_constant_8d950af8": { + "message": "Boolean constant." + }, + "boolean_constant_or_expression_to_evaluate_86d77849": { + "message": "Boolean constant or expression to evaluate." + }, + "boolean_field_602934c9": { + "message": "boolean field" + }, + "boolean_or_expression_6bd88208": { + "message": "Boolean or expression" + }, + "boolean_value_98d39ea1": { + "message": "Boolean value." + }, + "bot_asks_5e9f0202": { + "message": "Bot Asks" + }, + "bot_framework_composer_enables_developers_and_mult_ce0e42a9": { + "message": "Bot Framework Composer enables developers and multi-disciplinary teams to build all kinds of conversational experiences, using the latest components from the Bot Framework: SDK, LG, LU, and declarative file formats, all without writing code." + }, + "bot_framework_composer_fae721be": { + "message": "Bot Framework Composer" + }, + "bot_framework_composer_icon_gray_fa72d3d6": { + "message": "bot framework composer icon gray" + }, + "bot_framework_composer_is_a_visual_authoring_canva_c3947d91": { + "message": "Bot Framework Composer is a visual authoring canvas for building bots and other types of conversational application with the Microsoft Bot Framework technology stack. With Composer you will find everything you need to build a modern, state-of-the-art conversational experience." + }, + "bot_framework_composer_is_an_open_source_visual_au_2be2e02b": { + "message": "Bot Framework Composer is an open-source visual authoring canvas for developers and multi-disciplinary teams to build bots. Composer integrates LUIS and QnA Maker, and allows sophisticated composition of bot replies using language generation." + }, + "bot_framework_provides_the_most_comprehensive_expe_e34a7f5d": { + "message": "Bot Framework provides the most comprehensive experience for building conversational applications." + }, + "bot_language_6cf30c2": { + "message": "Bot language" + }, + "bot_language_active_7cf9dc78": { + "message": "Bot language (active)" + }, + "bot_name_is_botname_a28c2d05": { + "message": "Bot name is { botName }" + }, + "bot_project_file_does_not_exist_a0864a2c": { + "message": "Bot project file does not exist." + }, + "bot_responses_4617b4a2": { + "message": "Bot responses" + }, + "bot_responses_c4e63601": { + "message": "Bot Responses" + }, + "bot_settings_3751e1b2": { + "message": "Bot settings" + }, + "bot_settings_ce2783e4": { + "message": "Bot Settings" + }, + "branch_if_else_391e5681": { + "message": "Branch: If/else" + }, + "branch_if_else_992cf9bf": { + "message": "Branch: If/Else" + }, + "branch_if_else_f6a36f1d": { + "message": "Branch: if/else" + }, + "branch_switch_multiple_options_95c6a326": { + "message": "Branch: Switch (multiple options)" + }, + "break_out_of_loop_ab30157c": { + "message": "Break out of loop" + }, + "build_your_first_bot_f9c3e427": { + "message": "Build your first bot" + }, + "bytes_74acea97": { + "message": "Bytes" + }, + "calculating_17b21be7": { + "message": "Calculating..." + }, + "cancel_all_active_dialogs_335b1623": { + "message": "Cancel all active dialogs" + }, + "cancel_all_dialogs_32144c45": { + "message": "Cancel All Dialogs" + }, + "cancel_caeb1e68": { + "message": "Cancel" + }, + "cancel_dialog_event_cc671dee": { + "message": "Cancel dialog event" + }, + "change_recognizer_3145b93d": { + "message": "Change Recognizer" + }, + "check_for_updates_and_install_them_automatically_50337340": { + "message": "Check for updates and install them automatically." + }, + "choice_input_f75a2353": { + "message": "Choice Input" + }, + "choice_name_fe8411f4": { + "message": "Choice Name" + }, + "choose_a_location_for_your_new_bot_project_e979f2d5": { + "message": "Choose a location for your new bot project." + }, + "choose_how_to_create_your_bot_a97f7b3e": { + "message": "Choose how to create your bot" + }, + "choose_one_2c4277df": { + "message": "Choose One" + }, + "chris_whitten_11df1f35": { + "message": "Chris Whitten" + }, + "clear_all_da755751": { + "message": "Clear all" + }, + "click_on_the_b_add_b_button_in_the_toolbar_and_sel_4daf351a": { + "message": "Click on the Add button in the toolbar, and select Add a new trigger. In the Create a trigger wizard, set the Trigger Type to Intent recognized and configure the Trigger Name and Trigger Phrases. Then add actions in the Visual Editor." + }, + "click_the_b_add_b_button_in_the_toolbar_and_select_79001156": { + "message": "Click the Add button in the toolbar, and select Add a new trigger from the dropdown menu." + }, + "click_to_sort_by_file_type_1b0c9bd": { + "message": "Click to sort by file type" + }, + "close_d634289d": { + "message": "Close" + }, + "collapse_34080b4d": { + "message": "Collapse" + }, + "collapse_navigation_17228b95": { + "message": "Collapse Navigation" + }, + "comment_7ef1428e": { + "message": "Comment" + }, + "component_stacktrace_e24b1983": { + "message": "Component Stacktrace:" + }, + "composer_cannot_yet_translate_your_bot_automatical_2d54081b": { + "message": "Composer cannot yet translate your bot automatically.\nTo create a translation manually, Composer will create a copy of your bot’s content with the name of the additional language. This content can then be translated without affecting the original bot logic or flow and you can switch between languages to ensure the responses are correctly and appropriately translated." + }, + "composer_introduction_98a93701": { + "message": "Composer introduction" + }, + "composer_is_up_to_date_9118257d": { + "message": "Composer is up to date." + }, + "composer_logo_ba2048a0": { + "message": "Composer Logo" + }, + "composer_needs_net_core_sdk_46e2a8ae": { + "message": "Composer needs .NET Core SDK" + }, + "composer_will_restart_88ee8dc3": { + "message": "Composer will restart." + }, + "composer_will_update_the_next_time_you_close_the_a_d74264a1": { + "message": "Composer will update the next time you close the app." + }, + "conditionalselector_ed2031f0": { + "message": "ConditionalSelector" + }, + "configure_composer_to_start_your_bot_using_runtime_fe37dadf": { + "message": "Configure Composer to start your bot using runtime code you can customize and control." + }, + "configures_default_language_model_to_use_if_there__f09f1acd": { + "message": "Configures default language model to use if there is no culture code in the file name (Default: en-us)" + }, + "confirm_6556b3a6": { + "message": "Confirm" + }, + "confirm_choices_db8e99fb": { + "message": "Confirm Choices" + }, + "confirm_input_bf996e7a": { + "message": "Confirm Input" + }, + "confirmation_fec87d65": { + "message": "Confirmation" + }, + "confirmation_modal_must_have_a_title_b0816e0b": { + "message": "Confirmation modal must have a title." + }, + "congratulations_your_model_is_successfully_publish_52ebc297": { + "message": "Congratulations! Your model is successfully published." + }, + "connect_to_a_new_skill_34d582ab": { + "message": "Connect to a new skill" + }, + "connect_to_a_skill_53c9dff0": { + "message": "Connect to a skill" + }, + "connect_to_qna_knowledgebase_4b324132": { + "message": "Connect to QnA Knowledgebase" + }, + "continue_loop_22635585": { + "message": "Continue loop" + }, + "conversation_ended_a8bd37dd": { + "message": "Conversation ended" + }, + "conversation_invoked_e960884e": { + "message": "Conversation invoked" + }, + "conversationupdate_activity_9e94bff5": { + "message": "ConversationUpdate activity" + }, + "copy_9748f9f": { + "message": "Copy" + }, + "copy_content_for_translation_7affbcbb": { + "message": "Copy content for translation" + }, + "could_not_connect_to_storage_50411de0": { + "message": "Could not connect to storage." + }, + "could_not_init_plugin_1f1c29cd": { + "message": "Could not init plugin" + }, + "couldn_t_complete_the_update_a337a359": { + "message": "Couldn''t complete the update:" + }, + "create_132b3be1": { + "message": "Create" + }, + "create_a_bot_project_73e6ce33": { + "message": "Create a bot project" + }, + "create_a_condition_8686fd5": { + "message": "Create a condition" + }, + "create_a_dialog_9f6c8618": { + "message": "Create a dialog" + }, + "create_a_name_for_the_project_which_will_be_used_t_57e9b690": { + "message": "Create a name for the project which will be used to name the application: (projectname-environment-LUfilename)" + }, + "create_a_new_dialog_21d84b82": { + "message": "Create a new dialog" + }, + "create_a_new_form_dialog_schema_by_clicking_above_34b80531": { + "message": "Create a new form dialog schema by clicking + above." + }, + "create_a_new_skill_manifest_or_select_which_one_yo_a97e9616": { + "message": "Create a new skill manifest or select which one you want to edit" + }, + "create_a_trigger_40e74743": { + "message": "Create a trigger" + }, + "create_bot_from_template_or_scratch_92f0fefa": { + "message": "Create bot from template or scratch?" + }, + "create_copy_to_translate_bot_content_efc872c": { + "message": "Create copy to translate bot content" + }, + "create_folder_error_38aa86f5": { + "message": "Create Folder Error" + }, + "create_form_dialog_bb98a4f2": { + "message": "Create form dialog" + }, + "create_from_knowledge_base_qna_maker_7422486": { + "message": "Create from knowledge base (QnA Maker)" + }, + "create_from_qna_7aa9dcbb": { + "message": "Create from QnA" + }, + "create_from_scratch_485c3045": { + "message": "Create from scratch" + }, + "create_from_template_87e12c94": { + "message": "Create from template" + }, + "create_knowledge_base_db6d66c4": { + "message": "Create knowledge base" + }, + "create_knowledge_base_from_scratch_afe4d2a2": { + "message": "Create knowledge base from scratch" + }, + "create_new_empty_bot_21cf0ea3": { + "message": "Create new empty bot" + }, + "create_new_folder_19d3faa4": { + "message": "Create new folder" + }, + "create_new_knowledge_base_e14d07a5": { + "message": "Create New Knowledge Base" + }, + "create_or_edit_skill_manifest_8ad98da9": { + "message": "Create or edit skill manifest" + }, + "creating_your_knowledge_base_ef4f9872": { + "message": "Creating your knowledge base" + }, + "current_40c0812f": { + "message": " - Current" + }, + "custom_actions_5d396747": { + "message": "Custom Actions" + }, + "custom_events_d6f0e45b": { + "message": "Custom events" + }, + "custom_name_optional_60ee6a1a": { + "message": "Custom name (optional)" + }, + "custom_recognizer_951bab90": { + "message": "Custom recognizer" + }, + "cut_c8c92681": { + "message": "Cut" + }, + "data_loading_4c9bb9f6": { + "message": "Data loading..." + }, + "date_ee500367": { + "message": "Date" + }, + "date_modified_18beced9": { + "message": "Date modified" + }, + "date_modified_e1c8ac8f": { + "message": "Date Modified" + }, + "date_or_time_d30bcc7d": { + "message": "Date or time" + }, + "date_time_input_2416ffc1": { + "message": "Date Time Input" + }, + "debug_break_46cb5adb": { + "message": "Debug Break" + }, + "debug_break_870a75df": { + "message": "Debug break" + }, + "debugging_options_20e2e9da": { + "message": "Debugging options" + }, + "decrement_by_step_9b6c2fa3": { + "message": "decrement by { step }" + }, + "default_language_486a558d": { + "message": "Default language" + }, + "default_language_b11c37db": { + "message": "Default Language" + }, + "default_recognizer_9c06c1a3": { + "message": "Default recognizer" + }, + "define_by_value_type_3cd3d1a8": { + "message": "Define by value type" + }, + "define_conversation_objective_146d1cc6": { + "message": "Define conversation objective" + }, + "defined_in_475568fb": { + "message": "Defined in:" + }, + "definition_not_found_for_defname_7194fd07": { + "message": "Definition not found for { defName }" + }, + "delete_a6efa79d": { + "message": "Delete" + }, + "delete_a_property_f2d70f79": { + "message": "Delete a property" + }, + "delete_activity_6d881872": { + "message": "Delete activity" + }, + "delete_bot_73586104": { + "message": "Delete Bot" + }, + "delete_form_dialog_schema_892df4e8": { + "message": "Delete form dialog schema" + }, + "delete_language_1527609d": { + "message": "Delete language" + }, + "delete_properties_8bc77b42": { + "message": "Delete Properties" + }, + "delete_properties_c49a7892": { + "message": "Delete properties" + }, + "delete_property_b3786fa0": { + "message": "Delete Property" + }, + "describe_your_skill_88554792": { + "message": "Describe your skill" + }, + "description_436c48d7": { + "message": "Description" + }, + "design_51b2812a": { + "message": "Design" + }, + "dialog_68ba69ba": { + "message": "(Dialog)" + }, + "dialog_cancelled_767b512a": { + "message": "Dialog cancelled" + }, + "dialog_data_61d5539b": { + "message": "Dialog data" + }, + "dialog_dialogid_not_found_5e8214c3": { + "message": "dialog { dialogId } not found" + }, + "dialog_events_f1b2e2a0": { + "message": "Dialog events" + }, + "dialog_input_schema_c7864fbd": { + "message": "Dialog input schema." + }, + "dialog_interface_398bc493": { + "message": "Dialog Interface" + }, + "dialog_management_2980578": { + "message": "Dialog management" + }, + "dialog_opened_f6392b1": { + "message": "Dialog opened" + }, + "dialog_output_schema_acfe2186": { + "message": "Dialog output schema." + }, + "dialog_started_912507c": { + "message": "Dialog started" + }, + "dialog_with_the_name_value_already_exists_62838518": { + "message": "Dialog with the name: { value } already exists." + }, + "dialogfactory_missing_schema_5c3255c4": { + "message": "DialogFactory missing schema." + }, + "dialognum_plural_0_no_dialogs_1_one_dialog_other_d_1b86909b": { + "message": "{ dialogNum, plural,\n =0 {No dialogs}\n =1 {One dialog}\n other {# dialogs}\n} have been found.\n { dialogNum, select,\n 0 {}\n other {Press down arrow key to navigate the search results}\n}" + }, + "disable_a5c05db3": { + "message": "Disable" + }, + "display_lines_that_extends_beyond_the_width_of_the_9e500f3c": { + "message": "Display lines that extends beyond the width of the editor on the next line." + }, + "do_you_wish_to_continue_96469eaf": { + "message": "Do you wish to continue?" + }, + "does_not_exist_3a34b418": { + "message": "Does Not Exist" + }, + "done_1a4a010a": { + "message": "Done!" + }, + "done_54e3d4b6": { + "message": "Done" + }, + "download_now_and_install_when_you_close_composer_e241ed74": { + "message": "Download now and install when you close Composer." + }, + "downloading_bb6fb34b": { + "message": "Downloading..." + }, + "duplicate_dialog_name_824f9fce": { + "message": "Duplicate dialog name" + }, + "duplicate_fields_9fd0d3c2": { + "message": "duplicate fields" + }, + "duplicate_name_d295a09d": { + "message": "Duplicate name" + }, + "duplicate_skill_manifest_url_74d85839": { + "message": "Duplicate skill manifest Url" + }, + "duplicate_skill_name_d9f6eb8d": { + "message": "Duplicate skill name" + }, + "duplicated_intents_recognized_d3908424": { + "message": "Duplicated intents recognized" + }, + "early_adopters_e8db7999": { + "message": "Early adopters" + }, + "edit_a_publish_profile_30ebab3e": { + "message": "Edit a publish profile" + }, + "edit_a_skill_5665d9ac": { + "message": "Edit a skill" + }, + "edit_actions_b38e9fac": { + "message": "Edit Actions" + }, + "edit_an_array_property_5d886011": { + "message": "Edit an array property" + }, + "edit_array_4ab37c8": { + "message": "Edit Array" + }, + "edit_c5fbea07": { + "message": "Edit" + }, + "edit_displayname_dialog_description_986a7d60": { + "message": "Edit { displayName } dialog description" + }, + "edit_in_json_75d0d754": { + "message": "Edit in JSON" + }, + "edit_mode_7e07cfdf": { + "message": "Edit mode" + }, + "edit_property_dd6a1172": { + "message": "Edit Property" + }, + "ejecting_runtime_f6c90614": { + "message": "Ejecting runtime..." + }, + "emit_a_custom_event_78cf318b": { + "message": "Emit a custom event" + }, + "emit_a_telemetry_track_event_e2442842": { + "message": "Emit a telemetry track event" + }, + "emit_a_trace_event_f653ae84": { + "message": "Emit a trace event" + }, + "emit_event_32aa6583": { + "message": "Emit Event" + }, + "enable_6f5d1328": { + "message": "Enable" + }, + "enable_line_numbers_to_refer_to_code_lines_by_numb_e5ba66ea": { + "message": "Enable line numbers to refer to code lines by number." + }, + "enabled_ba7cab66": { + "message": "Enabled" + }, + "end_dialog_8f562a4c": { + "message": "End Dialog" + }, + "end_this_dialog_3ed0d50b": { + "message": "End this dialog" + }, + "end_turn_6ab71cea": { + "message": "End Turn" + }, + "end_turn_ca85b3d4": { + "message": "End turn" + }, + "endofconversation_activity_4aa21306": { + "message": "EndOfConversation activity" + }, + "english_us_1d884ccd": { + "message": "English (US)" + }, + "enter_a_manifest_url_to_add_a_new_skill_to_your_bo_57e9d660": { + "message": "Enter a manifest url to add a new skill to your bot." + }, + "enter_a_max_value_14e8ba52": { + "message": "Enter a max value" + }, + "enter_a_min_value_c3030813": { + "message": "Enter a min value" + }, + "enter_a_question_74f2e045": { + "message": "Enter a question" + }, + "enter_an_answer_84152a83": { + "message": "Enter an answer" + }, + "entities_ef09392c": { + "message": "Entities" + }, + "enum_help_text_434bd44f": { + "message": "Enum help text" + }, + "environment_68aed6d3": { + "message": "Environment" + }, + "equals_expression_47199f4e": { + "message": "Equals Expression" + }, + "error_98e81528": { + "message": "Error" + }, + "error_afac7133": { + "message": "Error:" + }, + "error_event_c079b608": { + "message": "Error event" + }, + "error_func_does_not_have_an_evaluator_it_s_not_a_b_8c660980": { + "message": "Error: { func } does not have an evaluator, it''s not a built-in function or a custom function" + }, + "error_in_ui_schema_for_title_errormsg_options_7f3c22f2": { + "message": "Error in UI schema for { title }: { errorMsg }\n{ options }" + }, + "error_occured_ejecting_runtime_76bb48e7": { + "message": "Error occured ejecting runtime!" + }, + "error_occurred_5549a6b4": { + "message": "Error occurred" + }, + "error_processing_schema_2c707cf3": { + "message": "Error Processing Schema" + }, + "event_activity_2067a94b": { + "message": "Event activity" + }, + "event_created_e9e05afc": { + "message": "Event created" + }, + "event_focused_51e447f3": { + "message": "Event focused" + }, + "event_received_457f99d6": { + "message": "Event received" + }, + "events_cf7a8c50": { + "message": "Events" + }, + "example_bot_list_9be1d563": { + "message": "Example bot list" + }, + "examples_c435f08c": { + "message": "Examples" + }, + "existing_files_in_scripts_folder_will_be_overwritt_afa8d787": { + "message": "Existing files in scripts/folder will be overwritten. Are you sure you want to continue?" + }, + "expand_2f2fadbd": { + "message": "Expand" + }, + "expand_navigation_20330d1d": { + "message": "Expand Navigation" + }, + "expected_responses_intent_intentname_44b051c": { + "message": "Expected responses (intent: #{ intentName })" + }, + "export_as_skill_764cf284": { + "message": "Export as skill" + }, + "export_assets_to_zip_d2d798ab": { + "message": "Export assets to .zip" + }, + "export_de71cd8e": { + "message": "Export" + }, + "export_json_2e2981f5": { + "message": "Export JSON" + }, + "expression_7f906a13": { + "message": "Expression" + }, + "expression_starting_with_a750efc8": { + "message": "Expression starting with =." + }, + "expression_to_evaluate_ce4095b1": { + "message": "Expression to evaluate." + }, + "extensions_7250a0bb": { + "message": "Extensions" + }, + "external_resources_will_not_be_changed_c08b0009": { + "message": "External resources will not be changed." + }, + "extract_question_and_answer_pairs_from_an_online_f_7316548e": { + "message": "Extract question-and-answer pairs from an online FAQ, product manuals, or other files. Supported formats are .tsv, .pdf, .doc, .docx, .xlsx, containing questions and answers in sequence. Learn more about knowledge base sources. Skip this step to add questions and answers manually after creation. The number of sources and file size you can add depends on the QnA service SKU you choose. Learn more about QnA Maker SKUs." + }, + "extract_question_and_answer_pairs_from_an_online_f_c1e12724": { + "message": "Extract question-and-answer pairs from an online FAQ, product manuals, or other files. Supported formats are .tsv, .pdf, .doc, .docx, .xlsx, containing questions and answers in sequence. " + }, + "extracting_qna_pairs_from_urls_eaed1b1c": { + "message": "Extracting QNA pairs from { urls }" + }, + "false_2f39ee6d": { + "message": "false" + }, + "false_eef8c169": { + "message": "False" + }, + "field_set_6e7d7f67": { + "message": "Field Set" + }, + "file_name_8fd421ff": { + "message": "File name" + }, + "file_not_found_9eec054c": { + "message": "File not found" + }, + "file_or_attachment_5467e604": { + "message": "File or attachment" + }, + "file_type_fd1ba7ee": { + "message": "File Type" + }, + "filter_dialog_de3ce43f": { + "message": "Filter Dialog" + }, + "firstselector_a3daca5d": { + "message": "FirstSelector" + }, + "folder_foldername_already_exists_4a2260e9": { + "message": "folder { folderName } already exists" + }, + "for_each_def04c48": { + "message": "For Each" + }, + "for_each_page_3b4d4b69": { + "message": "For Each Page" + }, + "form_editor_7c2b02f0": { + "message": "form editor" + }, + "form_title_baf85c7e": { + "message": "form title" + }, + "forms_380ab2ae": { + "message": "Forms" + }, + "fromtemplatename_does_not_exist_d429483c": { + "message": "fromTemplateName does not exist" + }, + "gb_7570760e": { + "message": "GB" + }, + "general_24ac26a8": { + "message": "General" + }, + "generate_44e33e72": { + "message": "Generate" + }, + "generate_dialog_b80a85b2": { + "message": "Generate dialog" + }, + "generating_form_dialog_using_schemaid_schema_faile_8062f953": { + "message": "Generating form dialog using ${ schemaId } schema failed." + }, + "get_a_new_copy_of_the_runtime_code_84970bf": { + "message": "Get a new copy of the runtime code" + }, + "get_started_50c13c6c": { + "message": "Get started!" + }, + "getting_help_ab6811b0": { + "message": "Getting Help" + }, + "getting_started_f45a7e87": { + "message": "Getting Started" + }, + "go_to_qna_all_up_view_page_d475333d": { + "message": "Go to QnA all-up view page." + }, + "got_it_2c06b54a": { + "message": "Got it!" + }, + "greeting_f906f962": { + "message": "Greeting" + }, + "handle_a_condition_f32eb8d": { + "message": "Handle a Condition" + }, + "handoff_activity_14363a20": { + "message": "Handoff activity" + }, + "handover_to_human_1a619574": { + "message": "Handover to human" + }, + "here_s_what_we_know_4e9c1731": { + "message": "Here’s what we know…" + }, + "hide_code_5dcffa94": { + "message": "Hide code" + }, + "home_351838cd": { + "message": "Home" + }, + "http_request_79847109": { + "message": "HTTP Request" + }, + "i_want_to_delete_this_bot_f81a4735": { + "message": "I want to delete this bot" + }, + "icon_name_is_file_c80dacae": { + "message": "{ icon } name is { file }" + }, + "iconname_file_icon_29976c8e": { + "message": "{ iconName } file icon" + }, + "id_already_exists_please_enter_a_unique_file_name_174913a3": { + "message": "{ id } already exists. Please enter a unique file name." + }, + "id_is_id_c0fc7a7d": { + "message": "id is { id }" + }, + "if_condition_56c9be4a": { + "message": "If Condition" + }, + "if_this_problem_persists_please_file_an_issue_on_6fbc8e2b": { + "message": "If this problem persists, please file an issue on" + }, + "if_you_already_have_a_luis_account_provide_the_inf_bede07a4": { + "message": "If you already have a LUIS account, provide the information below. If you do not have an account yet, create a (free) account first." + }, + "if_you_already_have_a_qna_account_provide_the_info_466d6a4b": { + "message": "If you already have a QNA account, provide the information below. If you do not have an account yet, create a (free) account first." + }, + "import_qna_from_url_26a99161": { + "message": "Import QnA From Url" + }, + "import_schema_75659c5f": { + "message": "Import schema" + }, + "in_production_5a70b8b4": { + "message": "In production" + }, + "in_test_63c32694": { + "message": "In test" + }, + "in_the_b_create_a_trigger_b_wizard_set_the_trigger_f9b23519": { + "message": "In the Create a trigger wizard, set the trigger type to Activities in the dropdown. Then set the Activity Type to Greeting (ConversationUpdate activity), and click the Submit button." + }, + "increment_by_step_1cf3a88": { + "message": "increment by { step }" + }, + "input_1d1d9b8e": { + "message": "Input" + }, + "install_microsoft_net_core_sdk_2de509f0": { + "message": "Install Microsoft .NET Core SDK" + }, + "install_pre_release_versions_of_composer_daily_to__ceb41b54": { + "message": "Install pre-release versions of Composer, daily, to access and test the latest features. Learn more." + }, + "install_the_update_and_restart_composer_fac30a61": { + "message": "Install the update and restart Composer." + }, + "integer_7f378275": { + "message": "integer" + }, + "integer_b08abbe9": { + "message": "Integer" + }, + "integer_constant_650191ba": { + "message": "Integer constant." + }, + "integer_constant_or_expression_to_evaluate_ec4a17da": { + "message": "Integer constant or expression to evaluate." + }, + "integer_or_expression_107f60fb": { + "message": "Integer or expression" + }, + "intent_2291200b": { + "message": "Intent" + }, + "intent_recognized_c3840853": { + "message": "Intent recognized" + }, + "intentname_is_missing_or_empty_e49db2f8": { + "message": "intentName is missing or empty" + }, + "interpolated_string_c96053f2": { + "message": "Interpolated string." + }, + "interpolated_string_e04923d3": { + "message": "Interpolated string" + }, + "interpolated_string_or_expression_to_evaluate_ba0b0ba": { + "message": "Interpolated string or expression to evaluate." + }, + "introduction_of_key_concepts_and_user_experience_e_971b32e9": { + "message": "Introduction of key concepts and user experience elements for Composer." + }, + "invoke_activity_87df4903": { + "message": "Invoke activity" + }, + "is_missing_or_empty_a551462e": { + "message": "is missing or empty" + }, + "it_s_not_a_built_in_function_or_a_custom_function_1527ed83": { + "message": "it''s not a built-in function or a custom function." + }, + "item_actions_22d0242": { + "message": "Item Actions" + }, + "item_actions_cd903bde": { + "message": "Item actions" + }, + "item_added_f3910bed": { + "message": "item added" + }, + "jan_28_2020_8beb36dc": { + "message": "Jan 28, 2020" + }, + "kb_d9c53902": { + "message": "KB" + }, + "key_cannot_be_blank_dccc1b46": { + "message": "Key cannot be blank" + }, + "key_f2a919d5": { + "message": "Key" + }, + "keys_must_be_unique_2028cef3": { + "message": "Keys must be unique" + }, + "l_startline_startcharacter_l_endline_endcharacter_72bc2e5d": { + "message": "L{ startLine }:{ startCharacter } - L{ endLine }:{ endCharacter } " + }, + "language_generation_1876f6d6": { + "message": "Language Generation" + }, + "language_generation_file_80c08efe": { + "message": "language generation file" + }, + "language_understanding_9ae3f1f6": { + "message": "Language Understanding" + }, + "language_understanding_file_a65abc2c": { + "message": "language understanding file" + }, + "languagepolicy_e754ad28": { + "message": "LanguagePolicy" + }, + "last_modified_time_is_time_b1497d3": { + "message": "Last modified time is { time }" + }, + "learn_more_a79a7918": { + "message": "Learn more" + }, + "learn_more_about_knowledge_base_sources_24369b09": { + "message": "Learn more about knowledge base sources. " + }, + "learn_more_about_qna_maker_skus_998c567": { + "message": "Learn more about QnA Maker SKUs." + }, + "learn_more_about_title_d1d3edbe": { + "message": "Learn more about { title }" + }, + "learn_more_c08939e8": { + "message": "Learn more." + }, + "learn_the_basics_2d9ae7df": { + "message": "Learn the basics" + }, + "leave_product_tour_49585718": { + "message": "Leave Product Tour?" + }, + "lg_editor_ee0184e6": { + "message": "LG editor" + }, + "lg_file_already_exist_55195d20": { + "message": "lg file already exist" + }, + "lg_file_id_not_found_6bd6869b": { + "message": "LG file { id } not found" + }, + "lg_language_client_7892c4c1": { + "message": "LG Language Client" + }, + "lg_navigation_pane_97b15c3d": { + "message": "LG Navigation Pane" + }, + "line_numbers_1bfa7fb": { + "message": "Line numbers" + }, + "line_startline_startcharacter_line_endline_endchar_372bb2f4": { + "message": "line { startLine }:{ startCharacter } - line { endLine }:{ endCharacter }" + }, + "link_to_where_this_luis_intent_is_defined_9be25fb7": { + "message": "link to where this LUIS intent is defined" + }, + "list_a034633b": { + "message": "list" + }, + "list_count_values_33ea7088": { + "message": "list - { count } values" + }, + "list_view_e33843f0": { + "message": "List view" + }, + "load_form_dialog_schema_templates_error_6a348e76": { + "message": "Load form dialog schema templates Error" + }, + "loading_25990131": { + "message": "Loading..." + }, + "loading_bde52856": { + "message": "Loading" + }, + "location_e17bd402": { + "message": "Location" + }, + "location_is_location_8c17b5de": { + "message": "location is { location }" + }, + "log_to_console_4fc23e34": { + "message": "Log to console" + }, + "login_6f3d6249": { + "message": "Login" + }, + "login_to_composer_eb42b41": { + "message": "Login to Composer" + }, + "loop_for_each_item_53eb7c5b": { + "message": "Loop: for each item" + }, + "loop_for_each_item_e09537ae": { + "message": "Loop: For each item" + }, + "loop_for_each_page_multiple_items_733e7f41": { + "message": "Loop: For each page (multiple items)" + }, + "looping_ddae56ff": { + "message": "Looping" + }, + "lu_editor_d09fb2b0": { + "message": "LU editor" + }, + "lu_file_id_not_found_8732d33e": { + "message": "LU file { id } not found" + }, + "lu_language_client_bbffcd7b": { + "message": "LU Language Client" + }, + "lu_navigation_pane_54dc5587": { + "message": "LU Navigation Pane" + }, + "luis_add4bbe3": { + "message": "LUIS" + }, + "luis_authoring_key_cfaba7dd": { + "message": "LUIS Authoring key:" + }, + "luis_authoring_region_b142f97b": { + "message": "Luis Authoring Region" + }, + "main_dialog_eed5c847": { + "message": "Main dialog" + }, + "make_a_copy_77d1233": { + "message": "Make a copy" + }, + "manage_properties_c9fa4456": { + "message": "Manage properties" + }, + "manifest_url_30824e88": { + "message": "Manifest url" + }, + "manifest_url_can_not_be_accessed_a7f147b2": { + "message": "Manifest url can not be accessed" + }, + "manifest_version_1edc004a": { + "message": "Manifest Version" + }, + "maximum_f0e8e5e4": { + "message": "Maximum" + }, + "maximum_help_text_7f3e6562": { + "message": "Maximum help text" + }, + "mb_8f9f9e84": { + "message": "MB" + }, + "message_5c38209d": { + "message": "Message" + }, + "message_deleted_628eef3a": { + "message": "Message deleted" + }, + "message_deleted_activity_85dd8915": { + "message": "Message deleted activity" + }, + "message_reaction_3704d790": { + "message": "Message reaction" + }, + "message_reaction_activity_379944e7": { + "message": "Message reaction activity" + }, + "message_received_5abfe9a0": { + "message": "Message received" + }, + "message_received_activity_1ff7c2a4": { + "message": "Message received activity" + }, + "message_updated_4f2e37fe": { + "message": "Message updated" + }, + "message_updated_activity_4997737e": { + "message": "Message updated activity" + }, + "microsoft_app_id_a7f3e591": { + "message": "Microsoft App Id" + }, + "microsoft_app_password_737ebc90": { + "message": "Microsoft App Password" + }, + "minimap_beb3be27": { + "message": "Minimap" + }, + "minimum_f31b05ab": { + "message": "Minimum" + }, + "minimum_help_text_6ab5b190": { + "message": "Minimum help text" + }, + "missing_definition_for_defname_33f2b594": { + "message": "Missing definition for { defName }" + }, + "missing_fields_1c88ab71": { + "message": "missing fields" + }, + "modification_rejected_6a6e8322": { + "message": "Modification Rejected" + }, + "modify_active_dialog_edcf4a45": { + "message": "Modify active dialog" + }, + "modify_this_dialog_3c38af24": { + "message": "Modify this dialog" + }, + "more_options_e89670cf": { + "message": "More options" + }, + "mostspecificselector_2cf1a6ae": { + "message": "MostSpecificSelector" + }, + "move_abf00365": { + "message": "Move" + }, + "move_down_eaae3426": { + "message": "Move Down" + }, + "move_up_b1c4d3a5": { + "message": "Move Up" + }, + "msft_ignite_ai_show_e131edef": { + "message": "MSFT Ignite AI Show" + }, + "multi_choice_839b54bb": { + "message": "Multi-choice" + }, + "multiple_wildcards_26f50b6c": { + "message": "multiple wildcards" + }, + "must_be_an_expression_error_477cbda6": { + "message": "must be an expression: { error }" + }, + "must_have_a_name_d5c5c464": { + "message": "Must have a name" + }, + "my_publish_profile_2ed56828": { + "message": "My Publish Profile" + }, + "name_1aed4a1b": { + "message": "Name" + }, + "name_and_save_your_skill_manifest_cfd672b7": { + "message": "Name and save your skill manifest." + }, + "name_cannot_include_special_characters_or_spaces_59a1950b": { + "message": "Name cannot include special characters or spaces" + }, + "name_copy_55d27c1a": { + "message": "{ name }_Copy" + }, + "name_is_name_abc564f9": { + "message": "Name is { name }" + }, + "name_is_required_29d28f8d": { + "message": "{ name } is required" + }, + "name_of_skill_dialog_to_call_201707f3": { + "message": "Name of skill dialog to call" + }, + "name_of_the_property_f3cae657": { + "message": "Name of the property" + }, + "navigation_control_94f2649e": { + "message": "navigation control" + }, + "navigation_pane_e587b73c": { + "message": "Navigation pane" + }, + "navigation_panel_530ae4ac": { + "message": "Navigation panel" + }, + "navigation_path_8b299e64": { + "message": "Navigation Path" + }, + "navigation_to_see_actions_3be545c9": { + "message": "navigation to see actions" + }, + "new_13daf639": { + "message": "New" + }, + "new_template_49e6f0f2": { + "message": "New template" + }, + "new_trigger_331c811b": { + "message": "New Trigger .." + }, + "new_update_available_30534ea5": { + "message": "New update available" + }, + "new_value_3c1ce474": { + "message": "New value" + }, + "newsectionplaceholdersectionname_5fc0d21": { + "message": "_NewSectionPlaceHolderSectionName" + }, + "next_40e12421": { + "message": "Next" + }, + "no_editor_for_type_8b5593c5": { + "message": "No Editor for { type }" + }, + "no_extensions_installed_4b925277": { + "message": "No extensions installed" + }, + "no_form_dialog_schema_matches_your_filtering_crite_a198cb62": { + "message": "No form dialog schema matches your filtering criteria!" + }, + "no_lu_file_with_name_id_fb21315d": { + "message": "NO LU FILE WITH NAME { id }" + }, + "no_lu_or_qna_file_with_name_id_21cfe9dc": { + "message": "NO LU OR QNA FILE WITH NAME { id }" + }, + "no_name_e082310e": { + "message": "[no name]" + }, + "no_search_results_1ba50423": { + "message": "No search results" + }, + "no_updates_available_cecd904d": { + "message": "No updates available" + }, + "no_wildcard_ff439e76": { + "message": "no wildcard" + }, + "node_menu_e2aa8092": { + "message": "Node menu" + }, + "not_a_single_template_e37f894": { + "message": "Not a single template" + }, + "not_yet_published_669e37b3": { + "message": "Not yet published" + }, + "notification_list_ee2abb6c": { + "message": "Notification list" + }, + "notification_message_msg_7d55d08e": { + "message": "Notification Message { msg }" + }, + "notification_type_263c145d": { + "message": "Notification type" + }, + "notifications_cbfa7704": { + "message": "Notifications" + }, + "nov_12_2019_96ec5473": { + "message": "Nov 12, 2019" + }, + "number_a6dc44e": { + "message": "Number" + }, + "number_constant_1938e96e": { + "message": "Number constant." + }, + "number_constant_or_expression_to_evaluate_1098771": { + "message": "Number constant or expression to evaluate." + }, + "number_dc1c178": { + "message": "number" + }, + "number_or_expression_55c7f9f": { + "message": "Number or expression" + }, + "numeric_field_c2564f69": { + "message": "numeric field" + }, + "oauth_login_b6aa9534": { + "message": "OAuth login" + }, + "object_345070f6": { + "message": "Object" + }, + "object_constant_77052b91": { + "message": "Object constant." + }, + "object_or_expression_89903fa1": { + "message": "Object or expression" + }, + "object_or_expression_to_evaluate_699c8827": { + "message": "Object or expression to evaluate." + }, + "off_5385ef3f": { + "message": "Off" + }, + "ok_c47544a2": { + "message": "OK" + }, + "ok_ff1b646a": { + "message": "Ok" + }, + "okay_1772bbeb": { + "message": "Okay" + }, + "on_8f7190e5": { + "message": "On" + }, + "onboarding_8407871c": { + "message": "Onboarding" + }, + "ondialogevents_types_3dc569b5": { + "message": "OnDialogEvents Types" + }, + "open_e0beb7b9": { + "message": "Open" + }, + "open_inline_editor_a5aabcfa": { + "message": "Open inline editor" + }, + "optional_221bcc9d": { + "message": "Optional" + }, + "optional_db6daecb": { + "message": "optional" + }, + "optional_properties_2c23c7c6": { + "message": "Optional properties" + }, + "or_4f7d4edb": { + "message": "Or: " + }, + "origin_lg_file_not_found_in_store_d194cdbc": { + "message": "origin lg file not found in store" + }, + "origin_lu_file_not_found_in_store_19e5cc8f": { + "message": "origin lu file not found in store" + }, + "original_ce7b7e64": { + "message": " - Original" + }, + "other_1c6d9c79": { + "message": "Other" + }, + "output_5023cf84": { + "message": "Output" + }, + "page_number_cdee4179": { + "message": "Page number" + }, + "parse_failed_at_name_error_8f562bda": { + "message": "Parse failed at { name }: { error }" + }, + "paste_5963d1c1": { + "message": "Paste" + }, + "please_add_at_least_minitems_endpoint_5439fd74": { + "message": "Please add at least { minItems } endpoint" + }, + "please_enter_a_value_for_key_77cfc097": { + "message": "Please enter a value for { key }" + }, + "please_enter_an_event_name_a148275a": { + "message": "Please enter an event name" + }, + "please_input_a_manifest_url_d726edbf": { + "message": "Please input a manifest Url" + }, + "please_input_regex_pattern_5cd659a2": { + "message": "Please input regEx pattern" + }, + "please_log_in_before_continuing_b6eace13": { + "message": "Please log in before continuing." + }, + "please_return_to_design_view_to_start_the_onboardi_a561af9d": { + "message": "Please return to Design View to start the Onboarding tutorial." + }, + "please_select_a_event_type_200c4c9f": { + "message": "Please select a event type" + }, + "please_select_a_specific_qna_file_to_import_qna_d71d871": { + "message": "Please select a specific qna file to import QnA" + }, + "please_select_a_trigger_type_67417abb": { + "message": "Please select a trigger type" + }, + "please_select_a_valid_endpoint_bf608af1": { + "message": "Please select a valid endpoint" + }, + "please_select_a_version_of_the_manifest_schema_4a3efbb1": { + "message": "Please select a version of the manifest schema" + }, + "please_select_an_activity_type_92f4a8a1": { + "message": "Please select an activity type" + }, + "populate_your_knowledge_base_bb2d3605": { + "message": "Populate your Knowledge Base" + }, + "press_enter_to_add_this_item_or_tab_to_move_to_the_6beb8a14": { + "message": "press Enter to add this item or Tab to move to the next interactive element" + }, + "press_enter_to_add_this_name_and_advance_to_the_ne_6a2ae080": { + "message": "press Enter to add this name and advance to the next row, or press Tab to advance to the value field" + }, + "previous_bd2ac015": { + "message": "Previous" + }, + "previous_folder_e7eeb306": { + "message": "previous folder" + }, + "privacy_290109ea": { + "message": "Privacy" + }, + "privacy_button_b58e437": { + "message": "Privacy button" + }, + "progress_of_total_87de8616": { + "message": "{ progress }% of { total }" + }, + "prompt_for_a_date_5d2c689e": { + "message": "Prompt for a date" + }, + "prompt_for_a_date_or_a_time_d2df7f90": { + "message": "Prompt for a date or a time" + }, + "prompt_for_a_file_or_an_attachment_1bf18e7e": { + "message": "Prompt for a file or an attachment" + }, + "prompt_for_a_number_84999edb": { + "message": "Prompt for a number" + }, + "prompt_for_attachment_727d4fac": { + "message": "Prompt for Attachment" + }, + "prompt_for_confirmation_dc85565c": { + "message": "Prompt for confirmation" + }, + "prompt_for_text_5c524f80": { + "message": "Prompt for text" + }, + "prompt_with_multi_choice_f428542f": { + "message": "Prompt with multi-choice" + }, + "property_array_help_text_126845dc": { + "message": "Property array help text" + }, + "property_description_8d21ea2e": { + "message": "Property description." + }, + "property_editor_preferences_ad7a4b62": { + "message": "Property editor preferences" + }, + "property_has_error_s_please_fix_the_error_s_for_th_e994c143": { + "message": "Property has error(s), please fix the error(s) for this property." + }, + "property_name_914371f5": { + "message": "Property name" + }, + "property_name_help_text_421a5f7f": { + "message": "Property name help text" + }, + "property_quick_actions_d3498f14": { + "message": "Property quick actions" + }, + "property_required_help_text_57f027ad": { + "message": "Property required help text" + }, + "property_title_f2b443b7": { + "message": "Property title" + }, + "property_type_95689fa5": { + "message": "Property type." + }, + "property_type_e38cf7e4": { + "message": "Property Type" + }, + "property_type_help_text_8a2c202d": { + "message": "Property type help text" + }, + "provide_a_brief_description_it_will_appear_on_the__f962eb38": { + "message": "Provide a brief description. It will appear on the publish history list." + }, + "pseudo_1a319287": { + "message": "Pseudo" + }, + "publish_5211dca3": { + "message": "Publish" + }, + "publish_configuration_d759a4e3": { + "message": "Publish Configuration" + }, + "publish_destination_type_5f8c200a": { + "message": "Publish Destination Type" + }, + "publish_models_9a36752a": { + "message": "Publish models" + }, + "publish_profiles_cf8d307b": { + "message": "Publish Profiles" + }, + "publish_to_selected_profile_c0998a2": { + "message": "Publish to selected profile" + }, + "published_4bb5209e": { + "message": "Published" + }, + "publishing_d63a8f2d": { + "message": "Publishing" + }, + "qna_28ee5e26": { + "message": "QnA" + }, + "qna_editor_9eb94b02": { + "message": "QnA editor" + }, + "qna_file_37705cb7": { + "message": "qna file" + }, + "qna_intent_recognized_49c3d797": { + "message": "QnA Intent recognized" + }, + "qna_navigation_pane_b79ebcbf": { + "message": "Qna Navigation Pane" + }, + "qna_region_5a864ef8": { + "message": "QnA Region" + }, + "qna_subscription_key_ed72a47": { + "message": "QNA Subscription key:" + }, + "question_9121487": { + "message": "Question" + }, + "randomselector_4a5274f1": { + "message": "RandomSelector" + }, + "range_selection_30caeea5": { + "message": "Range Selection" + }, + "re_prompt_for_input_c3b5b3ab": { + "message": "Re-prompt for input" + }, + "recent_bots_53585911": { + "message": "Recent Bots" + }, + "recognizer_type_dc591e16": { + "message": "Recognizer Type" + }, + "recognizers_cefce9d1": { + "message": "Recognizers" + }, + "redo_363c58b7": { + "message": "Redo" + }, + "redo_is_not_supported_b743e4dc": { + "message": "Redo is not supported" + }, + "refer_to_the_syntax_documentation_here_df8dc9b4": { + "message": "Refer to the syntax documentation here." + }, + "regex_intent_is_already_defined_df095c1f": { + "message": "RegEx { intent } is already defined" + }, + "regular_expression_855557bf": { + "message": "Regular Expression" + }, + "release_1af20f26": { + "message": "Release: " + }, + "reloading_49d2f661": { + "message": "Reloading" + }, + "remove_f47dc62a": { + "message": "Remove" + }, + "repeat_this_dialog_83ca994e": { + "message": "Repeat this dialog" + }, + "replace_this_dialog_e304015e": { + "message": "Replace this dialog" + }, + "reprompt_dialog_event_c42d2c33": { + "message": "Reprompt dialog event" + }, + "required_5f7ef8c0": { + "message": "Required" + }, + "required_a6089a96": { + "message": "required" + }, + "required_properties_dfb0350d": { + "message": "Required properties" + }, + "requiredtext_ff8f722f": { + "message": "{ requiredText }" + }, + "requiredtext_priority_priority_4293288f": { + "message": "{ requiredText } | Priority: { priority }" + }, + "response_is_response_3cd62f8f": { + "message": "Response is { response }" + }, + "responses_12d6df1d": { + "message": "Responses" + }, + "restart_bot_34e36428": { + "message": "Restart Bot" + }, + "review_and_generate_63dec712": { + "message": "Review and generate" + }, + "rollback_26326307": { + "message": "Rollback" + }, + "runtime_already_exists_f181f5ec": { + "message": "Runtime already exists" + }, + "runtime_code_location_4e94ee12": { + "message": "Runtime code location" + }, + "runtime_config_a2904ff9": { + "message": "Runtime Config" + }, + "sample_phrases_5d78fa35": { + "message": "Sample Phrases" + }, + "sample_phrases_are_phrases_a966b47f": { + "message": "Sample Phrases are { phrases }" + }, + "save_11a80ec3": { + "message": "Save" + }, + "save_as_9e0cf70b": { + "message": "Save as" + }, + "save_your_skill_manifest_63bf5f26": { + "message": "Save your skill manifest" + }, + "schema_24739a48": { + "message": "Schema" + }, + "schemaid_doesn_t_exists_select_an_schema_to_edit_o_9cccc954": { + "message": "{ schemaId } doesn''t exists, select an schema to edit or create a new one" + }, + "schemanum_plural_0_no_schemas_1_one_schema_other_s_b3523dc": { + "message": "{ schemaNum, plural,\n =0 {No schemas}\n =1 {One schema}\n other {# schemas}\n} have been found.\n { schemaNum, select,\n 0 {}\n other {Press down arrow key to navigate the search results}\n}" + }, + "schemas_74566170": { + "message": "Schemas" + }, + "scripts_successfully_updated_3a75d57f": { + "message": "Scripts successfully updated." + }, + "sdk_runtime_packages_22d8ed1a": { + "message": "SDK runtime packages" + }, + "search_280d00bd": { + "message": "Search" + }, + "search_for_extensions_on_npm_c5ca65d9": { + "message": "Search for extensions on npm" + }, + "see_log_4b833bf7": { + "message": "See Log" + }, + "select_a_bot_e1c4dc2b": { + "message": "Select a Bot" + }, + "select_a_trigger_on_the_left_a4b41558": { + "message": "Select a trigger on the left" + }, + "select_all_f73344a8": { + "message": "Select all" + }, + "select_an_activity_type_c982fcbe": { + "message": "Select an activity type" + }, + "select_an_event_type_3d7108f1": { + "message": "Select an event type" + }, + "select_an_schema_to_edit_or_create_a_new_one_59c7326a": { + "message": "Select an schema to edit or create a new one" + }, + "select_language_to_delete_d1662d3d": { + "message": "Select language to delete" + }, + "select_manifest_version_4f5b1230": { + "message": "Select manifest version" + }, + "select_options_9ee7b227": { + "message": "Select options" + }, + "select_property_type_5f3ab685": { + "message": "select property type" + }, + "select_runtime_version_to_add_d63d383b": { + "message": "Select runtime version to add" + }, + "select_the_language_that_bot_will_be_able_to_under_1f2bcb96": { + "message": "Select the language that bot will be able to understand (User input) and respond to (Bot responses).\n To make this bot available in other languages, click “Add’ to create a copy of the default language, and translate the content into the new language." + }, + "select_which_dialogs_are_included_in_the_skill_man_281ef8c9": { + "message": "Select which dialogs are included in the skill manifest" + }, + "select_which_tasks_this_skill_can_perform_172b0eae": { + "message": "Select which tasks this skill can perform" + }, + "selection_field_86d1dc94": { + "message": "selection field" + }, + "selectors_2dcb3029": { + "message": "Selectors" + }, + "send_a_response_1a917d7e": { + "message": "Send a response" + }, + "send_an_http_request_aa32fd2": { + "message": "Send an HTTP request" + }, + "send_messages_c48b239": { + "message": "Send Messages" + }, + "sentence_wrap_930c8ced": { + "message": "Sentence wrap" + }, + "session_expired_12aaf414": { + "message": "Session expired" + }, + "set_a_property_4258d8d7": { + "message": "Set a property" + }, + "set_destination_folder_f76e0259": { + "message": "Set destination folder" + }, + "set_properties_7415af3c": { + "message": "Set properties" + }, + "set_up_your_bot_75009578": { + "message": "Set up your bot" + }, + "settings_5aa0fd0c": { + "message": "Settings" + }, + "settings_contains_detailed_information_about_your__54aa601d": { + "message": "Settings contains detailed information about your bot. For security reasons, they are hidden by default. To test your bot or publish to Azure, you may need to provide these settings." + }, + "settings_editor_b5246933": { + "message": "Settings editor" + }, + "settings_menu_c99ecc6d": { + "message": "Settings menu" + }, + "show_all_notifications_55bf7858": { + "message": "Show All Notifications" + }, + "show_code_f3e9d1cc": { + "message": "Show code" + }, + "show_keys_3072a5b8": { + "message": "Show keys" + }, + "show_skill_manifest_5d0abde1": { + "message": "Show skill manifest" + }, + "sign_out_user_6845d640": { + "message": "Sign out user" + }, + "skill_dialog_name_1bbf0eff": { + "message": "Skill Dialog Name" + }, + "skill_endpoint_b563491e": { + "message": "Skill Endpoint" + }, + "skill_endpoints_e4e3d8c1": { + "message": "Skill endpoints" + }, + "skill_host_endpoint_b1088d0": { + "message": "Skill Host Endpoint" + }, + "skill_manifest_endpoint_is_configured_improperly_e083731d": { + "message": "Skill manifest endpoint is configured improperly" + }, + "skillname_manifest_ef3d9fed": { + "message": "{ skillName } Manifest" + }, + "skills_49cccd6a": { + "message": "Skills" + }, + "skip_this_step_to_add_questions_and_answers_manual_ed1b9f80": { + "message": "Skip this step to add questions and answers manually after creation. The number of sources and file size you can add depends on the QnA service SKU you choose. " + }, + "sorry_something_went_wrong_with_connecting_bot_run_7d6785e3": { + "message": "Sorry, something went wrong with connecting bot runtime" + }, + "sorry_something_went_wrong_with_publishing_try_aga_306a48f5": { + "message": "Sorry, something went wrong with publishing. Try again or exit out of this task." + }, + "sorted_a_to_z_915b2ed3": { + "message": "Sorted A to Z" + }, + "sorted_z_to_a_722f1567": { + "message": "Sorted Z to A" + }, + "spaces_and_special_characters_are_not_allowed_20d47684": { + "message": "Spaces and special characters are not allowed." + }, + "spaces_and_special_characters_are_not_allowed_use__48acec3c": { + "message": "Spaces and special characters are not allowed. Use letters, numbers, -, or _." + }, + "specify_a_name_and_description_for_your_new_dialog_86eb3130": { + "message": "Specify a name and description for your new dialog." + }, + "specify_a_name_description_and_location_for_your_n_667f1438": { + "message": "Specify a name, description, and location for your new bot project." + }, + "start_bot_25ecad14": { + "message": "Start Bot" + }, + "start_bot_failed_d75647d5": { + "message": "Start bot failed" + }, + "start_command_a085f2ec": { + "message": "Start command" + }, + "state_is_state_a2b8943": { + "message": "State is { state }" + }, + "status_e7fdbe06": { + "message": "Status" + }, + "step_of_setlength_43c73821": { + "message": "{ step } of { setLength }" + }, + "string_24369b3": { + "message": "String" + }, + "string_field_db491570": { + "message": "string field" + }, + "string_or_expression_c441b45c": { + "message": "String or expression" + }, + "submit_a3cc6859": { + "message": "Submit" + }, + "synonyms_optional_afe5cdb1": { + "message": "Synonyms (Optional)" + }, + "tb_149f379c": { + "message": "TB" + }, + "templatename_is_missing_or_empty_23e6b06e": { + "message": "templateName is missing or empty" + }, + "terms_of_use_6542769b": { + "message": "Terms of Use" + }, + "test_in_emulator_b1b3c278": { + "message": "Test in Emulator" + }, + "test_your_bot_3cd1f4bb": { + "message": "Test your bot" + }, + "text_7f4593da": { + "message": "Text" + }, + "the_api_messages_endpoint_for_the_skill_f318dc63": { + "message": "The /api/messages endpoint for the skill." + }, + "the_callback_url_for_the_skill_host_e20e1012": { + "message": "The callback url for the skill host." + }, + "the_dialog_you_have_tried_to_delete_is_currently_u_a37c7a02": { + "message": "The dialog you have tried to delete is currently used in the below dialog(s). Removing this dialog will cause your Bot to malfunction without additional action." + }, + "the_following_lufile_s_are_invalid_c61ea748": { + "message": "The Following LuFile(s) are invalid: \n" + }, + "the_main_dialog_is_named_after_your_bot_it_is_the__3d9864f": { + "message": "The main dialog is named after your bot. It is the root and entry point of a bot." + }, + "the_manifest_can_be_edited_and_refined_manually_if_9269e3f2": { + "message": "The manifest can be edited and refined manually if and where needed." + }, + "the_microsoft_app_id_that_will_be_calling_the_skil_955424de": { + "message": "The Microsoft App Id that will be calling the skill." + }, + "the_microsoft_app_password_that_will_be_calling_th_acf9e1ea": { + "message": "The Microsoft App Password that will be calling the skill." + }, + "the_page_you_are_looking_for_can_t_be_found_a3b75795": { + "message": "The page you are looking for can''t be found." + }, + "the_return_type_does_not_match_2ae72548": { + "message": "the return type does not match" + }, + "the_root_bot_is_not_a_bot_project_d1495cf6": { + "message": "The root bot is not a bot project" + }, + "the_welcome_message_is_triggered_by_the_i_conversa_a3ff58f8": { + "message": "The welcome message is triggered by the ConversationUpdate event. To add a new ConversationUpdate trigger:" + }, + "there_are_no_kind_properties_e299287e": { + "message": "There are no { kind } properties." + }, + "there_was_an_error_74ed3c58": { + "message": "There was an error" + }, + "there_was_error_creating_your_kb_53b31ff3": { + "message": "There was error creating your KB" + }, + "these_examples_bring_together_all_of_the_best_prac_ca1b89c7": { + "message": "These examples bring together all of the best practices and supporting components we''ve identified through building of conversational experiences." + }, + "these_tasks_will_be_used_to_generate_the_manifest__2791be0e": { + "message": "These tasks will be used to generate the manifest and describe the capabilities of this skill to those who may want to use it." + }, + "this_configures_a_data_driven_dialog_via_a_collect_c7fa4389": { + "message": "This configures a data driven dialog via a collection of events and actions." + }, + "this_dialog_has_no_trigger_yet_d1f1d173": { + "message": "This dialog has no trigger yet." + }, + "this_is_a_required_field_acb9837e": { + "message": "This is a required field." + }, + "this_is_a_severity_notification_9beabb58": { + "message": "This is a { severity } notification" + }, + "this_is_the_language_used_for_composer_s_user_inte_ab7fa82e": { + "message": "This is the language used for Composer’s user interface." + }, + "this_language_will_be_copied_and_used_as_the_basis_573515e4": { + "message": "This language will be copied and used as the basis (and fallback language) for the translation." + }, + "this_operation_cannot_be_completed_the_skill_is_al_4886d311": { + "message": "This operation cannot be completed. The skill is already part of the Bot Project" + }, + "this_trigger_type_is_not_supported_by_the_regex_re_2c3b7c46": { + "message": "This trigger type is not supported by the RegEx recognizer and will not be fired." + }, + "this_trigger_type_is_not_supported_by_the_regex_re_dc3eefa2": { + "message": "This trigger type is not supported by the RegEx recognizer. To ensure this trigger is fired, change the recognizer type." + }, + "this_url_is_duplicated_a0768f44": { + "message": "This url is duplicated" + }, + "this_version_of_the_content_is_out_of_date_and_you_5e878f29": { + "message": "This version of the content is out of date, and your last change was rejected. The content will be automatically refreshed." + }, + "this_will_delete_the_dialog_and_its_contents_do_yo_9b48fa3c": { + "message": "This will delete the Dialog and its contents. Do you wish to continue?" + }, + "this_will_delete_the_profile_do_you_wish_to_contin_61b54894": { + "message": "This will delete the profile. Do you wish to continue?" + }, + "this_will_open_your_emulator_application_if_you_do_ba277151": { + "message": "This will open your Emulator application. If you don''t yet have the Bot Framework Emulator installed, you can download it here." + }, + "time_2b5aac58": { + "message": "Time" + }, + "tip_8f74cd0": { + "message": "tip" + }, + "tips_80d0da2b": { + "message": "tips" + }, + "title_ee03d132": { + "message": "Title" + }, + "title_msg_ee91458d": { + "message": "{ title }. { msg }" + }, + "to_customize_the_welcome_message_select_the_i_send_9b4bf4f": { + "message": "To customize the welcome message, select the Send a response action in the Visual Editor. Then in the Form Editor on the right, you can edit the bot''s welcome message in the Language Generation field." + }, + "to_learn_more_about_the_lg_file_format_read_the_do_ef6e083d": { + "message": "> To learn more about the LG file format, read the documentation at\n> { lgHelp }" + }, + "to_learn_more_about_the_lu_file_format_read_the_do_cac5ffc9": { + "message": "> To learn more about the LU file format, read the documentation at\n> { LU_HELP }" + }, + "to_learn_more_about_the_qna_file_format_read_the_d_1ce18259": { + "message": "> To learn more about the QnA file format, read the documentation at\n> { QNA_HELP }" + }, + "to_make_your_bot_available_for_others_as_a_skill_w_f2c19b9c": { + "message": "To make your bot available for others as a skill, we need to generate a manifest." + }, + "to_run_this_bot_composer_needs_net_core_sdk_d1551038": { + "message": "To run this bot, Composer needs .NET Core SDK." + }, + "to_understand_what_the_user_says_your_dialog_needs_4e791611": { + "message": "To understand what the user says, your dialog needs a \"Recognizer\"; that includes example words and sentences that users may use." + }, + "to_understand_what_the_user_says_your_dialog_needs_957034cc": { + "message": "To understand what the user says, your dialog needs an ''IRecognizer'' that includes example words and sentences that users may use." + }, + "to_which_language_will_you_be_translating_your_bot_77219d69": { + "message": "To which language will you be translating your bot?" + }, + "toggle_extension_e41de2d2": { + "message": "Toggle extension" + }, + "toolbar_bafd4228": { + "message": "toolbar" + }, + "total_mb_531a3721": { + "message": "{ total }MB" + }, + "trigger_phrases_f6754fa": { + "message": "Trigger phrases" + }, + "trigger_phrases_intent_intentname_a1b62148": { + "message": "Trigger phrases (intent: #{ intentName })" + }, + "triggers_connect_intents_with_bot_responses_think__fdfc97ea": { + "message": "Triggers connect intents with bot responses. Think of a trigger as one capability of your bot. So your bot is a collection of triggers. To add a new trigger, click the Add button in the toolbar, and then select the Add a new trigger option from the dropdown menu." + }, + "true_1900d7ae": { + "message": "true" + }, + "true_b9327890": { + "message": "True" + }, + "trueselector_40702dda": { + "message": "TrueSelector" + }, + "try_again_ad656c3c": { + "message": "Try again" + }, + "type_and_press_enter_33a2905d": { + "message": "Type and press enter" + }, + "type_c8106334": { + "message": "Type" + }, + "type_could_not_be_loaded_65ebaf86": { + "message": "{ type } could not be loaded" + }, + "type_dialog_name_14a50769": { + "message": "Type dialog name" + }, + "type_form_dialog_schema_name_b767985c": { + "message": "Type form dialog schema name" + }, + "typing_activity_6b634ae": { + "message": "Typing activity" + }, + "unable_to_determine_recognizer_type_from_data_valu_2960f526": { + "message": "Unable to determine recognizer type from data: { value }" + }, + "undo_a7be8fef": { + "message": "Undo" + }, + "undo_is_not_supported_ecd6f9fc": { + "message": "Undo is not supported" + }, + "uninstall_8730233": { + "message": "Uninstall" + }, + "unknown_intent_44b962ba": { + "message": "Unknown intent" + }, + "unknown_intent_recognized_1953f9be": { + "message": "Unknown intent recognized" + }, + "unknown_state_23f73afb": { + "message": "Unknown State" + }, + "unused_8d193e3": { + "message": "Unused" + }, + "update_a_an_activity_previously_sent_during_the_co_f0619cca": { + "message": "Update a an activity previously sent during the conversation" + }, + "update_activity_2b05e6c6": { + "message": "Update activity" + }, + "update_available_b637d767": { + "message": "Update available" + }, + "update_complete_c5163fbf": { + "message": "Update complete" + }, + "update_failed_2c87428c": { + "message": "Update failed" + }, + "update_folder_name_error_24563bf6": { + "message": "Update Folder Name Error" + }, + "update_in_progress_f65e6b29": { + "message": "Update in progress" + }, + "update_scripts_a3a483e": { + "message": "Update scripts" + }, + "update_scripts_c58771a2": { + "message": "Update Scripts" + }, + "updating_scripts_e17a5722": { + "message": "Updating scripts... " + }, + "url_22a5f3b8": { + "message": "URL" + }, + "url_8c4ff7d2": { + "message": "Url" + }, + "url_should_start_with_http_s_9ca55d94": { + "message": "Url should start with http[s]://" + }, + "use_custom_runtime_d7d323fd": { + "message": "Use custom runtime" + }, + "used_3d895705": { + "message": "Used" + }, + "used_in_126529e5": { + "message": "Used In" + }, + "user_input_673e4a89": { + "message": "User input" + }, + "user_input_a6ff658d": { + "message": "User Input" + }, + "user_is_typing_790cb502": { + "message": "User is typing" + }, + "validating_35b79a96": { + "message": "Validating..." + }, + "validation_rules_efd3144d": { + "message": "Validation Rules" + }, + "value_d842f16d": { + "message": "Value" + }, + "version_5599c321": { + "message": "Version" + }, + "version_version_a051e218": { + "message": "Version { version }" + }, + "video_tutorials_79eb26ca": { + "message": "Video tutorials:" + }, + "view_ba339f93": { + "message": "View" + }, + "view_kb_c382e495": { + "message": "View KB" + }, + "view_on_npm_2051324d": { + "message": "View on npm" + }, + "vishwac_sena_45910bf0": { + "message": "Vishwac Sena" + }, + "visual_editor_216472d": { + "message": "Visual editor" + }, + "warning_53c98b03": { + "message": "Warning!" + }, + "warning_the_action_you_are_about_to_take_cannot_be_1071a3c3": { + "message": "Warning: the action you are about to take cannot be undone. Going further will delete this bot and any related files in the bot project folder." + }, + "we_have_created_a_sample_bot_to_help_you_get_start_95a58922": { + "message": "We have created a sample bot to help you get started with Composer. Click here to open the bot." + }, + "we_need_to_define_the_endpoints_for_the_skill_to_a_5dc98d90": { + "message": "We need to define the endpoints for the skill to allow other bots to interact with it." + }, + "weather_bot_c38920cd": { + "message": "Weather Bot" + }, + "welcome_73d18b4d": { + "message": "Welcome!" + }, + "welcome_dd4e7151": { + "message": "Welcome" + }, + "westeurope_cabf9688": { + "message": "westeurope" + }, + "westus_dc50d800": { + "message": "westus" + }, + "what_can_the_user_accomplish_through_this_conversa_7ddb03a1": { + "message": "What can the user accomplish through this conversation? For example, BookATable, OrderACoffee etc." + }, + "what_is_the_name_of_the_custom_event_b28a7b3": { + "message": "What is the name of the custom event?" + }, + "what_is_the_name_of_this_trigger_2642266e": { + "message": "What is the name of this trigger" + }, + "what_is_the_name_of_this_trigger_luis_17b60a23": { + "message": "What is the name of this trigger (LUIS)" + }, + "what_is_the_name_of_this_trigger_regex_f77376d7": { + "message": "What is the name of this trigger (RegEx)" + }, + "what_is_the_name_of_your_bot_a571c565": { + "message": "What is the name of your bot?" + }, + "what_is_the_type_of_this_trigger_d2701744": { + "message": "What is the type of this trigger?" + }, + "what_your_bot_says_to_the_user_this_is_a_template__a8d2266d": { + "message": "What your bot says to the user. This is a template used to create the outgoing message. It can include language generation rules, properties from memory, and other features.\n\nFor example, to define variations that will be chosen at random, write:\n- hello\n- hi" + }, + "what_your_bot_says_to_the_user_visit_a_target_blan_7735479": { + "message": "What your Bot says to the user. Visit the documentation a reference of capabilities." + }, + "when_deleting_a_language_only_the_content_will_be__8f7f8dee": { + "message": "When deleting a language, only the content will be removed. The flow and logic of the conversation and dialog will remain functional." + }, + "when_done_switch_to_the_newly_created_language_and_862b7dd9": { + "message": "When done, switch to the newly created language and start the (manual) translation process." + }, + "when_multiple_people_are_working_with_models_you_w_32b48099": { + "message": "When multiple people are working with models you want to be able to work with models independently from each other tied to the source control." + }, + "which_activity_type_18333457": { + "message": "Which activity type?" + }, + "which_bot_do_you_want_to_open_974bb1e5": { + "message": "Which bot do you want to open?" + }, + "which_event_6e655d2b": { + "message": "Which event?" + }, + "yes_dde87d5": { + "message": "Yes" + }, + "you_are_about_to_publish_your_bot_to_the_profile_b_79a6a226": { + "message": "You are about to publish your bot to the profile below. Do you want to proceed?" + }, + "you_can_create_a_new_bot_from_scratch_with_compose_1486288c": { + "message": "You can create a new bot from scratch with Composer, or start with a template." + }, + "you_can_define_and_manage_b_intents_b_here_each_in_721b8a0c": { + "message": "You can define and manage intents here. Each intent describes a particular user intention through utterances (i.e. user says). Intents are often triggers of your bot." + }, + "you_can_manage_all_bot_responses_here_make_good_us_5e6e1953": { + "message": "You can manage all bot responses here. Make good use of the templates to create sophisticated response logic based on your own needs." + }, + "you_do_not_have_permission_to_save_bots_here_56cc10c7": { + "message": "You do not have permission to save bots here" + }, + "your_bot_creation_journey_on_composer_131c1a8b": { + "message": "Your bot creation journey on Composer" + }, + "your_bot_is_using_luis_and_qna_for_natural_languag_53830684": { + "message": "Your bot is using LUIS and QNA for natural language understanding." + }, + "your_knowledge_base_is_ready_6ecc1871": { + "message": "Your knowledge base is ready!" + } +} \ No newline at end of file diff --git a/Composer/plugins/azurePublish/yarn.lock b/Composer/plugins/azurePublish/yarn.lock index 90ad7db056..683e4038e2 100644 --- a/Composer/plugins/azurePublish/yarn.lock +++ b/Composer/plugins/azurePublish/yarn.lock @@ -160,9 +160,12 @@ version "1.0.0" dependencies: debug "^4.1.1" + fs-extra "^9.0.1" globby "^11.0.0" + node-fetch "^2.6.1" passport "^0.4.1" path-to-regexp "^6.1.0" + tar "^6.0.5" "@bfc/indexers@../../packages/lib/indexers": version "0.0.0" @@ -912,6 +915,11 @@ charenc@~0.0.1: resolved "https://registry.yarnpkg.com/charenc/-/charenc-0.0.2.tgz#c0a1d2f3a7092e03774bfa83f14c0fc5790a8667" integrity sha1-wKHS86cJLgN3S/qD8UwPxXkKhmc= +chownr@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/chownr/-/chownr-2.0.0.tgz#15bfbe53d2eab4cf70f18a8cd68ebe5b3cb1dece" + integrity sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ== + clean-stack@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/clean-stack/-/clean-stack-1.3.0.tgz#9e821501ae979986c46b1d66d2d432db2fd4ae31" @@ -1418,6 +1426,13 @@ fs-extra@^9.0.1: jsonfile "^6.0.1" universalify "^1.0.0" +fs-minipass@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-2.1.0.tgz#7f5036fdbf12c63c169190cbe4199c852271f9fb" + integrity sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg== + dependencies: + minipass "^3.0.0" + fs.realpath@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" @@ -1956,6 +1971,26 @@ minimist@1.2.5: resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602" integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw== +minipass@^3.0.0: + version "3.1.3" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-3.1.3.tgz#7d42ff1f39635482e15f9cdb53184deebd5815fd" + integrity sha512-Mgd2GdMVzY+x3IJ+oHnVM+KG3lA5c8tnabyJKmHSaG2kAGpudxuOf8ToDkhumF7UzME7DecbQE9uOZhNm7PuJg== + dependencies: + yallist "^4.0.0" + +minizlib@^2.1.1: + version "2.1.2" + resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-2.1.2.tgz#e90d3466ba209b932451508a11ce3d3632145931" + integrity sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg== + dependencies: + minipass "^3.0.0" + yallist "^4.0.0" + +mkdirp@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" + integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== + moment-timezone@*, moment-timezone@^0.5.28: version "0.5.31" resolved "https://botbuilder.myget.org/F/botbuilder-v4-js-daily/npm/moment-timezone/-/moment-timezone-0.5.31.tgz#9c40d8c5026f0c7ab46eda3d63e49c155148de05" @@ -1998,7 +2033,7 @@ nice-try@^1.0.4: resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366" integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ== -node-fetch@^2.6.0, node-fetch@~2.6.0: +node-fetch@^2.6.0, node-fetch@^2.6.1, node-fetch@~2.6.0: version "2.6.1" resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052" integrity sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw== @@ -2449,6 +2484,18 @@ tar-stream@^2.1.4: inherits "^2.0.3" readable-stream "^3.1.1" +tar@^6.0.5: + version "6.0.5" + resolved "https://registry.yarnpkg.com/tar/-/tar-6.0.5.tgz#bde815086e10b39f1dcd298e89d596e1535e200f" + integrity sha512-0b4HOimQHj9nXNEAA7zWwMM91Zhhba3pspja6sQbgTpynOJf+bkjBnfybNYzbpLbnwXnbyB4LOREvlyXLkCHSg== + dependencies: + chownr "^2.0.0" + fs-minipass "^2.0.0" + minipass "^3.0.0" + minizlib "^2.1.1" + mkdirp "^1.0.3" + yallist "^4.0.0" + to-regex-range@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" @@ -2631,6 +2678,11 @@ yallist@^3.0.2: resolved "https://botbuilder.myget.org/F/botbuilder-v4-js-daily/npm/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd" integrity sha1-27fa+b/YusmrRev2ArjLrQ1dCP0= +yallist@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" + integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== + zip-stream@^4.0.0: version "4.0.2" resolved "https://registry.yarnpkg.com/zip-stream/-/zip-stream-4.0.2.tgz#3a20f1bd7729c2b59fd4efa04df5eb7a5a217d2e" From d88477ba194f6fd2b1ed7f63584a976897b82c00 Mon Sep 17 00:00:00 2001 From: Soroush Date: Mon, 12 Oct 2020 14:44:51 -0700 Subject: [PATCH 3/8] Update strings --- .../VisualFormDialogSchemaEditor.tsx | 4 +- .../packages/server/src/locales/en-US.json | 87 +++++++++++++------ 2 files changed, 62 insertions(+), 29 deletions(-) diff --git a/Composer/packages/client/src/pages/form-dialog/VisualFormDialogSchemaEditor.tsx b/Composer/packages/client/src/pages/form-dialog/VisualFormDialogSchemaEditor.tsx index 9f8747eaa3..302931e36b 100644 --- a/Composer/packages/client/src/pages/form-dialog/VisualFormDialogSchemaEditor.tsx +++ b/Composer/packages/client/src/pages/form-dialog/VisualFormDialogSchemaEditor.tsx @@ -47,9 +47,9 @@ const editorTopBarStyles = classNamesFunction()({ }); type Props = { - projectId?: string; - generationInProgress?: boolean; + projectId: string; schemaId: string; + generationInProgress?: boolean; templates: string[]; onChange: (id: string, content: string) => void; onGenerate: (schemaId: string) => void; diff --git a/Composer/packages/server/src/locales/en-US.json b/Composer/packages/server/src/locales/en-US.json index ede69fe405..b8519f2b5a 100644 --- a/Composer/packages/server/src/locales/en-US.json +++ b/Composer/packages/server/src/locales/en-US.json @@ -17,6 +17,9 @@ "a_dialog_file_must_have_a_name_123ff67d": { "message": "a dialog file must have a name" }, + "a_form_dialog_enables_your_bot_to_collect_pieces_o_fdd3fe56": { + "message": "A form dialog enables your bot to collect pieces of information ." + }, "a_minimap_gives_an_overview_of_your_source_code_fo_9a897f4f": { "message": "A minimap gives an overview of your source code for quick navigation and code understanding." }, @@ -233,6 +236,9 @@ "are_you_sure_you_want_to_exit_the_onboarding_produ_c2de1b23": { "message": "Are you sure you want to exit the Onboarding Product Tour? You can restart the tour in the onboarding settings." }, + "are_you_sure_you_want_to_remove_form_dialog_schema_67c927ce": { + "message": "Are you sure you want to remove form dialog schema \"{ id }\"?" + }, "are_you_sure_you_want_to_remove_propertyname_8a793e4f": { "message": "Are you sure you want to remove \"{ propertyName }\"?" }, @@ -560,6 +566,9 @@ "create_a_new_dialog_21d84b82": { "message": "Create a new dialog" }, + "create_a_new_form_dialog_schema_by_clicking_above_34b80531": { + "message": "Create a new form dialog schema by clicking + above." + }, "create_a_new_skill_manifest_or_select_which_one_yo_a97e9616": { "message": "Create a new skill manifest or select which one you want to edit" }, @@ -575,6 +584,9 @@ "create_folder_error_38aa86f5": { "message": "Create Folder Error" }, + "create_form_dialog_bb98a4f2": { + "message": "Create form dialog" + }, "create_from_knowledge_base_qna_maker_7422486": { "message": "Create from knowledge base (QnA Maker)" }, @@ -689,6 +701,9 @@ "delete_bot_73586104": { "message": "Delete Bot" }, + "delete_form_dialog_schema_892df4e8": { + "message": "Delete form dialog schema" + }, "delete_language_1527609d": { "message": "Delete language" }, @@ -743,6 +758,9 @@ "dialog_started_912507c": { "message": "Dialog started" }, + "dialog_with_the_name_value_already_exists_62838518": { + "message": "Dialog with the name: { value } already exists." + }, "dialogfactory_missing_schema_5c3255c4": { "message": "DialogFactory missing schema." }, @@ -773,6 +791,9 @@ "downloading_bb6fb34b": { "message": "Downloading..." }, + "duplicate_31cec192": { + "message": "Duplicate" + }, "duplicate_dialog_name_824f9fce": { "message": "Duplicate dialog name" }, @@ -791,12 +812,6 @@ "duplicated_intents_recognized_d3908424": { "message": "Duplicated intents recognized" }, - "each_page_of_pagesizestring_in_propstring_a7e23b00": { - "message": "Each page of { pageSizeString } in { propString }" - }, - "each_value_in_ae285cd9": { - "message": "Each value in" - }, "early_adopters_e8db7999": { "message": "Early adopters" }, @@ -923,9 +938,6 @@ "error_processing_schema_2c707cf3": { "message": "Error Processing Schema" }, - "event_48c2be6e": { - "message": " (Event)" - }, "event_activity_2067a94b": { "message": "Event activity" }, @@ -1031,12 +1043,18 @@ "for_each_page_3b4d4b69": { "message": "For Each Page" }, + "form_dialog_schema_actions_c9f1c26": { + "message": "Form dialog schema actions" + }, "form_editor_7c2b02f0": { "message": "form editor" }, "form_title_baf85c7e": { "message": "form title" }, + "forms_380ab2ae": { + "message": "Forms" + }, "fromtemplatename_does_not_exist_d429483c": { "message": "fromTemplateName does not exist" }, @@ -1052,6 +1070,9 @@ "generate_dialog_b80a85b2": { "message": "Generate dialog" }, + "generating_form_dialog_using_schemaid_schema_faile_8062f953": { + "message": "Generating form dialog using ${ schemaId } schema failed." + }, "get_a_new_copy_of_the_runtime_code_84970bf": { "message": "Get a new copy of the runtime code" }, @@ -1091,9 +1112,6 @@ "home_351838cd": { "message": "Home" }, - "host_ef9acfae": { - "message": "Host " - }, "http_request_79847109": { "message": "HTTP Request" }, @@ -1208,6 +1226,9 @@ "item_added_f3910bed": { "message": "item added" }, + "itemcount_plural_0_no_schemas_1_one_schema_other_s_e1aea7f": { + "message": "{ itemCount, plural,\n =0 {No schemas}\n =1 {One schema}\n other {# schemas}\n} have been found.\n { itemCount, select,\n 0 {}\n other {Press down arrow key to navigate the search results}\n}" + }, "jan_28_2020_8beb36dc": { "message": "Jan 28, 2020" }, @@ -1298,6 +1319,9 @@ "list_view_e33843f0": { "message": "List view" }, + "load_form_dialog_schema_templates_error_6a348e76": { + "message": "Load form dialog schema templates Error" + }, "loading_25990131": { "message": "Loading..." }, @@ -1535,6 +1559,9 @@ "no_extensions_installed_4b925277": { "message": "No extensions installed" }, + "no_form_dialog_schema_matches_your_filtering_crite_a198cb62": { + "message": "No form dialog schema matches your filtering criteria!" + }, "no_lu_file_with_name_id_fb21315d": { "message": "NO LU FILE WITH NAME { id }" }, @@ -1760,6 +1787,9 @@ "prompt_with_multi_choice_f428542f": { "message": "Prompt with multi-choice" }, + "property_actions_9c1a20d9": { + "message": "Property actions" + }, "property_array_help_text_126845dc": { "message": "Property array help text" }, @@ -1778,8 +1808,8 @@ "property_name_help_text_421a5f7f": { "message": "Property name help text" }, - "property_quick_actions_d3498f14": { - "message": "Property quick actions" + "property_required_help_text_57f027ad": { + "message": "Property required help text" }, "property_title_f2b443b7": { "message": "Property title" @@ -1922,15 +1952,6 @@ "restart_bot_34e36428": { "message": "Restart Bot" }, - "result_b1f14e91": { - "message": " = Result" - }, - "result_property_100ef34f": { - "message": " = Result property" - }, - "return_value_b386d1b": { - "message": "= Return value" - }, "review_and_generate_63dec712": { "message": "Review and generate" }, @@ -1964,6 +1985,12 @@ "schema_24739a48": { "message": "Schema" }, + "schemaid_doesn_t_exists_select_an_schema_to_edit_o_9cccc954": { + "message": "{ schemaId } doesn''t exists, select an schema to edit or create a new one" + }, + "schemas_74566170": { + "message": "Schemas" + }, "scripts_successfully_updated_3a75d57f": { "message": "Scripts successfully updated." }, @@ -1994,6 +2021,9 @@ "select_an_event_type_3d7108f1": { "message": "Select an event type" }, + "select_an_schema_to_edit_or_create_a_new_one_59c7326a": { + "message": "Select an schema to edit or create a new one" + }, "select_language_to_delete_d1662d3d": { "message": "Select language to delete" }, @@ -2315,9 +2345,6 @@ "toggle_extension_e41de2d2": { "message": "Toggle extension" }, - "token_property_e53a19d9": { - "message": " = Token Property" - }, "toolbar_bafd4228": { "message": "toolbar" }, @@ -2357,6 +2384,9 @@ "type_dialog_name_14a50769": { "message": "Type dialog name" }, + "type_form_dialog_schema_name_b767985c": { + "message": "Type form dialog schema name" + }, "typing_activity_6b634ae": { "message": "Typing activity" }, @@ -2462,6 +2492,9 @@ "view_ba339f93": { "message": "View" }, + "view_dialog_f5151228": { + "message": "View dialog" + }, "view_kb_c382e495": { "message": "View KB" }, @@ -2573,4 +2606,4 @@ "your_knowledge_base_is_ready_6ecc1871": { "message": "Your knowledge base is ready!" } -} +} \ No newline at end of file From 5e91674d72dcf4e0b0e4ed67e8f5cacb2b39c649 Mon Sep 17 00:00:00 2001 From: Soroush Date: Tue, 13 Oct 2020 10:19:39 -0700 Subject: [PATCH 4/8] tests --- .../pages/notifications/useNotifications.test.tsx | 6 ++++++ .../lib/indexers/src/formDialogSchemaIndexer.ts | 15 ++++++++------- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/Composer/packages/client/__tests__/pages/notifications/useNotifications.test.tsx b/Composer/packages/client/__tests__/pages/notifications/useNotifications.test.tsx index 326a4d7d06..bbed6681fb 100644 --- a/Composer/packages/client/__tests__/pages/notifications/useNotifications.test.tsx +++ b/Composer/packages/client/__tests__/pages/notifications/useNotifications.test.tsx @@ -15,6 +15,7 @@ import { schemasState, currentProjectIdState, botDiagnosticsState, + formDialogSchemaIdsState, } from '../../../src/recoilModel'; import mockProjectResponse from '../../../src/recoilModel/dispatchers/__tests__/mocks/mockProjectResponse.json'; @@ -94,6 +95,7 @@ const state = { }, }, }, + formDialogSchemas: [{ id: '1', content: '{}' }], }; const initRecoilState = ({ set }) => { @@ -104,6 +106,10 @@ const initRecoilState = ({ set }) => { set(botDiagnosticsState(state.projectId), state.diagnostics); set(settingsState(state.projectId), state.settings); set(schemasState(state.projectId), mockProjectResponse.schemas); + set( + formDialogSchemaIdsState(state.projectId), + state.formDialogSchemas.map((fds) => fds.id) + ); }; describe('useNotification hooks', () => { diff --git a/Composer/packages/lib/indexers/src/formDialogSchemaIndexer.ts b/Composer/packages/lib/indexers/src/formDialogSchemaIndexer.ts index ddaf6cea9a..0d770d2d17 100644 --- a/Composer/packages/lib/indexers/src/formDialogSchemaIndexer.ts +++ b/Composer/packages/lib/indexers/src/formDialogSchemaIndexer.ts @@ -1,16 +1,17 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -import { FileInfo } from '@bfc/shared'; +import { FileExtensions, FileInfo, FormDialogSchema } from '@bfc/shared'; import { getBaseName } from './utils/help'; -const index = (formDialogSchemaFiles: FileInfo[]) => { - return formDialogSchemaFiles.map((file) => { - const { content, lastModified, name } = file; - return { content, id: getBaseName(name, '.form-dialog'), lastModified }; - }); -}; +const index = (files: FileInfo[]): FormDialogSchema[] => + files + .filter((file) => file.name.endsWith(FileExtensions.FormDialogSchema)) + .map((file) => { + const { content, name } = file; + return { id: getBaseName(name, FileExtensions.FormDialogSchema), content }; + }); export const formDialogSchemaIndexer = { index, From cd21736f0a382c8dd3bb90bfc8cd83e70b2b563d Mon Sep 17 00:00:00 2001 From: Soroush Date: Tue, 13 Oct 2020 10:23:46 -0700 Subject: [PATCH 5/8] tests --- .../__tests__/formDialogSchemaIndexer.test.ts | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 Composer/packages/lib/indexers/__tests__/formDialogSchemaIndexer.test.ts diff --git a/Composer/packages/lib/indexers/__tests__/formDialogSchemaIndexer.test.ts b/Composer/packages/lib/indexers/__tests__/formDialogSchemaIndexer.test.ts new file mode 100644 index 0000000000..d5182a7e2b --- /dev/null +++ b/Composer/packages/lib/indexers/__tests__/formDialogSchemaIndexer.test.ts @@ -0,0 +1,33 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +import { FileInfo } from '@bfc/shared'; + +import { formDialogSchemaIndexer } from '../src/formDialogSchemaIndexer'; + +const files: FileInfo[] = [ + { name: 'file1.json', content: '', lastModified: '', relativePath: '', path: '' }, + { name: 'file2.dialog', content: '', lastModified: '', relativePath: '', path: '' }, + { name: 'file3.form-dialog', content: '', lastModified: '', relativePath: '', path: '' }, + { name: 'file4.lu', content: '', lastModified: '', relativePath: '', path: '' }, + { name: 'file5.lg', content: '', lastModified: '', relativePath: '', path: '' }, + { name: 'file6.botproj', content: '', lastModified: '', relativePath: '', path: '' }, + { name: 'file7.qna', content: '', lastModified: '', relativePath: '', path: '' }, + { name: 'file8.form-dialog', content: '', lastModified: '', relativePath: '', path: '' }, +]; + +const expected = [ + { id: 'file3', content: '' }, + { id: 'file8', content: '' }, +]; + +const { index } = formDialogSchemaIndexer; + +describe('formDialogSchemaIndexer', () => { + it('Should return form dialog schema files', () => { + const formDialogSchemas = index(files); + + expect(formDialogSchemas.length).toBe(2); + expect(formDialogSchemas).toEqual(expected); + }); +}); From 6fcb314ff8a017a78adfd4a8d35dd04ebb4fd2e6 Mon Sep 17 00:00:00 2001 From: Soroush Date: Tue, 13 Oct 2020 12:14:20 -0700 Subject: [PATCH 6/8] Flag and minor UX --- .../packages/client/config/webpack.config.js | 4 ++++ .../CreateFormDialogSchemaModal.tsx | 2 +- .../packages/client/src/utils/pageLinks.ts | 18 +++++++++++------- .../components/FormDialogPropertiesEditor.tsx | 4 ++-- 4 files changed, 18 insertions(+), 10 deletions(-) diff --git a/Composer/packages/client/config/webpack.config.js b/Composer/packages/client/config/webpack.config.js index 4014117ae6..f026477c45 100644 --- a/Composer/packages/client/config/webpack.config.js +++ b/Composer/packages/client/config/webpack.config.js @@ -22,6 +22,10 @@ const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPl const getClientEnvironment = require('./env'); const paths = require('./paths'); +new webpack.DefinePlugin({ + 'process.env.COMPOSER_ENABLE_FORMS': JSON.stringify(process.env.COMPOSER_ENABLE_FORMS), +}); + // Source maps are resource heavy and can cause out of memory issue for large source files. const shouldUseSourceMap = process.env.GENERATE_SOURCEMAP !== 'false'; diff --git a/Composer/packages/client/src/pages/form-dialog/CreateFormDialogSchemaModal.tsx b/Composer/packages/client/src/pages/form-dialog/CreateFormDialogSchemaModal.tsx index d9bcd8a8a0..bca1809dce 100644 --- a/Composer/packages/client/src/pages/form-dialog/CreateFormDialogSchemaModal.tsx +++ b/Composer/packages/client/src/pages/form-dialog/CreateFormDialogSchemaModal.tsx @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +import { DialogTypes, DialogWrapper } from '@bfc/ui-shared'; import formatMessage from 'format-message'; import { DefaultButton, PrimaryButton } from 'office-ui-fabric-react/lib/Button'; import { DialogFooter } from 'office-ui-fabric-react/lib/Dialog'; @@ -9,7 +10,6 @@ import { TextField } from 'office-ui-fabric-react/lib/TextField'; import React, { useCallback } from 'react'; import { useRecoilValue } from 'recoil'; -import { DialogTypes, DialogWrapper } from '../../components/DialogWrapper'; import { nameRegex } from '../../constants'; import { FieldConfig, useForm } from '../../hooks/useForm'; import { dialogsState } from '../../recoilModel'; diff --git a/Composer/packages/client/src/utils/pageLinks.ts b/Composer/packages/client/src/utils/pageLinks.ts index 4f5e580312..8f761c137d 100644 --- a/Composer/packages/client/src/utils/pageLinks.ts +++ b/Composer/packages/client/src/utils/pageLinks.ts @@ -64,13 +64,17 @@ export const topLinks = (projectId: string, openedDialogId: string, pluginPages: exact: true, disabled: !botLoaded, }, - { - to: `/bot/${projectId}/forms`, - iconName: 'Table', - labelName: formatMessage('Forms'), - exact: false, - disabled: !botLoaded, - }, + ...(process.env.COMPOSER_ENABLE_FORMS + ? [ + { + to: `/bot/${projectId}/forms`, + iconName: 'Table', + labelName: formatMessage('Forms'), + exact: false, + disabled: !botLoaded, + }, + ] + : []), ]; if (process.env.COMPOSER_AUTH_PROVIDER === 'abs-h') { diff --git a/Composer/packages/form-dialogs/src/components/FormDialogPropertiesEditor.tsx b/Composer/packages/form-dialogs/src/components/FormDialogPropertiesEditor.tsx index ca6dbcba76..75de6dbc6f 100644 --- a/Composer/packages/form-dialogs/src/components/FormDialogPropertiesEditor.tsx +++ b/Composer/packages/form-dialogs/src/components/FormDialogPropertiesEditor.tsx @@ -112,7 +112,7 @@ export const FormDialogPropertiesEditor = React.memo((props: Props) => { iconProps: { iconName: 'Undo' }, title: formatMessage('Undo'), ariaLabel: formatMessage('Undo'), - disabled: !canUndo(), + disabled: isGenerating || !canUndo(), onClick: () => { undo(); }, @@ -123,7 +123,7 @@ export const FormDialogPropertiesEditor = React.memo((props: Props) => { iconProps: { iconName: 'Redo' }, title: formatMessage('Redo'), ariaLabel: formatMessage('Redo'), - disabled: !canRedo(), + disabled: isGenerating || !canRedo(), onClick: () => { redo(); }, From e93f84273d465d0d35239a9d6e51026fca0892b7 Mon Sep 17 00:00:00 2001 From: Soroush Date: Tue, 13 Oct 2020 14:24:34 -0700 Subject: [PATCH 7/8] minor await --- .../recoilModel/dispatchers/formDialogs.ts | 2 +- .../packages/server/src/locales/en-US.json | 162 ++++++++++++++++++ 2 files changed, 163 insertions(+), 1 deletion(-) diff --git a/Composer/packages/client/src/recoilModel/dispatchers/formDialogs.ts b/Composer/packages/client/src/recoilModel/dispatchers/formDialogs.ts index 10de63d905..3b1257c9bd 100644 --- a/Composer/packages/client/src/recoilModel/dispatchers/formDialogs.ts +++ b/Composer/packages/client/src/recoilModel/dispatchers/formDialogs.ts @@ -75,7 +75,7 @@ export const formDialogsDispatcher = () => { const response = await httpClient.post(`/formDialogs/${projectId}/generate`, { name: schemaId, }); - reloadProject(callbackHelpers, response); + await reloadProject(callbackHelpers, response); } catch (error) { set(applicationErrorState, { message: error.message, diff --git a/Composer/packages/server/src/locales/en-US.json b/Composer/packages/server/src/locales/en-US.json index b8519f2b5a..6750058d0f 100644 --- a/Composer/packages/server/src/locales/en-US.json +++ b/Composer/packages/server/src/locales/en-US.json @@ -251,6 +251,9 @@ "are_you_sure_you_want_to_uninstall_these_extension_cf6265e8": { "message": "Are you sure you want to uninstall these extensions?" }, + "arm_template_bc8441bb": { + "message": "ARM Template" + }, "array_643947ee": { "message": "Array" }, @@ -287,6 +290,27 @@ "available_skills_95c114ac": { "message": "Available Skills" }, + "azure_app_service_plan_a285fb06": { + "message": "Azure App Service Plan" + }, + "azure_application_insights_c80028e4": { + "message": "Azure Application Insights" + }, + "azure_blob_storage_b5503630": { + "message": "Azure Blob Storage" + }, + "azure_bot_service_3afc983": { + "message": "Azure Bot Service" + }, + "azure_cosmos_db_a82b5d09": { + "message": "Azure Cosmos DB" + }, + "back_2900f52a": { + "message": "Back" + }, + "basic_assistant_d23bde15": { + "message": "Basic Assistant" + }, "been_used_5daccdb2": { "message": "Been used" }, @@ -335,6 +359,12 @@ "bot_asks_5e9f0202": { "message": "Bot Asks" }, + "bot_configured_for_speech_66fc8e57": { + "message": "Bot Configured for Speech" + }, + "bot_configured_for_text_a67eca78": { + "message": "Bot Configured for Text" + }, "bot_framework_composer_enables_developers_and_mult_ce0e42a9": { "message": "Bot Framework Composer enables developers and multi-disciplinary teams to build all kinds of conversational experiences, using the latest components from the Bot Framework: SDK, LG, LU, and declarative file formats, all without writing code." }, @@ -359,6 +389,9 @@ "bot_language_active_7cf9dc78": { "message": "Bot language (active)" }, + "bot_name_bbd0779d": { + "message": "Bot Name" + }, "bot_name_is_botname_a28c2d05": { "message": "Bot name is { botName }" }, @@ -377,6 +410,9 @@ "bot_settings_ce2783e4": { "message": "Bot Settings" }, + "bot_web_app_ca8b1468": { + "message": "Bot Web App" + }, "branch_if_else_391e5681": { "message": "Branch: If/else" }, @@ -434,6 +470,12 @@ "choose_one_2c4277df": { "message": "Choose One" }, + "choose_one_7feacdb6": { + "message": "Choose one:" + }, + "choose_your_assistant_7ee96d89": { + "message": "Choose Your Assistant" + }, "chris_whitten_11df1f35": { "message": "Chris Whitten" }, @@ -488,9 +530,21 @@ "conditionalselector_ed2031f0": { "message": "ConditionalSelector" }, + "configuration_summary_f9b92424": { + "message": "Configuration Summary" + }, "configure_composer_to_start_your_bot_using_runtime_fe37dadf": { "message": "Configure Composer to start your bot using runtime code you can customize and control." }, + "configured_with_enterprise_scenarios_calendar_who__e9e7e75a": { + "message": "Configured with enterprise scenarios, calendar, who bot, professional chit-chat." + }, + "configured_with_hospitality_scenarios_bing_search__f71c09c0": { + "message": "Configured with hospitality scenarios, Bing search and caring chit-chat." + }, + "configured_with_simple_conversational_capability_l_d42f128c": { + "message": "Configured with simple conversational capability like greeting, chit-chat & more." + }, "configures_default_language_model_to_use_if_there__f09f1acd": { "message": "Configures default language model to use if there is no culture code in the file name (Default: en-us)" }, @@ -521,6 +575,9 @@ "connect_to_qna_knowledgebase_4b324132": { "message": "Connect to QnA Knowledgebase" }, + "content_1440204b": { + "message": "Content" + }, "continue_loop_22635585": { "message": "Continue loop" }, @@ -563,6 +620,9 @@ "create_a_name_for_the_project_which_will_be_used_t_57e9b690": { "message": "Create a name for the project which will be used to name the application: (projectname-environment-LUfilename)" }, + "create_a_new_bot_or_choose_from_virtual_assistant__7c1e6674": { + "message": "Create a new bot or choose from Virtual assistant templates." + }, "create_a_new_dialog_21d84b82": { "message": "Create a new dialog" }, @@ -635,6 +695,9 @@ "custom_recognizer_951bab90": { "message": "Custom recognizer" }, + "customize_your_assistant_e4038b74": { + "message": "Customize your assistant" + }, "cut_c8c92681": { "message": "Cut" }, @@ -902,6 +965,9 @@ "enter_an_answer_84152a83": { "message": "Enter an answer" }, + "enterprise_assistant_434df551": { + "message": "Enterprise Assistant" + }, "entities_ef09392c": { "message": "Entities" }, @@ -1007,6 +1073,9 @@ "extracting_qna_pairs_from_urls_eaed1b1c": { "message": "Extracting QNA pairs from { urls }" }, + "fallback_text_e5ff1cb7": { + "message": "Fallback Text" + }, "false_2f39ee6d": { "message": "false" }, @@ -1085,6 +1154,9 @@ "getting_started_f45a7e87": { "message": "Getting Started" }, + "give_your_bot_personality_multi_language_capabilit_f00c2c93": { + "message": "Give your bot personality, multi-language capabilities and more; you can edit this later in Bot Settings." + }, "go_to_qna_all_up_view_page_d475333d": { "message": "Go to QnA all-up view page." }, @@ -1094,6 +1166,9 @@ "greeting_f906f962": { "message": "Greeting" }, + "greeting_message_ce77bbb2": { + "message": "Greeting Message" + }, "handle_a_condition_f32eb8d": { "message": "Handle a Condition" }, @@ -1106,15 +1181,30 @@ "here_s_what_we_know_4e9c1731": { "message": "Here’s what we know…" }, + "hi_there_here_are_some_things_that_i_can_do_32909722": { + "message": "Hi there! Here are some things that I can do!" + }, "hide_code_5dcffa94": { "message": "Hide code" }, "home_351838cd": { "message": "Home" }, + "hospitality_assistant_c330e403": { + "message": "Hospitality Assistant" + }, + "hosts_your_bot_application_9dd01ffc": { + "message": "Hosts your Bot application." + }, + "hosts_your_qna_maker_knowledgebases_64713aee": { + "message": "Hosts your QnA Maker knowledgebases" + }, "http_request_79847109": { "message": "HTTP Request" }, + "i_am_sorry_i_didn_t_understand_that_f86e307a": { + "message": "I am sorry, I didn''t understand that" + }, "i_want_to_delete_this_bot_f81a4735": { "message": "I want to delete this bot" }, @@ -1589,6 +1679,9 @@ "not_yet_published_669e37b3": { "message": "Not yet published" }, + "notes_c42e0fd5": { + "message": "Notes" + }, "notification_list_ee2abb6c": { "message": "Notification list" }, @@ -1700,6 +1793,15 @@ "paste_5963d1c1": { "message": "Paste" }, + "personality_1381bf78": { + "message": "{ personality }" + }, + "personality_cf47985f": { + "message": "Personality" + }, + "personality_choice_3dfd8268": { + "message": "Personality Choice" + }, "please_add_at_least_minitems_endpoint_5439fd74": { "message": "Please add at least { minItems } endpoint" }, @@ -1826,6 +1928,9 @@ "provide_a_brief_description_it_will_appear_on_the__f962eb38": { "message": "Provide a brief description. It will appear on the publish history list." }, + "provisioning_summary_2be9422f": { + "message": "Provisioning Summary" + }, "pseudo_1a319287": { "message": "Pseudo" }, @@ -1865,6 +1970,15 @@ "qna_intent_recognized_49c3d797": { "message": "QnA Intent recognized" }, + "qna_maker_azure_search_service_88a403db": { + "message": "QnA Maker Azure Search Service" + }, + "qna_maker_c313ed6e": { + "message": "QnA Maker" + }, + "qna_maker_web_app_3da183bd": { + "message": "QnA Maker Web App" + }, "qna_navigation_pane_b79ebcbf": { "message": "Qna Navigation Pane" }, @@ -1943,6 +2057,9 @@ "requiredtext_priority_priority_4293288f": { "message": "{ requiredText } | Priority: { priority }" }, + "resource_374a9df": { + "message": "Resource" + }, "response_is_response_3cd62f8f": { "message": "Response is { response }" }, @@ -2003,6 +2120,9 @@ "search_for_extensions_on_npm_c5ca65d9": { "message": "Search for extensions on npm" }, + "search_index_for_your_qna_maker_knowledgebases_22700ca7": { + "message": "Search index for your QnA Maker knowledgebases." + }, "see_log_4b833bf7": { "message": "See Log" }, @@ -2021,6 +2141,9 @@ "select_an_event_type_3d7108f1": { "message": "Select an event type" }, + "select_an_option_9f5dfb55": { + "message": "Select an option" + }, "select_an_schema_to_edit_or_create_a_new_one_59c7326a": { "message": "Select an schema to edit or create a new one" }, @@ -2048,6 +2171,9 @@ "select_which_tasks_this_skill_can_perform_172b0eae": { "message": "Select which tasks this skill can perform" }, + "selected_assistant_type_83d58f54": { + "message": "Selected Assistant Type" + }, "selection_field_86d1dc94": { "message": "selection field" }, @@ -2156,6 +2282,9 @@ "specify_a_name_description_and_location_for_your_n_667f1438": { "message": "Specify a name, description, and location for your new bot project." }, + "speech_16063aed": { + "message": "Speech" + }, "start_bot_25ecad14": { "message": "Start Bot" }, @@ -2186,6 +2315,15 @@ "submit_a3cc6859": { "message": "Submit" }, + "subscription_keys_for_language_understanding_cogni_105e039f": { + "message": "Subscription keys for Language Understanding Cognitive Service." + }, + "subscription_keys_for_qna_maker_cognitive_service__91da99a4": { + "message": "Subscription keys for QnA Maker Cognitive Service which facilitates the bot personality you selected." + }, + "summary_24ad7681": { + "message": "Summary" + }, "synonyms_optional_afe5cdb1": { "message": "Synonyms (Optional)" }, @@ -2210,15 +2348,24 @@ "the_api_messages_endpoint_for_the_skill_f318dc63": { "message": "The /api/messages endpoint for the skill." }, + "the_azure_bot_service_resource_stores_configuratio_b7b4e73d": { + "message": "The Azure Bot Service resource stores configuration information that allows your Virtual Assistant to be accessed on the supported Channels and provide OAuth authentication." + }, "the_callback_url_for_the_skill_host_e20e1012": { "message": "The callback url for the skill host." }, "the_dialog_you_have_tried_to_delete_is_currently_u_a37c7a02": { "message": "The dialog you have tried to delete is currently used in the below dialog(s). Removing this dialog will cause your Bot to malfunction without additional action." }, + "the_following_customizations_will_be_applied_to_yo_7d3384cf": { + "message": "The following customizations will be applied to your bot" + }, "the_following_lufile_s_are_invalid_c61ea748": { "message": "The Following LuFile(s) are invalid: \n" }, + "the_following_will_be_provisioned_to_enable_your_b_6c52fdac": { + "message": "The following will be provisioned to enable your bot" + }, "the_main_dialog_is_named_after_your_bot_it_is_the__3d9864f": { "message": "The main dialog is named after your bot. It is the root and entry point of a bot." }, @@ -2462,6 +2609,18 @@ "used_in_126529e5": { "message": "Used In" }, + "used_to_capture_conversation_and_application_telem_cae2c2a4": { + "message": "Used to capture conversation and application telemetry." + }, + "used_to_host_your_bot_web_app_and_qna_maker_web_ap_514dc11b": { + "message": "Used to host your Bot Web App and QnA Maker Web App." + }, + "used_to_store_conversation_state_11f348bd": { + "message": "Used to store conversation state." + }, + "used_to_store_conversation_transcripts_d80440ab": { + "message": "Used to store conversation transcripts." + }, "user_input_673e4a89": { "message": "User input" }, @@ -2555,6 +2714,9 @@ "what_is_the_type_of_this_trigger_d2701744": { "message": "What is the type of this trigger?" }, + "what_will_your_virtual_assistant_say_if_it_does_no_972dd9ab": { + "message": "What will your Virtual Assistant say if it does not understand the user?" + }, "what_your_bot_says_to_the_user_this_is_a_template__a8d2266d": { "message": "What your bot says to the user. This is a template used to create the outgoing message. It can include language generation rules, properties from memory, and other features.\n\nFor example, to define variations that will be chosen at random, write:\n- hello\n- hi" }, From 914040e5145c0b879e28c96732adf63c2f6db22f Mon Sep 17 00:00:00 2001 From: Soroush Date: Wed, 14 Oct 2020 10:54:33 -0700 Subject: [PATCH 8/8] Updated strings --- Composer/packages/server/src/locales/en-US.json | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/Composer/packages/server/src/locales/en-US.json b/Composer/packages/server/src/locales/en-US.json index 6750058d0f..cfc7db70c3 100644 --- a/Composer/packages/server/src/locales/en-US.json +++ b/Composer/packages/server/src/locales/en-US.json @@ -1085,6 +1085,9 @@ "field_set_6e7d7f67": { "message": "Field Set" }, + "fields_must_be_either_all_strings_or_all_fieldset__d3df28c": { + "message": "fields must be either all strings or all fieldset objects" + }, "file_name_8fd421ff": { "message": "File name" }, @@ -1865,6 +1868,9 @@ "progress_of_total_87de8616": { "message": "{ progress }% of { total }" }, + "prompt_configurations_ab47cd3f": { + "message": "Prompt Configurations" + }, "prompt_for_a_date_5d2c689e": { "message": "Prompt for a date" }, @@ -2633,6 +2639,9 @@ "validating_35b79a96": { "message": "Validating..." }, + "validation_b10c677c": { + "message": "Validation" + }, "validation_rules_efd3144d": { "message": "Validation Rules" },