Skip to content
This repository was archived by the owner on Jul 9, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import Home from '../../pages/home/Home';
import { useProjectIdCache } from '../../utils/hooks';
import { useShell } from '../../shell';
import plugins from '../../plugins';
import { ImportModal } from '../ImportModal/ImportModal';

import { CreateOptions } from './CreateOptions';
import { OpenProject } from './OpenProject';
Expand Down Expand Up @@ -120,6 +121,11 @@ const CreationFlow: React.FC<CreationFlowProps> = () => {
schemaUrl: formData.schemaUrl,
appLocale,
qnaKbUrls,
templateDir: formData.templateDir,
eTag: formData.eTag,
urlSuffix: formData.urlSuffix,
alias: formData.alias,
preserveRoot: formData.preserveRoot,
};
createNewBot(newBotData);
};
Expand Down Expand Up @@ -199,6 +205,7 @@ const CreationFlow: React.FC<CreationFlowProps> = () => {
path="create/vaCore/*"
onDismiss={handleDismiss}
/>
<ImportModal path="import" />
</Router>
</EditorExtension>
</Fragment>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,20 @@ import { DialogFooter } from 'office-ui-fabric-react/lib/Dialog';
import formatMessage from 'format-message';
import { PrimaryButton, DefaultButton } from 'office-ui-fabric-react/lib/Button';
import { Stack, StackItem } from 'office-ui-fabric-react/lib/Stack';
import React, { Fragment, useEffect, useCallback, useMemo } from 'react';
import React, { Fragment, useEffect, useCallback, useMemo, useState } from 'react';
import { TextField } from 'office-ui-fabric-react/lib/TextField';
import { RouteComponentProps } from '@reach/router';
import querystring from 'query-string';
import { FontWeights } from '@uifabric/styling';
import { DialogWrapper, DialogTypes } from '@bfc/ui-shared';
import { useRecoilValue } from 'recoil';

import { DialogCreationCopy, QnABotTemplateId, nameRegex } from '../../constants';
import { FieldConfig, useForm } from '../../hooks/useForm';
import { StorageFolder } from '../../recoilModel/types';
import { createNotification } from '../../recoilModel/dispatchers/notification';
import { ImportSuccessNotificationWrapper } from '../ImportModal/ImportSuccessNotification';
import { dispatcherState } from '../../recoilModel';

import { LocationSelectContent } from './LocationSelectContent';

Expand Down Expand Up @@ -66,6 +70,12 @@ interface DefineConversationFormData {
description: string;
schemaUrl: string;
location?: string;

templateDir?: string; // location of the imported template
eTag?: string; // e tag used for content sync between composer and imported bot content
urlSuffix?: string; // url to deep link to after creation
alias?: string; // identifier that is used to track bots between imports
preserveRoot?: boolean; // identifier that is used to determine ay project file renames upon creation
}

interface DefineConversationProps
Expand Down Expand Up @@ -109,18 +119,19 @@ const DefineConversation: React.FC<DefineConversationProps> = (props) => {
);
return defaultName;
};
const { addNotification } = useRecoilValue(dispatcherState);

const formConfig: FieldConfig<DefineConversationFormData> = {
name: {
required: true,
validate: (value) => {
if (!value || !nameRegex.test(value)) {
if (!value || !nameRegex.test(`${value}`)) {
return formatMessage('Spaces and special characters are not allowed. Use letters, numbers, -, or _.');
}

const newBotPath =
focusedStorageFolder !== null && Object.keys(focusedStorageFolder as Record<string, any>).length
? Path.join(focusedStorageFolder.parent, focusedStorageFolder.name, value)
? Path.join(focusedStorageFolder.parent, focusedStorageFolder.name, `${value}`)
: '';
if (
files.some((bot) => {
Expand All @@ -146,6 +157,14 @@ const DefineConversation: React.FC<DefineConversationProps> = (props) => {
},
};
const { formData, formErrors, hasErrors, updateField, updateForm } = useForm(formConfig);
const [isImported, setIsImported] = useState<boolean>(false);

useEffect(() => {
if (props.location?.state) {
const { imported } = props.location.state;
setIsImported(imported);
}
}, [props.location?.state]);

useEffect(() => {
const formData: DefineConversationFormData = {
Expand Down Expand Up @@ -189,9 +208,35 @@ const DefineConversation: React.FC<DefineConversationProps> = (props) => {
return;
}

// handle extra properties in the case of an imported bot project
const dataToSubmit = {
...formData,
};
if (props.location?.state) {
const { alias, eTag, imported, templateDir, urlSuffix } = props.location.state;

if (imported) {
dataToSubmit.templateDir = templateDir;
dataToSubmit.eTag = eTag;
dataToSubmit.urlSuffix = urlSuffix;
dataToSubmit.alias = alias;
dataToSubmit.preserveRoot = true;

// create a notification to indicate import success
const notification = createNotification({
type: 'success',
title: '',
onRenderCardContent: ImportSuccessNotificationWrapper({
importedToExisting: false,
}),
});
addNotification(notification);
}
}

onSubmit(
{
...formData,
...dataToSubmit,
},
templateId || ''
);
Expand Down Expand Up @@ -223,15 +268,11 @@ const DefineConversation: React.FC<DefineConversationProps> = (props) => {
/>
);
}, [focusedStorageFolder]);
const dialogCopy = isImported ? DialogCreationCopy.IMPORT_BOT_PROJECT : DialogCreationCopy.DEFINE_BOT_PROJECT;

return (
<Fragment>
<DialogWrapper
isOpen
{...DialogCreationCopy.DEFINE_BOT_PROJECT}
dialogType={DialogTypes.CreateFlow}
onDismiss={onDismiss}
>
<DialogWrapper isOpen {...dialogCopy} dialogType={DialogTypes.CreateFlow} onDismiss={onDismiss}>
<form onSubmit={handleSubmit}>
<input style={{ display: 'none' }} type="submit" />
<Stack horizontal styles={stackinput} tokens={{ childrenGap: '2rem' }}>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

/** @jsx jsx */
import { jsx } from '@emotion/core';
import { Dialog, DialogFooter, DialogType } from 'office-ui-fabric-react/lib/Dialog';
import { DefaultButton } from 'office-ui-fabric-react/lib/Button';
import React from 'react';
import formatMessage from 'format-message';
import { generateUniqueId } from '@bfc/shared';

import { boldText, boldBlueText, dialogContent } from './style';

type ImportFailedModalProps = {
botName: string;
error?: Error | string;
onDismiss: () => any;
};

const BoldBlue = ({ children }) => (
<span key={generateUniqueId()} css={boldBlueText}>
{children}
</span>
);

export const ImportFailedModal: React.FC<ImportFailedModalProps> = (props) => {
const { botName, error, onDismiss } = props;

return (
<Dialog
dialogContentProps={{
title: formatMessage('Something went wrong'),
type: DialogType.close,
}}
hidden={false}
minWidth={560}
onDismiss={onDismiss}
>
<p css={dialogContent}>
{formatMessage.rich('There was an unexpected error importing bot content to <b>{ botName }</b>', {
b: BoldBlue,
botName,
})}
</p>
<p css={boldText}>{typeof error === 'object' ? error.message : error}</p>
<DialogFooter>
<DefaultButton text={formatMessage('Cancel')} onClick={onDismiss} />
</DialogFooter>
</Dialog>
);
};
Loading