diff --git a/Composer/packages/client/src/pages/notifications/NotificationList.tsx b/Composer/packages/client/src/pages/notifications/NotificationList.tsx index a50bf2e88a..9aadd3b14e 100644 --- a/Composer/packages/client/src/pages/notifications/NotificationList.tsx +++ b/Composer/packages/client/src/pages/notifications/NotificationList.tsx @@ -43,6 +43,7 @@ const columns: IColumn[] = [ return ; }, }, + { key: 'NotificationType', name: formatMessage('Type'), @@ -68,6 +69,31 @@ const columns: IColumn[] = [ }, isPadded: true, }, + { + key: 'NotificationBotName', + name: formatMessage('Bot'), + className: notification.columnCell, + fieldName: 'botName', + minWidth: 70, + maxWidth: 90, + isRowHeader: true, + isResizable: true, + data: 'string', + onRender: (item: INotification) => { + return ( +
+
+ {item.botName} +
+
+ ); + }, + isPadded: true, + }, { key: 'NotificationLocation', name: formatMessage('Location'), diff --git a/Composer/packages/client/src/pages/notifications/Notifications.tsx b/Composer/packages/client/src/pages/notifications/Notifications.tsx index 89d0377eed..4057f06db6 100644 --- a/Composer/packages/client/src/pages/notifications/Notifications.tsx +++ b/Composer/packages/client/src/pages/notifications/Notifications.tsx @@ -20,13 +20,18 @@ const Notifications: React.FC> = (pro const { projectId = '' } = props; const [filter, setFilter] = useState(''); const notifications = useNotifications(projectId, filter); + const rootProjectId = projectId; + const navigations = { [NotificationType.LG]: (item: INotification) => { const { projectId, resourceId, diagnostic, dialogPath } = item; let uri = `/bot/${projectId}/language-generation/${resourceId}/edit#L=${diagnostic.range?.start.line || 0}`; //the format of item.id is lgFile#inlineTemplateId if (dialogPath) { - uri = convertPathToUrl(projectId, resourceId, dialogPath); + uri = + rootProjectId === projectId + ? convertPathToUrl(projectId, null, resourceId, dialogPath) + : convertPathToUrl(rootProjectId, projectId, resourceId, dialogPath); } navigateTo(uri); }, @@ -34,7 +39,10 @@ const Notifications: React.FC> = (pro const { projectId, resourceId, diagnostic, dialogPath } = item; let uri = `/bot/${projectId}/language-understanding/${resourceId}/edit#L=${diagnostic.range?.start.line || 0}`; if (dialogPath) { - uri = convertPathToUrl(projectId, resourceId, dialogPath); + uri = + rootProjectId === projectId + ? convertPathToUrl(projectId, null, resourceId, dialogPath) + : convertPathToUrl(rootProjectId, projectId, resourceId, dialogPath); } navigateTo(uri); }, @@ -46,8 +54,11 @@ const Notifications: React.FC> = (pro [NotificationType.DIALOG]: (item: INotification) => { //path is like main.trigers[0].actions[0] //uri = id?selected=triggers[0]&focused=triggers[0].actions[0] - const { projectId, id, dialogPath } = item; - const uri = convertPathToUrl(projectId, id, dialogPath ?? ''); + const { projectId, id, dialogPath = '' } = item; + const uri = + rootProjectId === projectId + ? convertPathToUrl(projectId, null, id, dialogPath) + : convertPathToUrl(rootProjectId, projectId, id, dialogPath); navigateTo(uri); }, [NotificationType.SKILL]: (item: INotification) => { diff --git a/Composer/packages/client/src/pages/notifications/types.ts b/Composer/packages/client/src/pages/notifications/types.ts index 1253df95c2..fbde0a273b 100644 --- a/Composer/packages/client/src/pages/notifications/types.ts +++ b/Composer/packages/client/src/pages/notifications/types.ts @@ -20,6 +20,7 @@ export enum NotificationType { export interface INotification { projectId: string; + botName: string; id: string; severity: string; type: NotificationType; @@ -32,6 +33,7 @@ export interface INotification { export abstract class Notification implements INotification { projectId: string; + botName: string; id: string; severity: string; type = NotificationType.GENERAL; @@ -40,8 +42,9 @@ export abstract class Notification implements INotification { diagnostic: Diagnostic; dialogPath?: string; resourceId: string; - constructor(projectId: string, id: string, location: string, diagnostic: Diagnostic) { + constructor(projectId: string, botName: string, id: string, location: string, diagnostic: Diagnostic) { this.projectId = projectId; + this.botName = botName; this.id = id; this.resourceId = getBaseName(id); this.severity = DiagnosticSeverity[diagnostic.severity] || ''; @@ -52,16 +55,16 @@ export abstract class Notification implements INotification { export class ServerNotification extends Notification { type = NotificationType.GENERAL; - constructor(projectId: string, id: string, location: string, diagnostic: Diagnostic) { - super(projectId, id, location, diagnostic); + constructor(projectId: string, botName: string, id: string, location: string, diagnostic: Diagnostic) { + super(projectId, botName, id, location, diagnostic); this.message = diagnostic.message; } } export class DialogNotification extends Notification { type = NotificationType.DIALOG; - constructor(projectId: string, id: string, location: string, diagnostic: Diagnostic) { - super(projectId, id, location, diagnostic); + constructor(projectId: string, botName: string, id: string, location: string, diagnostic: Diagnostic) { + super(projectId, botName, id, location, diagnostic); this.message = `In ${replaceDialogDiagnosticLabel(diagnostic.path)} ${diagnostic.message}`; this.dialogPath = diagnostic.path; } @@ -69,8 +72,8 @@ export class DialogNotification extends Notification { export class SkillNotification extends Notification { type = NotificationType.SKILL; - constructor(projectId: string, id: string, location: string, diagnostic: Diagnostic) { - super(projectId, id, location, diagnostic); + constructor(projectId: string, botName: string, id: string, location: string, diagnostic: Diagnostic) { + super(projectId, botName, id, location, diagnostic); this.message = `${replaceDialogDiagnosticLabel(diagnostic.path)} ${diagnostic.message}`; this.dialogPath = diagnostic.path; } @@ -78,8 +81,8 @@ export class SkillNotification extends Notification { export class SettingNotification extends Notification { type = NotificationType.SETTING; - constructor(projectId: string, id: string, location: string, diagnostic: Diagnostic) { - super(projectId, id, location, diagnostic); + constructor(projectId: string, botName: string, id: string, location: string, diagnostic: Diagnostic) { + super(projectId, botName, id, location, diagnostic); this.message = `${replaceDialogDiagnosticLabel(diagnostic.path)} ${diagnostic.message}`; this.dialogPath = diagnostic.path; } @@ -89,13 +92,14 @@ export class LgNotification extends Notification { type = NotificationType.LG; constructor( projectId: string, + botName: string, id: string, location: string, diagnostic: Diagnostic, lgFile: LgFile, dialogs: DialogInfo[] ) { - super(projectId, id, location, diagnostic); + super(projectId, botName, id, location, diagnostic); this.message = createSingleMessage(diagnostic); this.dialogPath = this.findDialogPath(lgFile, dialogs, diagnostic); } @@ -120,13 +124,14 @@ export class LuNotification extends Notification { type = NotificationType.LU; constructor( projectId: string, + botName: string, id: string, location: string, diagnostic: Diagnostic, luFile: LuFile, dialogs: DialogInfo[] ) { - super(projectId, id, location, diagnostic); + super(projectId, botName, id, location, diagnostic); this.dialogPath = this.findDialogPath(luFile, dialogs, diagnostic); this.message = createSingleMessage(diagnostic); } @@ -146,8 +151,8 @@ export class LuNotification extends Notification { export class QnANotification extends Notification { type = NotificationType.QNA; - constructor(projectId: string, id: string, location: string, diagnostic: Diagnostic) { - super(projectId, id, location, diagnostic); + constructor(projectId: string, botName: string, id: string, location: string, diagnostic: Diagnostic) { + super(projectId, botName, id, location, diagnostic); this.dialogPath = ''; this.message = createSingleMessage(diagnostic); } diff --git a/Composer/packages/client/src/pages/notifications/useNotifications.tsx b/Composer/packages/client/src/pages/notifications/useNotifications.tsx index 08997f0e14..365502a4e2 100644 --- a/Composer/packages/client/src/pages/notifications/useNotifications.tsx +++ b/Composer/packages/client/src/pages/notifications/useNotifications.tsx @@ -1,116 +1,11 @@ // 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 { - botDiagnosticsState, - botProjectFileState, - crossTrainConfigState, - dialogSchemasState, - formDialogSchemasSelectorFamily, - jsonSchemaFilesState, - lgFilesState, - luFilesState, - qnaFilesState, - settingsState, - skillManifestsState, - validateDialogsSelectorFamily, -} from '../../recoilModel'; -import { recognizersSelectorFamily } from '../../recoilModel/selectors/recognizers'; +import { messagersSelector } from '../../recoilModel/selectors'; -import { getReferredLuFiles } from './../../utils/luUtil'; -import { - DialogNotification, - LgNotification, - LuNotification, - Notification, - QnANotification, - ServerNotification, - SettingNotification, - SkillNotification, -} from './types'; - -export default function useNotifications(projectId: string, filter?: string) { - const dialogs = useRecoilValue(validateDialogsSelectorFamily(projectId)); - const luFiles = useRecoilValue(luFilesState(projectId)); - const lgFiles = useRecoilValue(lgFilesState(projectId)); - const diagnostics = useRecoilValue(botDiagnosticsState(projectId)); - const setting = useRecoilValue(settingsState(projectId)); - const skillManifests = useRecoilValue(skillManifestsState(projectId)); - const dialogSchemas = useRecoilValue(dialogSchemasState(projectId)); - const qnaFiles = useRecoilValue(qnaFilesState(projectId)); - const formDialogSchemas = useRecoilValue(formDialogSchemasSelectorFamily(projectId)); - const botProjectFile = useRecoilValue(botProjectFileState(projectId)); - const jsonSchemaFiles = useRecoilValue(jsonSchemaFilesState(projectId)); - const recognizers = useRecoilValue(recognizersSelectorFamily(projectId)); - const crossTrainConfig = useRecoilValue(crossTrainConfigState(projectId)); - const botAssets: BotAssets = { - projectId, - dialogs, - luFiles, - qnaFiles, - lgFiles, - skillManifests, - setting, - dialogSchemas, - formDialogSchemas, - botProjectFile, - jsonSchemaFiles, - recognizers, - crossTrainConfig, - }; - - const memoized = useMemo(() => { - const notifications: Notification[] = []; - diagnostics.forEach((d) => { - notifications.push(new ServerNotification(projectId, '', d.source, d)); - }); - const skillDiagnostics = BotIndexer.checkSkillSetting(botAssets); - skillDiagnostics.forEach((item) => { - if (item.source.endsWith('.json')) { - notifications.push(new SkillNotification(projectId, item.source, item.source, item)); - } else { - notifications.push(new DialogNotification(projectId, item.source, item.source, item)); - } - }); - const luisLocaleDiagnostics = BotIndexer.checkLUISLocales(botAssets); - - luisLocaleDiagnostics.forEach((item) => { - notifications.push(new SettingNotification(projectId, item.source, item.source, item)); - }); - - dialogs.forEach((dialog) => { - dialog.diagnostics.forEach((diagnostic) => { - const location = `${dialog.id}.dialog`; - notifications.push(new DialogNotification(projectId, dialog.id, location, diagnostic)); - }); - }); - getReferredLuFiles(luFiles, dialogs).forEach((lufile) => { - lufile.diagnostics.forEach((diagnostic) => { - const location = `${lufile.id}.lu`; - notifications.push(new LuNotification(projectId, lufile.id, location, diagnostic, lufile, dialogs)); - }); - }); - lgFiles.forEach((lgFile) => { - lgFile.diagnostics.forEach((diagnostic) => { - const location = `${lgFile.id}.lg`; - notifications.push(new LgNotification(projectId, lgFile.id, location, diagnostic, lgFile, dialogs)); - }); - }); - qnaFiles.forEach((qnaFile) => { - get(qnaFile, 'diagnostics', []).forEach((diagnostic) => { - const location = `${qnaFile.id}.qna`; - notifications.push(new QnANotification(projectId, qnaFile.id, location, diagnostic)); - }); - }); - return notifications; - }, [botAssets, diagnostics]); - - const notifications: Notification[] = filter ? memoized.filter((x) => x.severity === filter) : memoized; - return notifications; +export default function useNotifications(_projectId: string, filter?: string) { + const messagers = useRecoilValue(messagersSelector); + return filter ? messagers.filter((x) => x.severity === filter) : messagers; } diff --git a/Composer/packages/client/src/recoilModel/selectors/index.ts b/Composer/packages/client/src/recoilModel/selectors/index.ts index 062b4fdbe0..37b4503a74 100644 --- a/Composer/packages/client/src/recoilModel/selectors/index.ts +++ b/Composer/packages/client/src/recoilModel/selectors/index.ts @@ -8,3 +8,4 @@ export * from './validatedDialogs'; export * from './dialogs'; export * from './dialogImports'; export * from './projectTemplates'; +export * from './messagers'; diff --git a/Composer/packages/client/src/recoilModel/selectors/messagers.ts b/Composer/packages/client/src/recoilModel/selectors/messagers.ts new file mode 100644 index 0000000000..5b689542d6 --- /dev/null +++ b/Composer/packages/client/src/recoilModel/selectors/messagers.ts @@ -0,0 +1,124 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +import { selector } from 'recoil'; +import { BotIndexer } from '@bfc/indexers'; +import { BotAssets } from '@bfc/shared'; + +import { + botDiagnosticsState, + botProjectFileState, + botProjectSpaceSelector, + crossTrainConfigState, + dialogSchemasState, + formDialogSchemasSelectorFamily, + jsonSchemaFilesState, + lgFilesState, + luFilesState, + qnaFilesState, + settingsState, + skillManifestsState, + validateDialogsSelectorFamily, +} from '../../recoilModel'; +import { recognizersSelectorFamily } from '../../recoilModel/selectors/recognizers'; +import { + DialogNotification, + LgNotification, + LuNotification, + Notification, + QnANotification, + ServerNotification, + SettingNotification, + SkillNotification, +} from '../../pages/notifications/types'; + +import { getReferredLuFiles } from './../../utils/luUtil'; + +export const messagersSelector = selector({ + key: 'messagersSelector', + get: ({ get }) => { + const botProjectSpace = get(botProjectSpaceSelector); + const allMessage: Notification[] = []; + + console.log(botProjectSpace); + for (const project of botProjectSpace) { + const { projectId, name } = project; + const dialogs = get(validateDialogsSelectorFamily(projectId)); + const luFiles = get(luFilesState(projectId)); + const lgFiles = get(lgFilesState(projectId)); + const diagnostics = get(botDiagnosticsState(projectId)); + const setting = get(settingsState(projectId)); + const skillManifests = get(skillManifestsState(projectId)); + const dialogSchemas = get(dialogSchemasState(projectId)); + const qnaFiles = get(qnaFilesState(projectId)); + const formDialogSchemas = get(formDialogSchemasSelectorFamily(projectId)); + const botProjectFile = get(botProjectFileState(projectId)); + const jsonSchemaFiles = get(jsonSchemaFilesState(projectId)); + const recognizers = get(recognizersSelectorFamily(projectId)); + const crossTrainConfig = get(crossTrainConfigState(projectId)); + const botAssets: BotAssets = { + projectId, + dialogs, + luFiles, + qnaFiles, + lgFiles, + skillManifests, + setting, + dialogSchemas, + formDialogSchemas, + botProjectFile, + jsonSchemaFiles, + recognizers, + crossTrainConfig, + }; + + const notifications: Notification[] = []; + diagnostics.forEach((d) => { + notifications.push(new ServerNotification(projectId, name, '', d.source, d)); + }); + const skillDiagnostics = BotIndexer.checkSkillSetting(botAssets); + skillDiagnostics.forEach((item) => { + if (item.source.endsWith('.json')) { + notifications.push(new SkillNotification(projectId, name, item.source, item.source, item)); + } else { + notifications.push(new DialogNotification(projectId, name, item.source, item.source, item)); + } + }); + const luisLocaleDiagnostics = BotIndexer.checkLUISLocales(botAssets); + + luisLocaleDiagnostics.forEach((item) => { + notifications.push(new SettingNotification(projectId, name, item.source, item.source, item)); + }); + + dialogs.forEach((dialog) => { + dialog.diagnostics.forEach((diagnostic) => { + const location = `${dialog.id}.dialog`; + notifications.push(new DialogNotification(projectId, name, dialog.id, location, diagnostic)); + }); + }); + getReferredLuFiles(luFiles, dialogs).forEach((lufile) => { + lufile.diagnostics.forEach((diagnostic) => { + const location = `${lufile.id}.lu`; + notifications.push(new LuNotification(projectId, name, lufile.id, location, diagnostic, lufile, dialogs)); + }); + }); + lgFiles.forEach((lgFile) => { + lgFile.diagnostics.forEach((diagnostic) => { + const location = `${lgFile.id}.lg`; + notifications.push(new LgNotification(projectId, name, lgFile.id, location, diagnostic, lgFile, dialogs)); + }); + }); + qnaFiles.forEach((qnaFile) => { + qnaFile.diagnostics.forEach((diagnostic) => { + const location = `${qnaFile.id}.qna`; + notifications.push(new QnANotification(projectId, name, qnaFile.id, location, diagnostic)); + }); + }); + allMessage.push(...notifications); + } + + return allMessage; + }, +});