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
54 changes: 52 additions & 2 deletions Composer/packages/client/src/pages/publish/BotStatusList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,16 @@ import moment from 'moment';
import formatMessage from 'format-message';
import { Checkbox } from 'office-ui-fabric-react/lib/Checkbox';
import { Icon } from 'office-ui-fabric-react/lib/Icon';
import React, { useState, Fragment, useMemo } from 'react';
import React, { useState, Fragment, useMemo, useRef } from 'react';
import { Dropdown, IDropdownOption } from 'office-ui-fabric-react/lib/Dropdown';
import { Spinner, SpinnerSize } from 'office-ui-fabric-react/lib/Spinner';
import { PublishResult } from '@bfc/shared';
import { CheckboxVisibility, DetailsList } from 'office-ui-fabric-react/lib/DetailsList';
import { IconButton } from 'office-ui-fabric-react/lib/Button';
import { ActionButton, IconButton } from 'office-ui-fabric-react/lib/Button';
import { SharedColors } from '@uifabric/fluent-theme';
import { FontSizes } from '@uifabric/styling';
import get from 'lodash/get';
import { ITextField, TextField } from 'office-ui-fabric-react/lib/TextField';

import { ApiStatus } from '../../utils/publishStatusPollingUpdater';

Expand Down Expand Up @@ -48,6 +49,25 @@ export const BotStatusList: React.FC<BotStatusListProps> = ({
}) => {
const [expandedBotIds, setExpandedBotIds] = useState<Record<string, boolean>>({});
const [currentSort, setSort] = useState({ key: 'Bot', descending: true });
const [clipboardText, setClipboardText] = useState('');
const clipboardTextFieldRef = useRef<ITextField>(null);

const copyStringToClipboard = (value?: string) => {
try {
if (clipboardTextFieldRef.current) {
Comment thread
GeoffCoxMSFT marked this conversation as resolved.
setClipboardText(value || '');
setTimeout(() => {
Comment thread
GeoffCoxMSFT marked this conversation as resolved.
if (clipboardTextFieldRef.current) {
clipboardTextFieldRef.current.select();
document.execCommand('copy');
}
}, 10);
}
} catch (e) {
// eslint-disable-next-line no-console
console.error('Something went wrong when copying to the clipboard.', e, location);
}
};

const displayedItems: BotStatus[] = useMemo(() => {
if (currentSort.key !== 'Bot') return botStatusList;
Expand Down Expand Up @@ -244,6 +264,30 @@ export const BotStatusList: React.FC<BotStatusListProps> = ({
},
isPadded: true,
},
{
key: 'SkillManifest',
name: '',
className: 'skillManifest',
fieldName: 'skillManifestUrl',
minWidth: 114,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

magic numbers 😄

maxWidth: 134,
data: 'string',
onRender: (item: BotStatus) => {
return (
item?.skillManifestUrl && (
<ActionButton
title={item.skillManifestUrl}
onClick={() => {
copyStringToClipboard(item.skillManifestUrl);
Comment thread
GeoffCoxMSFT marked this conversation as resolved.
}}
>
{formatMessage('Copy Skill Manifest URL')}
</ActionButton>
)
);
},
isPadded: true,
},
{
key: 'ShowPublishHistory',
name: '',
Expand Down Expand Up @@ -321,6 +365,12 @@ export const BotStatusList: React.FC<BotStatusListProps> = ({
onRenderRow={renderTableRow}
/>
</div>
<TextField

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd use a simple input since it's hidden instead of TextField component

readOnly
componentRef={clipboardTextFieldRef}
styles={{ root: { display: 'none' } }}
value={clipboardText}
/>
</div>
);
};
27 changes: 26 additions & 1 deletion Composer/packages/client/src/pages/publish/publishPageUtils.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

import { PublishTarget, SkillManifestFile } from '@bfc/shared';

import { ApiStatus } from '../../utils/publishStatusPollingUpdater';

import { Bot, BotStatus, BotPublishHistory, BotProjectType, BotPropertyType } from './type';
Expand All @@ -18,6 +20,7 @@ export const generateBotPropertyData = (botProjectData: BotProjectType[]) => {
setting: bot.setting,
publishTargets,
publishTypes: bot.publishTypes,
skillManifests: bot.skillManifests,
};
const tmpBot = { id: bot.projectId, name: bot.name, publishTarget: '' };
if (publishTargets.length > 0) {
Expand All @@ -28,14 +31,26 @@ export const generateBotPropertyData = (botProjectData: BotProjectType[]) => {
return { botPropertyData, botList };
};

const findSkillManifestUrl = (skillManifests: SkillManifestFile[], appId: string) => {
for (const skillManifest of skillManifests || []) {
Comment thread
GeoffCoxMSFT marked this conversation as resolved.
for (const endpoint of skillManifest?.content?.endpoints || []) {
if (endpoint?.msAppId === appId) {
return endpoint?.endpointUrl;
}
}
}

return undefined;
};

export const generateBotStatusList = (
botList: Bot[],
botPropertyData: BotPropertyType,
botPublishHistoryList: BotPublishHistory
): BotStatus[] => {
const bots = botList.map((bot) => {
const botStatus: BotStatus = Object.assign({}, bot);
const publishTargets = botPropertyData[bot.id].publishTargets;
const publishTargets: PublishTarget[] = botPropertyData[bot.id].publishTargets;
const publishHistory = botPublishHistoryList[bot.id];
if (publishTargets.length > 0 && botStatus.publishTarget && publishHistory) {
botStatus.publishTargets = publishTargets;
Expand All @@ -46,7 +61,17 @@ export const generateBotStatusList = (
botStatus.message = history.message;
botStatus.status = history.status;
}

const currentPublishTarget = publishTargets.find((pt) => pt.name === botStatus.publishTarget);
if (currentPublishTarget) {
const config = JSON.parse(currentPublishTarget.configuration);
const appId = config?.settings?.MicrosoftAppId;
if (appId) {
botStatus.skillManifestUrl = findSkillManifestUrl(botPropertyData[bot.id].skillManifests, appId);
}
}
}

return botStatus;
});
return bots;
Expand Down
7 changes: 6 additions & 1 deletion Composer/packages/client/src/pages/publish/type.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

import { DialogSetting, PublishResult, PublishTarget } from '@bfc/shared';
import { DialogSetting, PublishResult, PublishTarget, SkillManifestFile } from '@bfc/shared';

import { PublishType } from '../../recoilModel/types';

Expand All @@ -16,6 +16,10 @@ export type BotStatus = {
status?: number;
message?: string;
comment?: string;
/**
* The skill manifest URL associated with the current publishTarget.
*/
skillManifestUrl?: string;
};

export type Bot = {
Expand All @@ -28,6 +32,7 @@ type BotProperty = {
setting: DialogSetting;
publishTargets: PublishTarget[];
publishTypes: PublishType[];
skillManifests: SkillManifestFile[];
};

export type BotProjectType = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,7 @@ export const botProjectSpaceSelector = selector({
buildEssentials,
isPvaSchema,
publishTypes,
skillManifests,
};
});
return result;
Expand Down
29 changes: 28 additions & 1 deletion Composer/packages/server/src/locales/en-US.json
Original file line number Diff line number Diff line change
Expand Up @@ -815,6 +815,9 @@
"copy_project_location_to_clipboard_eb85c474": {
"message": "Copy project location to clipboard"
},
"copy_skill_manifest_url_217975ba": {
"message": "Copy Skill Manifest URL"
},
"could_not_connect_to_storage_50411de0": {
"message": "Could not connect to storage."
},
Expand Down Expand Up @@ -1205,6 +1208,9 @@
"done_54e3d4b6": {
"message": "Done"
},
"download_icon_2e0d10": {
"message": "Download Icon"
},
"download_now_and_install_when_you_close_composer_e241ed74": {
"message": "Download now and install when you close Composer."
},
Expand Down Expand Up @@ -1640,6 +1646,9 @@
"fromtemplatename_does_not_exist_d429483c": {
"message": "fromTemplateName does not exist"
},
"full_description_for_fd03dbf8": {
"message": "full description for"
},
"gb_7570760e": {
"message": "GB"
},
Expand Down Expand Up @@ -2543,12 +2552,18 @@
"open_inline_editor_a5aabcfa": {
"message": "Open inline editor"
},
"open_manifest_ffb556af": {
"message": "Open Manifest"
},
"open_notification_panel_5796edb3": {
"message": "Open notification panel"
},
"open_start_bots_panel_f7f87200": {
"message": "Open start bots panel"
},
"open_teams_416aae5c": {
"message": "Open Teams"
},
"open_web_chat_23601990": {
"message": "Open Web Chat"
},
Expand Down Expand Up @@ -3242,6 +3257,9 @@
"share_resource_request_afc3e465": {
"message": "Share resource request"
},
"short_description_for_6abb9a1b": {
"message": "short description for"
},
"show_all_diagnostics_c11f4e09": {
"message": "Show All Diagnostics"
},
Expand Down Expand Up @@ -3428,6 +3446,12 @@
"tb_149f379c": {
"message": "TB"
},
"teams_manifest_59d7fb0e": {
"message": "Teams Manifest"
},
"teams_manifest_for_your_bot_7d0ec7ea": {
"message": "Teams manifest for your bot:"
},
"template_name_c37cf8d9": {
"message": "Template name: "
},
Expand Down Expand Up @@ -4022,7 +4046,10 @@
"your_new_bot_is_almost_ready_1bb596e": {
"message": "Your new bot is almost ready!"
},
"your_teams_adapter_is_configured_for_your_publishe_e84e9275": {
"message": "Your Teams adapter is configured for your published bot. Copy the manifest, open App Studio in Teams and add the manifest so you can test your bot in Teams"
},
"your_template_requires_qna_maker_to_access_content_a4ca6f76": {
"message": "Your template requires QnA Maker to access content for your bot."
}
}
}