Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Added HandOffResourcesToAdmin step #8173

Merged
merged 2 commits into from
Jun 23, 2021
Merged
Show file tree
Hide file tree
Changes from 1 commit
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 @@ -4,26 +4,76 @@
import * as React from 'react';
import { usePublishApi } from '@bfc/extension-client';
import formatMessage from 'format-message';
import { useRecoilValue } from 'recoil';
import { ProvisionHandoff } from '@bfc/ui-shared';

import { WizardStep, Wizard } from '../shared/wizard';
import { useDispatcher } from '../../hooks/useDispatcher';
import { enabledHandOffResourcesState } from '../../recoilModel/atoms/handOffToAdminState';
import {
subscriptionState,
hostNameState,
resourceGroupState,
deployLocationState,
} from '../../recoilModel/atoms/resourceConfigurationState';

import { HandOffInstructionsStep } from './steps/HandOffInstructionsStep';
import { WizardFooter } from './footers/WizardFooter';
import { ChooseResourcesStep } from './steps/ChooseResourcesStep';

type Props = {
onStepChange?: (stepIndex: number, stepId: string) => void;
onStepChange?: (stepIndex: number, step: WizardStep) => void;
stepIndex?: number;
};

const urls = {
createNewResources: 'https://aka.ms/composer-publish-bot#create-new-azure-resources',
};

export const HandOffToAdminWizard = React.memo((props: Props) => {
const { onStepChange } = props;
const [steps, setSteps] = React.useState<WizardStep[]>([]);
const { setTitle, onBack, closeDialog: onCancel } = usePublishApi();
const { onBack, closeDialog: onCancel } = usePublishApi();
const resources = useRecoilValue(enabledHandOffResourcesState);
const subscriptionId = useRecoilValue(subscriptionState);
const hostName = useRecoilValue(hostNameState);
const deployRegion = useRecoilValue(deployLocationState);
const { name: resourceGroupName } = useRecoilValue(resourceGroupState);
const { setEnabledHandOffResources } = useDispatcher();
const [showHandOff, setShowHandOff] = React.useState<boolean>(false);

const handOffInstructions = React.useMemo(() => {
let instructions = '';
if (showHandOff) {
const createLuisResource = resources?.filter((r) => r.key === 'luisPrediction').length > 0;
const createLuisAuthoringResource = resources?.filter((r) => r.key === 'luisAuthoring').length > 0;
const createCosmosDb = resources?.filter((r) => r.key === 'cosmosDb').length > 0;
const createStorage = resources?.filter((r) => r.key === 'blobStorage').length > 0;
const createAppInsights = resources?.filter((r) => r.key === 'applicationInsights').length > 0;
const createQnAResource = resources?.filter((r) => r.key === 'qna').length > 0;

const provisionComposer = `node provisionComposer.js --subscriptionId ${
subscriptionId || '<YOUR SUBSCRIPTION ID>'
} --name ${hostName || '<RESOURCE NAME>'} --appPassword=<16 CHAR PASSWORD> --location=${
deployRegion || 'westus'
} --resourceGroup=${
resourceGroupName || '<RESOURCE GROUP NAME>'
} --createLuisResource=${createLuisResource} --createLuisAuthoringResource=${createLuisAuthoringResource} --createCosmosDb=${createCosmosDb} --createStorage=${createStorage} --createAppInsights=${createAppInsights} --createQnAResource=${createQnAResource}`;
VamsiModem marked this conversation as resolved.
Show resolved Hide resolved

const setDialogTitle = (step: WizardStep) => {
step && setTitle({ title: step.title, subText: step.subTitle });
return <></>;
};
instructions = formatMessage(
'I am creating a conversational experience using Microsoft Bot Framework project.' +
' For my project to work, Azure resources, including app registration, hosting, channels, Language Understanding, and QnA Maker, are required.' +
' Below are the steps to create these resources.\n\n' +
'1. Follow the instructions at the link below to run the provisioning command (seen below)\n' +
'2. Copy and paste the resulting JSON and securely share it with me.\n\n' +
'Provisoning Command:\n' +
'{command}\n\n' +
'Detailed instructions:\nhttps://aka.ms/how-to-complete-provision-handoff',
{ command: provisionComposer }
);
}
return instructions;
}, [resources, subscriptionId, hostName, deployRegion, resourceGroupName, showHandOff]);

React.useEffect(() => {
setSteps([
Expand All @@ -35,16 +85,54 @@ export const HandOffToAdminWizard = React.memo((props: Props) => {
onBack,
onCancel,
},
{
id: 'add-resources',
title: formatMessage('Add resources'),
subTitle: formatMessage.rich(
'Your bot needs the following resources based on its capabilities. Select resources that you want to provision in your publishing profile. <a>Learn more</a>',
{
a: ({ children }) => (
<a key="add-resource-learn-more" href={urls.createNewResources} rel="noopener noreferrer" target="_blank">
{children}
</a>
),
}
),
onRenderContent: () => (
<ChooseResourcesStep enabledResources={resources} onChangeSelection={setEnabledHandOffResources} />
),
onCancel,
onNext: () => setShowHandOff(true),
},
]);
}, []);
}, [resources]);

return (
<Wizard
firstStepId="handoff-instructions"
steps={steps}
onRenderFooter={(navState) => <WizardFooter {...navState} />}
onRenderHeader={(step) => setDialogTitle(step)}
onStepChange={(index, step) => onStepChange(index, step.id)}
/>
<>
<Wizard
firstStepId="handoff-instructions"
steps={steps}
onRenderFooter={(navState) => <WizardFooter {...navState} />}
onRenderHeader={() => <></>}
onStepChange={(index, step) => onStepChange(index, step)}
/>
{showHandOff && (
<ProvisionHandoff
developerInstructions={formatMessage(
'If Azure resources and subscription are managed by others, use the following information to request creation of the resources that you need to build and run your bot.'
)}
handoffInstructions={handOffInstructions}
hidden={!showHandOff}
learnMoreLink="https://aka.ms/how-to-complete-provision-handoff"
VamsiModem marked this conversation as resolved.
Show resolved Hide resolved
title={formatMessage('Generate instructions for Azure administrator')}
onBack={() => {
setShowHandOff(false);
}}
onDismiss={() => {
onCancel();
}}
/>
)}
</>
);
});
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ export const ChooseResourcesList = (props: Props) => {

return (
<FocusZone direction={FocusZoneDirection.vertical}>
<List items={items} onRenderCell={renderItem} />
<List items={[...items]} onRenderCell={renderItem} />
VamsiModem marked this conversation as resolved.
Show resolved Hide resolved
</FocusZone>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ const Content = styled.div`
flex-direction: column;
justify-content: space-between;
min-height: 430px;
height: calc(100vh - 65px);
`;

const FooterButton = styled(DefaultButton)`
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

import { atom } from 'recoil';

import { ResourcesItem } from '../../types';

export const enabledHandOffResourcesState = atom<ResourcesItem[] | undefined>({
key: 'hor_optional_resources',
VamsiModem marked this conversation as resolved.
Show resolved Hide resolved
default: undefined,
});

export const requiredHandOffResourcesState = atom<ResourcesItem[]>({
key: 'hor_required_resources',
VamsiModem marked this conversation as resolved.
Show resolved Hide resolved
default: [],
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

import { CallbackInterface, useRecoilCallback } from 'recoil';

import { ResourcesItem } from '../../types';
import { requiredHandOffResourcesState, enabledHandOffResourcesState } from '../atoms/handOffToAdminState';

export const handOffToAdminDispatcher = () => {
const setEnabledHandOffResources = useRecoilCallback(({ set }: CallbackInterface) => (resources: ResourcesItem[]) => {
set(enabledHandOffResourcesState, resources);
});

const setRequiredHandOffResources = useRecoilCallback(
({ set }: CallbackInterface) => (resources: ResourcesItem[]) => {
set(requiredHandOffResourcesState, resources);
}
);

return {
setEnabledHandOffResources,
setRequiredHandOffResources,
};
};
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,14 @@
import { resourceConfigurationDispatcher } from './resourceConfigurationDispatcher';
import { userInfoDispatcher } from './userInfoDispatcher';
import { importConfigurationDispatcher } from './importConfigurationDispatcher';
import { handOffToAdminDispatcher } from './handOffToAdminDispatcher';

const createDispatchers = () => {
return {
...resourceConfigurationDispatcher(),
...userInfoDispatcher(),
...importConfigurationDispatcher(),
...handOffToAdminDispatcher(),
};
};

Expand Down