diff --git a/Composer/packages/client/src/CreationFlow/DefineConversation/index.js b/Composer/packages/client/src/CreationFlow/DefineConversation/index.js index acc19be84a..b152b40c90 100644 --- a/Composer/packages/client/src/CreationFlow/DefineConversation/index.js +++ b/Composer/packages/client/src/CreationFlow/DefineConversation/index.js @@ -1,12 +1,11 @@ -import React, { useState, useContext, useEffect, useRef, Fragment } from 'react'; +import React, { useState, useContext, useEffect, useRef } from 'react'; +import { DialogFooter, PrimaryButton, DefaultButton } from 'office-ui-fabric-react'; import formatMessage from 'format-message'; -import { DialogFooter, PrimaryButton, DefaultButton, Stack, StackItem, TextField } from 'office-ui-fabric-react'; +import { DialogModal } from '../../CreationFlow/DialogModal/index'; import { LocationSelectContent } from '../LocationBrowser/LocationSelectContent'; -import { styles as wizardStyles } from '../StepWizard/styles'; import { StoreContext } from './../../store'; -import { name, description } from './styles'; const nameRegex = /^[a-zA-Z0-9-_.]+$/; @@ -23,16 +22,14 @@ const validateForm = data => { return errors; }; -export function DefineConversation(props) { - const { onSubmit, onGetErrorMessage, onDismiss, enableLocationBrowse } = props; +export default function DefineConversation(props) { + const { onDismiss, onSubmit, onGetErrorMessage } = props; + const [formData, setFormData] = useState({ errors: {} }); + const [disable, setDisable] = useState(false); const { state } = useContext(StoreContext); const { storages } = state; const currentStorageIndex = useRef(0); - const [formData, setFormData] = useState({ errors: {} }); - const [disable, setDisable] = useState(false); - - // set the default path useEffect(() => { const index = currentStorageIndex.current; if (storages[index]) { @@ -48,24 +45,6 @@ export function DefineConversation(props) { }); }; - const handleSubmit = e => { - e.preventDefault(); - const errors = validateForm(formData); - - if (Object.keys(errors).length) { - setFormData({ - ...formData, - errors, - }); - return; - } - - onSubmit({ - ...formData, - }); - }; - - //disable the next button if the text has errors. const getErrorMessage = text => { if (typeof onGetErrorMessage === 'function') { const result = onGetErrorMessage(text); @@ -83,45 +62,41 @@ export function DefineConversation(props) { } }; - // // update the path in the form and toggle the location picker. + const handleSubmit = e => { + e.preventDefault(); + const errors = validateForm(formData); + + if (Object.keys(errors).length) { + setFormData({ + ...formData, + errors, + }); + return; + } + + onSubmit({ + ...formData, + }); + }; + const updateLocation = path => { updateForm('location')(null, path); }; return ( - -
- - - - - - - - - - {enableLocationBrowse && } - - - - - - -
+
+ + + + + + + ); } diff --git a/Composer/packages/client/src/CreationFlow/DialogModal/index.js b/Composer/packages/client/src/CreationFlow/DialogModal/index.js new file mode 100644 index 0000000000..5610c0aa23 --- /dev/null +++ b/Composer/packages/client/src/CreationFlow/DialogModal/index.js @@ -0,0 +1,40 @@ +import React, { Fragment } from 'react'; +import formatMessage from 'format-message'; +import { Stack, StackItem, TextField } from 'office-ui-fabric-react'; + +import { styles as wizardStyles } from '../StepWizard/styles'; + +import { name, description } from './styles'; + +export function DialogModal(props) { + const { horizontal, formData, updateForm, getErrorMessage } = props; + + return ( + + + + + + + + + + + + ); +} diff --git a/Composer/packages/client/src/CreationFlow/DefineConversation/styles.js b/Composer/packages/client/src/CreationFlow/DialogModal/styles.js similarity index 100% rename from Composer/packages/client/src/CreationFlow/DefineConversation/styles.js rename to Composer/packages/client/src/CreationFlow/DialogModal/styles.js diff --git a/Composer/packages/client/src/CreationFlow/index.js b/Composer/packages/client/src/CreationFlow/index.js index df1d85b80f..5b0280cd34 100644 --- a/Composer/packages/client/src/CreationFlow/index.js +++ b/Composer/packages/client/src/CreationFlow/index.js @@ -4,7 +4,7 @@ import { toLower } from 'lodash'; import { CreationFlowStatus, DialogCreationCopy, Steps } from '../constants'; import { CreateOptions } from './CreateOptions/index'; -import { DefineConversation } from './DefineConversation/index'; +import DefineConversation from './DefineConversation/index'; import { OpenProject } from './OpenProject'; import { StoreContext } from './../store'; import { StepWizard } from './StepWizard/StepWizard'; @@ -128,12 +128,7 @@ export function CreationFlow(props) { [Steps.DEFINE]: { ...DialogCreationCopy.DEFINE_CONVERSATION_OBJECTIVE, children: ( - + ), }, }; diff --git a/Composer/packages/client/src/pages/design/new-dialog-modal.js b/Composer/packages/client/src/pages/design/new-dialog-modal.js index 0c53a5c416..c37fd556d6 100644 --- a/Composer/packages/client/src/pages/design/new-dialog-modal.js +++ b/Composer/packages/client/src/pages/design/new-dialog-modal.js @@ -1,20 +1,91 @@ -import React from 'react'; +import React, { useState } from 'react'; +import { DialogFooter, PrimaryButton, DefaultButton } from 'office-ui-fabric-react'; +import formatMessage from 'format-message'; import { DialogCreationCopy } from '../../constants'; -import { DefineConversation } from '../../CreationFlow/DefineConversation/index'; +import { DialogModal } from '../../CreationFlow/DialogModal/index'; import { DialogWrapper } from '../../components/DialogWrapper/index'; +const nameRegex = /^[a-zA-Z0-9-_.]+$/; + +const validateForm = data => { + const errors = {}; + const { name } = data; + + if (!name || !nameRegex.test(name)) { + errors.name = formatMessage( + 'Spaces and special characters are not allowed. Use letters, numbers, -, or _., numbers, -, and _' + ); + } + + return errors; +}; + export default function NewDialogModal(props) { const { isOpen, onDismiss, onSubmit, onGetErrorMessage } = props; + const [formData, setFormData] = useState({ errors: {} }); + const [disable, setDisable] = useState(false); + const updateForm = field => (e, newValue) => { + setFormData({ + ...formData, + errors: {}, + [field]: newValue, + }); + }; + + const getErrorMessage = text => { + if (typeof onGetErrorMessage === 'function') { + const result = onGetErrorMessage(text); + if (result === '' && disable) { + setDisable(false); + } + + if (result !== '' && !disable) { + setDisable(true); + } + + return result; + } else { + return ''; + } + }; + + const onClickCancel = () => { + setFormData({ errors: {} }); + onDismiss(); + }; + + const handleSubmit = e => { + e.preventDefault(); + const errors = validateForm(formData); + + if (Object.keys(errors).length) { + setFormData({ + ...formData, + errors, + }); + return; + } + onSubmit({ + ...formData, + }); + }; return ( - - + +
+ + + + + +
); } diff --git a/Composer/packages/server/src/models/asset/assetManager.ts b/Composer/packages/server/src/models/asset/assetManager.ts index 487f512bf9..be810f3c53 100644 --- a/Composer/packages/server/src/models/asset/assetManager.ts +++ b/Composer/packages/server/src/models/asset/assetManager.ts @@ -71,6 +71,7 @@ export class AssetManager { const folders = await this.templateStorage.readDir(path); this.projectTemplates = []; for (const name of folders) { + if (!templates[name]) continue; const absPath = Path.join(path, name); if ((await this.templateStorage.stat(absPath)).isDir) { const base = { id: name, name: templates[name].name, description: templates[name].description };