From 3b7f803407b82191706120bb9f12b82de1955704 Mon Sep 17 00:00:00 2001 From: tianxiang <60452964+tianxiangs@users.noreply.github.com> Date: Thu, 18 Jun 2020 13:17:12 -0700 Subject: [PATCH] feat: model compose (#328) * fix: resize canvas on change of tag split pane (#210) * fix: initial commit on #203 * fix: don't rotate image to match OCR result (#211) * update app version to b481f37 (#212) * feat: Add model compose page * feat: add icon SortUp and SortDown * feat: add getSelections * fix: remove marqueeSelection * fix: remove unused pointer-events * feat: Add spinner to model compose page * feat: reset selected in componentDidUpdate * refactor: remove and rewrite comments and console * feat: spinner class changes * feat: add model source in predict page * feat: first version of composed models reorder * feat: add filter field and more on composed list reorder * feat: reset sort icon and add move composed items back to top button in commandbar * feat: get models from nextLink and set sort by createdTime by default * fix: add nextLink check and remove console * fix: preserve selection while items are getting filtered * feat: delay to meet the rate threshold when extracting data from next link * feat: Add blink notification while loading * fix: enlarge the blink notification * feat: add refresh button on model compose page * feat: add toast when exceed rate limitation * feat: UX design improve * refactor: refactor code * refactor: remove comments * fix: set allModels as items in ViewSelection * feat: finish load next page mannually draft * feat: put composed model on top * refactor: remove not in use code * feat: enable model name and set sticky list header * fix: style change and refactor code * feat: add compose View draft * fix: tslint style error * fix: change typescript version in pkg * feat: add modelComposeView, add colon in train record and comment model selection in predict page * fix: change train button style * fix: textfield name and turn off autocomplete * fix: change to yarn and hide loading spinner when no nextpage * feat: enable compose new model * fix: composed model shows more than once in list and tslint error, remove console, refactor code * fix: change train route to preview version to enable model name * fix: renew icon source * spelling * fix: model name sort * refactor: predict page * fix: text filter is case sensitive * fix: filter now case insensitive * fix: spelling * refactor: modify variable names * feat: set either latest train or latest compose model as model using in prediction * fix: remove dark block when refreshing page * fix: update composed models status * add test runbook * fix: spelling * fix: deletes unused import * refactor:remove console * refactor: change column header name * refactor: naming * fix: disable compose button in modal when selected models are not enough * fix: reset column header when refreshing * fix: remove train page backend warning * update yarn * fix: remove unused import and migrate to fluent ui * fix: create and update time sort * fix: register icons * styling for StatusCircleCheckmark * fix: transparent backgounf on checkmarkCircle * fix: localize strings * fix: add alert when there aren't enough models for compose * refactor: remove console * fix: add yarn to try to fix check failure * fix: alert will be shown when list has two models * fix: throw error when selected model has invalid one * refactor: viewSelection indent and rename * fix: change trigger download model Id * fix:allModels is changed by sort * refactor: change viewSelection to LF * remove ViewSelect file * add viewSelection.tsx * fix: change file to LF * fix: retreat yarn.lock to resolve yarn build error * fix: change import of IModel in composeModelView Co-authored-by: stew-ro <60453211+stew-ro@users.noreply.github.com> Co-authored-by: alex-krasn <64093224+alex-krasn@users.noreply.github.com> Co-authored-by: kunzheng <58841788+kunzms@users.noreply.github.com> Co-authored-by: alex-krasn --- docs/manual_testing/manual-test-runbook.md | 45 ++ package.json | 3 +- src/assets/sass/fabric-icons-inline.scss | 252 ++++--- src/common/constants.ts | 2 + src/common/localization/en-us.ts | 45 ++ src/common/localization/es-cl.ts | 45 ++ src/common/mockFactory.ts | 1 + src/common/strings.ts | 45 ++ src/common/themes.ts | 34 +- src/config/fabric-icons.json | 430 +++++------ src/models/applicationState.ts | 1 + .../pages/modelCompose/composeCommandBar.tsx | 82 +++ .../pages/modelCompose/composeModelView.tsx | 146 ++++ .../pages/modelCompose/modelCompose.scss | 164 +++++ .../pages/modelCompose/modelCompose.tsx | 682 ++++++++++++++++++ .../pages/modelCompose/viewSelection.tsx | 136 ++++ .../components/pages/predict/predictPage.tsx | 18 +- .../components/pages/train/trainPage.tsx | 53 +- .../components/pages/train/trainRecord.tsx | 11 +- .../components/shell/mainContentRouter.tsx | 2 + src/react/components/shell/sidebar.tsx | 7 + src/registerIcons.ts | 10 +- src/services/serviceHelper.ts | 2 + yarn.lock | 119 +-- 24 files changed, 1939 insertions(+), 396 deletions(-) create mode 100644 src/react/components/pages/modelCompose/composeCommandBar.tsx create mode 100644 src/react/components/pages/modelCompose/composeModelView.tsx create mode 100644 src/react/components/pages/modelCompose/modelCompose.scss create mode 100644 src/react/components/pages/modelCompose/modelCompose.tsx create mode 100644 src/react/components/pages/modelCompose/viewSelection.tsx diff --git a/docs/manual_testing/manual-test-runbook.md b/docs/manual_testing/manual-test-runbook.md index 5b6386ed0..41bb2ee87 100644 --- a/docs/manual_testing/manual-test-runbook.md +++ b/docs/manual_testing/manual-test-runbook.md @@ -50,3 +50,48 @@ Adding the following buttons to the canvas command bar: **`Given`** I've opened a project containing documents and I'm on the Tag Editor page.\ **`When`** I click "Run OCR on all documents" in the canvas command bar\ **`Then`** I should see "Running OCR..." for all documents. When running OCR finishes for each document, I should be ale to view each document's updated OCR JSON file. + + +___ +___ + +## **Feat: enable compose model and add model name when training a new model** + +> ### Feature description ### +- Add model name imput field on train page to add model name when training a new model +- Add model compose page in order to compose a new model with existing models + +> ### Use Case ### + +**`As`** a user +**`I want`** to give the new train model a customerized name +**`So`** I can type the name in input field in train page before click train button. + +**`As`** a user +**`I want`** to generate a new mode through existing model +**`So`** I can use model compose + +> ### Acceptance criteria ### + +#### Scenario One #### + +**`Given`** I've opened a project containing documents and I'm on the Train page.\ +**`When`** I type customerized name in input field and click train button\ +**`Then`** I should see typed name shows in Train Record after record shows up.\ + +#### **Scenario Two** #### + +**`Given`** I've opened a project containing documents and I'm on the Model Compose page. There are enough existing models in modelList.\ +**`When`** I select more than one models then click compose button\ +**`Then`** I should see a pop up modal with a list contains selected models and a input field.\ +**`When`** I type customerized model name in input field and click compose button on modal\ +**`Then`** I should see "Model is composing, please wait...". After that the list shows up again, new composed model with given name will be on the top of the list. The new composed model also has a "combine" icon. + + +#### ***Scenario Three*** #### + +**`Given`** I've opened a project containing documents and I'm on the Model Compose page.\ +**`When`** I click the header of a column\ +**`Then`** I should see the column becomes sorted in either ascending or discending order.\ +**`When`** I type some text inside the fliter field on top right\ +**`Then`** I should see items whose id or name contains the text be filtered out. \ No newline at end of file diff --git a/package.json b/package.json index 684699fdf..c52f48204 100644 --- a/package.json +++ b/package.json @@ -41,7 +41,8 @@ "serialize-javascript": "^3.0.0", "shortid": "^2.2.15", "utif": "^3.1.0", - "vott-react": "^0.2.12" + "vott-react": "^0.2.12", + "yarn": "^1.22.4" }, "scripts": { "start": "nf start -p 3000", diff --git a/src/assets/sass/fabric-icons-inline.scss b/src/assets/sass/fabric-icons-inline.scss index 7a2dada0d..8efdeda6f 100644 --- a/src/assets/sass/fabric-icons-inline.scss +++ b/src/assets/sass/fabric-icons-inline.scss @@ -1,119 +1,133 @@ -/* - Your use of the content in the files referenced here is subject to the terms of the license at https://aka.ms/fabric-assets-license -*/ -@font-face { - font-family: 'FabricMDL2Icons'; - src: url('data:application/octet-stream;base64,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') format('truetype'); - } - - .ms-Icon { - -moz-osx-font-smoothing: grayscale; - -webkit-font-smoothing: antialiased; - display: inline-block; - font-family: 'FabricMDL2Icons'; - font-style: normal; - font-weight: normal; - speak: none; - } - - // Mixins - @mixin ms-Icon--Table { content: "\ED86"; } - @mixin ms-Icon--TextField { content: "\EDC3"; } - @mixin ms-Icon--TextDocument { content: "\F029"; } - @mixin ms-Icon--DocumentManagement { content: "\EFFC"; } - @mixin ms-Icon--OpenFolderHorizontal { content: "\ED25"; } - @mixin ms-Icon--Info { content: "\E946"; } - @mixin ms-Icon--Label { content: "\E932"; } - @mixin ms-Icon--Documentation { content: "\EC17"; } - @mixin ms-Icon--AddTo { content: "\ECC8"; } - @mixin ms-Icon--Hide3 { content: "\F6AC"; } - @mixin ms-Icon--WarningSolid { content: "\F736"; } - @mixin ms-Icon--BranchMerge { content: "\F295"; } - @mixin ms-Icon--PlugConnected { content: "\F302"; } - @mixin ms-Icon--Plug { content: "\F300"; } - @mixin ms-Icon--AlertSolid { content: "\F331"; } - @mixin ms-Icon--Refresh { content: "\E72C"; } - @mixin ms-Icon--CheckboxComposite { content: "\E73A"; } - @mixin ms-Icon--Cancel { content: "\E711"; } - @mixin ms-Icon--More { content: "\E712"; } - @mixin ms-Icon--Settings { content: "\E713"; } - @mixin ms-Icon--Link { content: "\E71B"; } - @mixin ms-Icon--Filter { content: "\E71C"; } - @mixin ms-Icon--ZoomOut { content: "\E71F"; } - @mixin ms-Icon--Search { content: "\E721"; } - @mixin ms-Icon--Add { content: "\E710"; } - @mixin ms-Icon--CheckMark { content: "\E73E"; } - @mixin ms-Icon--ChevronLeft { content: "\E76B"; } - @mixin ms-Icon--ChevronRight { content: "\E76C"; } - @mixin ms-Icon--Up { content: "\E74A"; } - @mixin ms-Icon--Down { content: "\E74B"; } - @mixin ms-Icon--Delete { content: "\E74D"; } - @mixin ms-Icon--Cloud { content: "\E753"; } - @mixin ms-Icon--Edit { content: "\E70F"; } - @mixin ms-Icon--ChevronUp { content: "\E70E"; } - @mixin ms-Icon--ChevronDown { content: "\E70D"; } - @mixin ms-Icon--Copy { content: "\E8C8"; } - @mixin ms-Icon--ZoomIn { content: "\E8A3"; } - @mixin ms-Icon--Rename { content: "\E8AC"; } - @mixin ms-Icon--Tag { content: "\E8EC"; } - @mixin ms-Icon--Download { content: "\E896"; } - @mixin ms-Icon--View { content: "\E890"; } - @mixin ms-Icon--Help { content: "\E897"; } - @mixin ms-Icon--Home { content: "\E80F"; } - @mixin ms-Icon--MapLayers { content: "\E81E"; } - @mixin ms-Icon--Insights { content: "\E3AF"; } - @mixin ms-Icon--MachineLearning { content: "\E3B8"; } - @mixin ms-Icon--TagGroup { content: "\E3F6"; } - @mixin ms-Icon--BookAnswers { content: "\F8A4"; } - - - // Classes - .ms-Icon--Table:before { @include ms-Icon--Table } - .ms-Icon--TextField:before { @include ms-Icon--TextField } - .ms-Icon--TextDocument:before { @include ms-Icon--TextDocument } - .ms-Icon--DocumentManagement:before { @include ms-Icon--DocumentManagement } - .ms-Icon--OpenFolderHorizontal:before { @include ms-Icon--OpenFolderHorizontal } - .ms-Icon--Info:before { @include ms-Icon--Info } - .ms-Icon--Label:before { @include ms-Icon--Label } - .ms-Icon--Documentation:before { @include ms-Icon--Documentation } - .ms-Icon--AddTo:before { @include ms-Icon--AddTo } - .ms-Icon--Hide3:before { @include ms-Icon--Hide3 } - .ms-Icon--WarningSolid:before { @include ms-Icon--WarningSolid } - .ms-Icon--BranchMerge:before { @include ms-Icon--BranchMerge } - .ms-Icon--PlugConnected:before { @include ms-Icon--PlugConnected } - .ms-Icon--Plug:before { @include ms-Icon--Plug } - .ms-Icon--AlertSolid:before { @include ms-Icon--AlertSolid } - .ms-Icon--Refresh:before { @include ms-Icon--Refresh } - .ms-Icon--CheckboxComposite:before { @include ms-Icon--CheckboxComposite } - .ms-Icon--Cancel:before { @include ms-Icon--Cancel } - .ms-Icon--More:before { @include ms-Icon--More } - .ms-Icon--Settings:before { @include ms-Icon--Settings } - .ms-Icon--Link:before { @include ms-Icon--Link } - .ms-Icon--Filter:before { @include ms-Icon--Filter } - .ms-Icon--ZoomOut:before { @include ms-Icon--ZoomOut } - .ms-Icon--Search:before { @include ms-Icon--Search } - .ms-Icon--Add:before { @include ms-Icon--Add } - .ms-Icon--CheckMark:before { @include ms-Icon--CheckMark } - .ms-Icon--ChevronLeft:before { @include ms-Icon--ChevronLeft } - .ms-Icon--ChevronRight:before { @include ms-Icon--ChevronRight } - .ms-Icon--Up:before { @include ms-Icon--Up } - .ms-Icon--Down:before { @include ms-Icon--Down } - .ms-Icon--Delete:before { @include ms-Icon--Delete } - .ms-Icon--Cloud:before { @include ms-Icon--Cloud } - .ms-Icon--Edit:before { @include ms-Icon--Edit } - .ms-Icon--ChevronUp:before { @include ms-Icon--ChevronUp } - .ms-Icon--ChevronDown:before { @include ms-Icon--ChevronDown } - .ms-Icon--Copy:before { @include ms-Icon--Copy } - .ms-Icon--ZoomIn:before { @include ms-Icon--ZoomIn } - .ms-Icon--Rename:before { @include ms-Icon--Rename } - .ms-Icon--Tag:before { @include ms-Icon--Tag } - .ms-Icon--Download:before { @include ms-Icon--Download } - .ms-Icon--View:before { @include ms-Icon--View } - .ms-Icon--Help:before { @include ms-Icon--Help } - .ms-Icon--Home:before { @include ms-Icon--Home } - .ms-Icon--MapLayers:before { @include ms-Icon--MapLayers } - .ms-Icon--Insights:before { @include ms-Icon--Insights } - .ms-Icon--MachineLearning:before { @include ms-Icon--MachineLearning } - .ms-Icon--TagGroup:before { @include ms-Icon--TagGroup } - .ms-Icon--BookAnswers:before { @include ms-Icon--BookAnswers } - +/* + Your use of the content in the files referenced here is subject to the terms of the license at https://aka.ms/fabric-assets-license +*/ +@font-face { + font-family: 'FabricMDL2Icons'; + src: url('data:application/octet-stream;base64,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') format('truetype'); +} + +.ms-Icon { + -moz-osx-font-smoothing: grayscale; + -webkit-font-smoothing: antialiased; + display: inline-block; + font-family: 'FabricMDL2Icons'; + font-style: normal; + font-weight: normal; + speak: none; +} + +// Mixins +@mixin ms-Icon--SortUp { content: "\EE68"; } +@mixin ms-Icon--SortDown { content: "\EE69"; } +@mixin ms-Icon--Table { content: "\ED86"; } +@mixin ms-Icon--TextField { content: "\EDC3"; } +@mixin ms-Icon--Combine { content: "\EDBB"; } +@mixin ms-Icon--TextDocument { content: "\F029"; } +@mixin ms-Icon--StatusCircleCheckmark { content: "\F13E"; } +@mixin ms-Icon--DocumentManagement { content: "\EFFC"; } +@mixin ms-Icon--CircleRing { content: "\EA3A"; } +@mixin ms-Icon--Label { content: "\E932"; } +@mixin ms-Icon--Info { content: "\E946"; } +@mixin ms-Icon--Documentation { content: "\EC17"; } +@mixin ms-Icon--OpenFolderHorizontal { content: "\ED25"; } +@mixin ms-Icon--AddTo { content: "\ECC8"; } +@mixin ms-Icon--Hide3 { content: "\F6AC"; } +@mixin ms-Icon--WarningSolid { content: "\F736"; } +@mixin ms-Icon--BranchMerge { content: "\F295"; } +@mixin ms-Icon--PlugConnected { content: "\F302"; } +@mixin ms-Icon--Plug { content: "\F300"; } +@mixin ms-Icon--AlertSolid { content: "\F331"; } +@mixin ms-Icon--Refresh { content: "\E72C"; } +@mixin ms-Icon--CheckboxComposite { content: "\E73A"; } +@mixin ms-Icon--More { content: "\E712"; } +@mixin ms-Icon--Settings { content: "\E713"; } +@mixin ms-Icon--Link { content: "\E71B"; } +@mixin ms-Icon--Filter { content: "\E71C"; } +@mixin ms-Icon--ZoomOut { content: "\E71F"; } +@mixin ms-Icon--Search { content: "\E721"; } +@mixin ms-Icon--CheckMark { content: "\E73E"; } +@mixin ms-Icon--ChevronRight { content: "\E76C"; } +@mixin ms-Icon--ChevronLeft { content: "\E76B"; } +@mixin ms-Icon--Cancel { content: "\E711"; } +@mixin ms-Icon--Up { content: "\E74A"; } +@mixin ms-Icon--Down { content: "\E74B"; } +@mixin ms-Icon--Delete { content: "\E74D"; } +@mixin ms-Icon--Cloud { content: "\E753"; } +@mixin ms-Icon--Add { content: "\E710"; } +@mixin ms-Icon--ChevronUp { content: "\E70E"; } +@mixin ms-Icon--ReceiptProcessing { content: "\E496"; } +@mixin ms-Icon--ChevronDown { content: "\E70D"; } +@mixin ms-Icon--Edit { content: "\E70F"; } +@mixin ms-Icon--Copy { content: "\E8C8"; } +@mixin ms-Icon--ZoomIn { content: "\E8A3"; } +@mixin ms-Icon--Rename { content: "\E8AC"; } +@mixin ms-Icon--Tag { content: "\E8EC"; } +@mixin ms-Icon--View { content: "\E890"; } +@mixin ms-Icon--Download { content: "\E896"; } +@mixin ms-Icon--Help { content: "\E897"; } +@mixin ms-Icon--Home { content: "\E80F"; } +@mixin ms-Icon--MapLayers { content: "\E81E"; } +@mixin ms-Icon--KeyPhraseExtraction { content: "\E395"; } +@mixin ms-Icon--Insights { content: "\E3AF"; } +@mixin ms-Icon--MachineLearning { content: "\E3B8"; } +@mixin ms-Icon--TagGroup { content: "\E3F6"; } +@mixin ms-Icon--BookAnswers { content: "\F8A4"; } + + +// Classes +.ms-Icon--SortUp:before { @include ms-Icon--SortUp } +.ms-Icon--SortDown:before { @include ms-Icon--SortDown } +.ms-Icon--Table:before { @include ms-Icon--Table } +.ms-Icon--TextField:before { @include ms-Icon--TextField } +.ms-Icon--Combine:before { @include ms-Icon--Combine } +.ms-Icon--TextDocument:before { @include ms-Icon--TextDocument } +.ms-Icon--StatusCircleCheckmark:before { @include ms-Icon--StatusCircleCheckmark } +.ms-Icon--DocumentManagement:before { @include ms-Icon--DocumentManagement } +.ms-Icon--CircleRing:before { @include ms-Icon--CircleRing } +.ms-Icon--Label:before { @include ms-Icon--Label } +.ms-Icon--Info:before { @include ms-Icon--Info } +.ms-Icon--Documentation:before { @include ms-Icon--Documentation } +.ms-Icon--OpenFolderHorizontal:before { @include ms-Icon--OpenFolderHorizontal } +.ms-Icon--AddTo:before { @include ms-Icon--AddTo } +.ms-Icon--Hide3:before { @include ms-Icon--Hide3 } +.ms-Icon--WarningSolid:before { @include ms-Icon--WarningSolid } +.ms-Icon--BranchMerge:before { @include ms-Icon--BranchMerge } +.ms-Icon--PlugConnected:before { @include ms-Icon--PlugConnected } +.ms-Icon--Plug:before { @include ms-Icon--Plug } +.ms-Icon--AlertSolid:before { @include ms-Icon--AlertSolid } +.ms-Icon--Refresh:before { @include ms-Icon--Refresh } +.ms-Icon--CheckboxComposite:before { @include ms-Icon--CheckboxComposite } +.ms-Icon--More:before { @include ms-Icon--More } +.ms-Icon--Settings:before { @include ms-Icon--Settings } +.ms-Icon--Link:before { @include ms-Icon--Link } +.ms-Icon--Filter:before { @include ms-Icon--Filter } +.ms-Icon--ZoomOut:before { @include ms-Icon--ZoomOut } +.ms-Icon--Search:before { @include ms-Icon--Search } +.ms-Icon--CheckMark:before { @include ms-Icon--CheckMark } +.ms-Icon--ChevronRight:before { @include ms-Icon--ChevronRight } +.ms-Icon--ChevronLeft:before { @include ms-Icon--ChevronLeft } +.ms-Icon--Cancel:before { @include ms-Icon--Cancel } +.ms-Icon--Up:before { @include ms-Icon--Up } +.ms-Icon--Down:before { @include ms-Icon--Down } +.ms-Icon--Delete:before { @include ms-Icon--Delete } +.ms-Icon--Cloud:before { @include ms-Icon--Cloud } +.ms-Icon--Add:before { @include ms-Icon--Add } +.ms-Icon--ChevronUp:before { @include ms-Icon--ChevronUp } +.ms-Icon--ReceiptProcessing:before { @include ms-Icon--ReceiptProcessing } +.ms-Icon--ChevronDown:before { @include ms-Icon--ChevronDown } +.ms-Icon--Edit:before { @include ms-Icon--Edit } +.ms-Icon--Copy:before { @include ms-Icon--Copy } +.ms-Icon--ZoomIn:before { @include ms-Icon--ZoomIn } +.ms-Icon--Rename:before { @include ms-Icon--Rename } +.ms-Icon--Tag:before { @include ms-Icon--Tag } +.ms-Icon--View:before { @include ms-Icon--View } +.ms-Icon--Download:before { @include ms-Icon--Download } +.ms-Icon--Help:before { @include ms-Icon--Help } +.ms-Icon--Home:before { @include ms-Icon--Home } +.ms-Icon--MapLayers:before { @include ms-Icon--MapLayers } +.ms-Icon--KeyPhraseExtraction:before { @include ms-Icon--KeyPhraseExtraction } +.ms-Icon--Insights:before { @include ms-Icon--Insights } +.ms-Icon--MachineLearning:before { @include ms-Icon--MachineLearning } +.ms-Icon--TagGroup:before { @include ms-Icon--TagGroup } +.ms-Icon--BookAnswers:before { @include ms-Icon--BookAnswers } + diff --git a/src/common/constants.ts b/src/common/constants.ts index cb2bc836b..2e076d4d7 100644 --- a/src/common/constants.ts +++ b/src/common/constants.ts @@ -23,6 +23,8 @@ export const constants = { convertedThumbnailQuality: 0.2, apiModelsPath: "/formrecognizer/v2.0-preview/custom/models", + apiPreviewPath: "/formrecognizer/v2.1-preview.1/custom/models", + apiPreviewComposePath: "/formrecognizer/v2.1-preview.1/custom/models/compose", pdfjsWorkerSrc(version: string) { return `//fotts.azureedge.net/npm/pdfjs-dist/${version}/pdf.worker.js`; diff --git a/src/common/localization/en-us.ts b/src/common/localization/en-us.ts index a7ac1fbaf..13cb1548f 100644 --- a/src/common/localization/en-us.ts +++ b/src/common/localization/en-us.ts @@ -122,6 +122,51 @@ export const english: IAppStrings = { pleaseWait: "Please wait", notTrainedYet: "Not trained yet", backEndNotAvailable: "Checkbox feature will work in future version of Form Recognizer service, please stay tuned.", + addName: "Add model name...", + }, + modelCompose: { + title: "Model compose", + columnAria: { + icon: "Model with icon is a new composed model", + }, + loading: "Model is loading...", + composing: "Model is composing, please wait...", + column: { + icon: { + name:"Composed Icon", + }, + id: { + headerName: "Model Id", + fieldName: "modelId", + }, + name: { + headerName: "Model Name", + fieldName: "modelName", + }, + status: { + headerName: "Status", + fieldName: "status", + }, + created: { + headerName: "Created", + fieldName: "created", + }, + lastupdated: { + headerName: "Last Updated", + fieldName: "lastUpdated", + }, + }, + modelView: { + titleAria: "Compose Model View", + addComposeModelName: "Add compose model name...", + NotEnoughModels: " Should have at least more than one selected model to compose a new model", + }, + commandBar: { + ariaLabel: "Please use command bar to compose models", + composeAria: "Compose Model", + refreshAria: "Refresh the list", + filter: "Filter By Name...", + } }, predict: { title: "Analyze", diff --git a/src/common/localization/es-cl.ts b/src/common/localization/es-cl.ts index 75168ec43..e61e6b01a 100644 --- a/src/common/localization/es-cl.ts +++ b/src/common/localization/es-cl.ts @@ -123,6 +123,51 @@ export const spanish: IAppStrings = { pleaseWait: "Por favor espera", notTrainedYet: "Aún no entrenado", backEndNotAvailable: "La función de casilla de verificación funcionará en la versión futura del servicio de reconocimiento de formularios, manténgase atento.", + addName:"Agregar nombre de modelo ...", + }, + modelCompose: { + title: "Modelo componer", + columnAria: { + icon: "Modelo con icono es un nuevo modelo compuesto", + }, + loading: "La modelo se está cargando ...", + composing: "La modelo está componiendo, por favor espera ...", + column: { + icon: { + name: "Icono compuesto", + }, + id: { + headerName: "ID del Modelo", + fieldName: "modelId", + }, + name: { + headerName: "Nombre del Modelo", + fieldName: "modelName", + }, + status: { + headerName: "Estado", + fieldName: "status", + }, + created: { + headerName: "Creada", + fieldName: "created", + }, + lastupdated: { + headerName: "Última Actualización", + fieldName: "lastUpdated", + }, + }, + modelView: { + titleAria: "Componer vista de modelo", + addComposeModelName: "Añadir componer el nombre de modelo ...", + NotEnoughModels: "Debe tener más de un modelo seleccionado para componer un nuevo modelo", + }, + commandBar: { + ariaLabel: "Utilice la barra de comandos para componer modelos", + composeAria: "Componer modelo", + refreshAria: "Actualizar la lista", + filter: "Filtrar por nombre ...", + } }, predict: { title: "Analizar", diff --git a/src/common/mockFactory.ts b/src/common/mockFactory.ts index a2aaea342..94b3faefa 100644 --- a/src/common/mockFactory.ts +++ b/src/common/mockFactory.ts @@ -147,6 +147,7 @@ export default class MockFactory { apiUriBase: "localhost", folderPath: "", trainRecord: null, + predictModelId: "", }; } diff --git a/src/common/strings.ts b/src/common/strings.ts index b97a9c228..2bd7b8df2 100644 --- a/src/common/strings.ts +++ b/src/common/strings.ts @@ -122,7 +122,52 @@ export interface IAppStrings { pleaseWait: string; notTrainedYet: string; backEndNotAvailable: string; + addName: string; }; + modelCompose: { + title: string; + columnAria: { + icon: string; + } + loading: string; + composing: string; + column: { + icon: { + name: string; + } + id: { + headerName: string; + fieldName: string; + } + name: { + headerName: string; + fieldName: string; + } + status: { + headerName: string; + fieldName: string; + } + created: { + headerName: string; + fieldName: string; + } + lastupdated: { + headerName: string; + fieldName: string; + } + } + modelView: { + titleAria: string; + addComposeModelName: string; + NotEnoughModels: string; + } + commandBar: { + ariaLabel: string; + composeAria: string; + refreshAria: string; + filter: string; + } + } predict: { title: string; uploadFile: string; diff --git a/src/common/themes.ts b/src/common/themes.ts index 3af257ea8..d312e34bc 100644 --- a/src/common/themes.ts +++ b/src/common/themes.ts @@ -1,4 +1,4 @@ -import {createTheme} from "@fluentui/react"; +import {createTheme, IPalette} from "@fluentui/react"; const greenButtonPalette = { themePrimary: "#78ad0e", @@ -201,6 +201,34 @@ const greenWithWhiteBackgroundPalette = { white: "#ffffff", }; +const DarkDefaultPalette: Partial = { + themeDarker: "#82c7ff", + themeDark: "#6cb8f6", + themeDarkAlt: "#3aa0f3", + themePrimary: "#2899f5", + themeSecondary: "#0078d4", + themeTertiary: "#235a85", + themeLight: "#004c87", + themeLighter: "#043862", + themeLighterAlt: "#092c47", + black: "#ffffff", + neutralDark: "#faf9f8", + neutralPrimary: "#f3f2f1", + neutralPrimaryAlt: "#c8c6c4", + neutralSecondary: "#a19f9d", + neutralSecondaryAlt: "#979693", + neutralTertiary: "#797775", + neutralTertiaryAlt: "#484644", + neutralQuaternary: "#3b3a39", + neutralQuaternaryAlt: "#323130", + neutralLight: "#292827", + neutralLighter: "#252423", + neutralLighterAlt: "#201f1e", + white: "#1b1a19", + redDark: "#F1707B", + }; + +const defaultDarkTheme = createTheme({palette: DarkDefaultPalette}); const whiteTheme = createTheme({palette: whiteButtonPalette}); const redTheme = createTheme({palette: redButtonPalette}); const greenTheme = createTheme({palette: greenButtonPalette}); @@ -241,3 +269,7 @@ export function getDarkTheme() { export function getGreenWithWhiteBackgroundTheme() { return greenWithWhiteBackgroundTheme; } + +export function getDefaultDarkTheme() { + return defaultDarkTheme; +} diff --git a/src/config/fabric-icons.json b/src/config/fabric-icons.json index 9b6d5baa8..8237d0b7a 100644 --- a/src/config/fabric-icons.json +++ b/src/config/fabric-icons.json @@ -1,202 +1,230 @@ { - "fontName": "fabric-icons", - "fontFamilyName": "FabricMDL2Icons", - "excludeGlyphs": false, - "excludeThirdPartyIcons": false, - "chunkSubsets": false, - "hashFontFileName": true, - "glyphs": [ - { - "name": "Table", - "unicode": "ED86" - }, - { - "name": "TextField", - "unicode": "EDC3" - }, - { - "name": "TextDocument", - "unicode": "F029" - }, - { - "name": "DocumentManagement", - "unicode": "EFFC" - }, - { - "name": "OpenFolderHorizontal", - "unicode": "ED25" - }, - { - "name": "Info", - "unicode": "E946" - }, - { - "name": "Label", - "unicode": "E932" - }, - { - "name": "Documentation", - "unicode": "EC17" - }, - { - "name": "AddTo", - "unicode": "ECC8" - }, - { - "name": "Hide3", - "unicode": "F6AC" - }, - { - "name": "WarningSolid", - "unicode": "F736" - }, - { - "name": "BranchMerge", - "unicode": "F295" - }, - { - "name": "PlugConnected", - "unicode": "F302" - }, - { - "name": "Plug", - "unicode": "F300" - }, - { - "name": "AlertSolid", - "unicode": "F331" - }, - { - "name": "Refresh", - "unicode": "E72C" - }, - { - "name": "CheckboxComposite", - "unicode": "E73A" - }, - { - "name": "Cancel", - "unicode": "E711" - }, - { - "name": "More", - "unicode": "E712" - }, - { - "name": "Settings", - "unicode": "E713" - }, - { - "name": "Link", - "unicode": "E71B" - }, - { - "name": "Filter", - "unicode": "E71C" - }, - { - "name": "ZoomOut", - "unicode": "E71F" - }, - { - "name": "Search", - "unicode": "E721" - }, - { - "name": "Add", - "unicode": "E710" - }, - { - "name": "CheckMark", - "unicode": "E73E" - }, - { - "name": "ChevronLeft", - "unicode": "E76B" - }, - { - "name": "ChevronRight", - "unicode": "E76C" - }, - { - "name": "Up", - "unicode": "E74A" - }, - { - "name": "Down", - "unicode": "E74B" - }, - { - "name": "Delete", - "unicode": "E74D" - }, - { - "name": "Cloud", - "unicode": "E753" - }, - { - "name": "Edit", - "unicode": "E70F" - }, - { - "name": "ChevronUp", - "unicode": "E70E" - }, - { - "name": "ChevronDown", - "unicode": "E70D" - }, - { - "name": "Copy", - "unicode": "E8C8" - }, - { - "name": "ZoomIn", - "unicode": "E8A3" - }, - { - "name": "Rename", - "unicode": "E8AC" - }, - { - "name": "Tag", - "unicode": "E8EC" - }, - { - "name": "Download", - "unicode": "E896" - }, - { - "name": "View", - "unicode": "E890" - }, - { - "name": "Help", - "unicode": "E897" - }, - { - "name": "Home", - "unicode": "E80F" - }, - { - "name": "MapLayers", - "unicode": "E81E" - }, - { - "name": "Insights", - "unicode": "E3AF" - }, - { - "name": "MachineLearning", - "unicode": "E3B8" - }, - { - "name": "TagGroup", - "unicode": "E3F6" - }, - { - "name": "BookAnswers", - "unicode": "F8A4" - } - ] - } + "fontName": "fabric-icons", + "fontFamilyName": "FabricMDL2Icons", + "excludeGlyphs": false, + "excludeThirdPartyIcons": false, + "chunkSubsets": false, + "hashFontFileName": true, + "glyphs": [ + { + "name": "SortUp", + "unicode": "EE68" + }, + { + "name": "SortDown", + "unicode": "EE69" + }, + { + "name": "Table", + "unicode": "ED86" + }, + { + "name": "TextField", + "unicode": "EDC3" + }, + { + "name": "Combine", + "unicode": "EDBB" + }, + { + "name": "TextDocument", + "unicode": "F029" + }, + { + "name": "StatusCircleCheckmark", + "unicode": "F13E" + }, + { + "name": "DocumentManagement", + "unicode": "EFFC" + }, + { + "name": "CircleRing", + "unicode": "EA3A" + }, + { + "name": "Label", + "unicode": "E932" + }, + { + "name": "Info", + "unicode": "E946" + }, + { + "name": "Documentation", + "unicode": "EC17" + }, + { + "name": "OpenFolderHorizontal", + "unicode": "ED25" + }, + { + "name": "AddTo", + "unicode": "ECC8" + }, + { + "name": "Hide3", + "unicode": "F6AC" + }, + { + "name": "WarningSolid", + "unicode": "F736" + }, + { + "name": "BranchMerge", + "unicode": "F295" + }, + { + "name": "PlugConnected", + "unicode": "F302" + }, + { + "name": "Plug", + "unicode": "F300" + }, + { + "name": "AlertSolid", + "unicode": "F331" + }, + { + "name": "Refresh", + "unicode": "E72C" + }, + { + "name": "CheckboxComposite", + "unicode": "E73A" + }, + { + "name": "More", + "unicode": "E712" + }, + { + "name": "Settings", + "unicode": "E713" + }, + { + "name": "Link", + "unicode": "E71B" + }, + { + "name": "Filter", + "unicode": "E71C" + }, + { + "name": "ZoomOut", + "unicode": "E71F" + }, + { + "name": "Search", + "unicode": "E721" + }, + { + "name": "CheckMark", + "unicode": "E73E" + }, + { + "name": "ChevronRight", + "unicode": "E76C" + }, + { + "name": "ChevronLeft", + "unicode": "E76B" + }, + { + "name": "Cancel", + "unicode": "E711" + }, + { + "name": "Up", + "unicode": "E74A" + }, + { + "name": "Down", + "unicode": "E74B" + }, + { + "name": "Delete", + "unicode": "E74D" + }, + { + "name": "Cloud", + "unicode": "E753" + }, + { + "name": "Add", + "unicode": "E710" + }, + { + "name": "ChevronUp", + "unicode": "E70E" + }, + { + "name": "ReceiptProcessing", + "unicode": "E496" + }, + { + "name": "ChevronDown", + "unicode": "E70D" + }, + { + "name": "Edit", + "unicode": "E70F" + }, + { + "name": "Copy", + "unicode": "E8C8" + }, + { + "name": "ZoomIn", + "unicode": "E8A3" + }, + { + "name": "Rename", + "unicode": "E8AC" + }, + { + "name": "Tag", + "unicode": "E8EC" + }, + { + "name": "View", + "unicode": "E890" + }, + { + "name": "Download", + "unicode": "E896" + }, + { + "name": "Help", + "unicode": "E897" + }, + { + "name": "Home", + "unicode": "E80F" + }, + { + "name": "MapLayers", + "unicode": "E81E" + }, + { + "name": "KeyPhraseExtraction", + "unicode": "E395" + }, + { + "name": "Insights", + "unicode": "E3AF" + }, + { + "name": "MachineLearning", + "unicode": "E3B8" + }, + { + "name": "TagGroup", + "unicode": "E3F6" + }, + { + "name": "BookAnswers", + "unicode": "F8A4" + } + ] +} \ No newline at end of file diff --git a/src/models/applicationState.ts b/src/models/applicationState.ts index 6c5b7cf05..88675ed4f 100644 --- a/src/models/applicationState.ts +++ b/src/models/applicationState.ts @@ -94,6 +94,7 @@ export interface IProject { apiKey?: string | ISecureString; folderPath: string; trainRecord: ITrainRecordProps; + predictModelId: string; } /** diff --git a/src/react/components/pages/modelCompose/composeCommandBar.tsx b/src/react/components/pages/modelCompose/composeCommandBar.tsx new file mode 100644 index 000000000..60fca3877 --- /dev/null +++ b/src/react/components/pages/modelCompose/composeCommandBar.tsx @@ -0,0 +1,82 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +import * as React from "react"; +import "./modelCompose.scss"; +import { TextField, SpinnerSize, Spinner, CommandBar, ICommandBarItemProps } from "@fluentui/react"; +import { getPrimaryWhiteTheme, getDefaultDarkTheme } from "../../../../common/themes"; +import { strings } from "../../../../common/strings"; + +interface IModelComposeCommandBarProps { + composedModels: any[]; + allModels: any[]; + isComposing: boolean; + isLoading: boolean; + hasText: boolean; + handleCompose: () => void; + handleRefresh: () => void; + filterTextChange: (ev: React.FormEvent, text: string) => void; +} + +export const ModelComposeCommandBar: React.FunctionComponent = (props) => { + + const commandBarItems: ICommandBarItemProps[] = [ + { + key: "Compose", + text: "Compose", + ariaLabel: strings.modelCompose.commandBar.composeAria, + iconProps: { iconName: "combine" }, + onClick: () => {props.handleCompose(); }, + }, + { + key: "Refresh", + text: "Refresh", + ariaLabel: strings.modelCompose.commandBar.refreshAria, + disabled: props.isComposing || props.isLoading, + iconProps: {iconName: "refresh"}, + onClick: () => {props.handleRefresh(); }, + }, + { + key: "loadingSpinner", + onRender: () => ( + props.isLoading && +
+ + +
+ ), + }, + ]; + + const commandBarFarItems: ICommandBarItemProps[] = [ + { + key: "filter", + className: "filter-item", + onRender: () => ( +
+ + +
+ ), + }, + ]; + + return ( + + ); +}; diff --git a/src/react/components/pages/modelCompose/composeModelView.tsx b/src/react/components/pages/modelCompose/composeModelView.tsx new file mode 100644 index 000000000..eff73420c --- /dev/null +++ b/src/react/components/pages/modelCompose/composeModelView.tsx @@ -0,0 +1,146 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +import React from "react"; +import { Customizer, IColumn, ICustomizations, Modal, DetailsList, SelectionMode, DetailsListLayoutMode, PrimaryButton, TextField } from "@fluentui/react"; +import { getDarkGreyTheme, getPrimaryGreenTheme, getPrimaryRedTheme } from "../../../../common/themes"; +import { strings } from "../../../../common/strings"; +import { IModel } from "./modelCompose"; + + +export interface IComposeModelViewProps { + onComposeConfirm: (composeModelName: string) => void; +} + +export interface IComposeModelViewState { + hideModal: boolean; + items: IModel[]; +} + +export default class ComposeModelView extends React.Component { + /** + * + */ + private modelName: string = ""; + + constructor(props) { + super(props); + this.state = { + hideModal: true, + items: [], + } + } + + public render() { + const columns: IColumn[] = [ + { + key: "column1", + name: strings.modelCompose.column.id.headerName, + minWidth: 250, + maxWidth: 250, + isResizable: true, + onRender: (model) => { + return {model.modelId} + } + }, + { + key: "column2", + name: strings.modelCompose.column.name.headerName, + minWidth: 50, + isResizable: true, + onRender: (model) => { + return {model.modelName} + } + } + ]; + const dark: ICustomizations = { + settings: { + theme: getDarkGreyTheme(), + }, + scopedSettings: {}, + }; + return ( + + +
+ Add name for Compose model + + +
+
+ { + this.state.items && + + + } +
+ <>{ + this.state.items.length < 2 && +
+ {strings.modelCompose.modelView.NotEnoughModels} +
+ } +
+ + Compose + + + Close + +
+
+
+ ) + } + + public open = (models) => { + this.setState({ + hideModal: false, + items: models, + }) + } + + public close = () => { + this.setState({ + hideModal: true, + }) + } + + public confirm = () => { + if (this.state.items.length > 1) { + this.props.onComposeConfirm(this.modelName); + this.setState({ + hideModal: true, + }) + } + } + + private onTextChange = (ev: React.FormEvent, text: string) => { + this.modelName = text; + } +} \ No newline at end of file diff --git a/src/react/components/pages/modelCompose/modelCompose.scss b/src/react/components/pages/modelCompose/modelCompose.scss new file mode 100644 index 000000000..01c292d03 --- /dev/null +++ b/src/react/components/pages/modelCompose/modelCompose.scss @@ -0,0 +1,164 @@ +.modelCompose-page { + width: calc(100vw - 45px); + height: calc(100vh - 58px); + overflow: auto; + display: flex; +} + +.models-list { + width: 100%; + overflow-x: visible; +} + +.compact-toggle { + color: white; + margin: 2px 10px; +} + +.commandbar-container { + width: 100%; + height: 44px; +} + +.commandbar { + z-index: 1; + top: 0; + width: 100%; + position: sticky; +} + +.compose-spinner { + color: white; + height: 70vh; +} + +.composed-icon-cell { + text-align: left; +} + +.model-fontIcon { + text-align: left; +} + +.label-filter-background { + background-color: #1E2024; + padding-bottom: 10px; +} + +.label-filter-field { + margin-left: auto; + margin-right: 15px; + background-color: #1E2024; + max-width: 300px; + width: 100%; +} + +.filter-item { + width: 200px; +} + +.commandbar-filter { + background-color: #1E2024; + display: flex; + justify-items: center; + align-self: center; + width: 300px; +} + +.spinner-item { + width: 300px; + display: inline-flex; + align-items: center; + padding-left: 10px; +} + +.commandbar-spinner { + color: white; +} + +.next-page-container { + height: 40px; + background-color: #1E2024 ; + display: flex; + justify-content: center; + align-items: center; +} + +.next-page-button { + background: transparent; + border: 1px white; + color: white; +} + +.list-container { + width: 100%; +} + +.scrollpane-container { + width: 100%; +} + +.pane-container { + width: calc(100vw - 45px); + height: calc(100vh - 102px); + top: 78px; + left: auto; + right: 0px; + bottom: auto; +} + +.modal-container { + width: 700px; + min-height: 200px; + max-height: 400px; + padding: 20px; + padding-right: 10px; +} + +.modal-list-container { + margin: 0; + width: 100%; + height: 80%; + min-height: 100px; + padding-bottom: 10px; +} + +.modal-textfield { + margin-top: 10px; +} + +.modal-alert { + color: #e30202; + font-size: small; +} + +.modal-button-container { + width: 100%; + display: flex; + justify-content: right; +} + +.modal-confirm { + //display: flex; + width: 110px; +} + +.modal-cancel { + width: 110px; + float: right; +} + +.is-checked { + i { + color: white; + } + &::before{ + background-color: transparent; + } +} + +.ms-Check { + &::before { + background: transparent; + } +} diff --git a/src/react/components/pages/modelCompose/modelCompose.tsx b/src/react/components/pages/modelCompose/modelCompose.tsx new file mode 100644 index 000000000..ce056113a --- /dev/null +++ b/src/react/components/pages/modelCompose/modelCompose.tsx @@ -0,0 +1,682 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +import React from "react"; +import {connect} from "react-redux"; +import url from "url"; +import { RouteComponentProps } from "react-router-dom"; +import { IProject, IConnection, IAppSettings, IApplicationState, AppError, ErrorCode } from "../../../../models/applicationState"; +import { constants } from "../../../../common/constants"; +import ServiceHelper from "../../../../services/serviceHelper"; +import { IColumn, + Fabric, + DetailsList, + Selection, SelectionMode, + DetailsListLayoutMode, Customizer, + ICustomizations, + Spinner, + SpinnerSize, + FontIcon, + IDetailsList, + PrimaryButton, + Sticky, + IDetailsHeaderProps, + IRenderFunction, + IDetailsColumnRenderTooltipProps, + TooltipHost, + StickyPositionType, + ScrollablePane, + ScrollbarVisibility} from "@fluentui/react"; +import "./modelCompose.scss"; +import { strings } from "../../../../common/strings"; +import { getDarkGreyTheme, getDefaultDarkTheme } from "../../../../common/themes"; +import { ModelComposeCommandBar } from "./composeCommandBar"; +import { bindActionCreators } from "redux"; +import IProjectActions, * as projectActions from "../../../../redux/actions/projectActions"; +import IApplicationActions, * as applicationActions from "../../../../redux/actions/applicationActions"; +import IAppTitleActions, * as appTitleActions from "../../../../redux/actions/appTitleActions"; +import ComposeModelView from "./composeModelView"; +import { ViewSelection } from "./viewSelection"; + +export interface IModelComposePageProps extends RouteComponentProps, React.Props { + recentProjects: IProject[]; + connections: IConnection[]; + appSettings: IAppSettings; + project: IProject; + actions: IProjectActions; + applicationActions: IApplicationActions; + appTitleActions: IAppTitleActions; +} + +export interface IModelComposePageState { + modelList: IModel[]; + nextLink: string; + composedModelList: IModel[]; + columns: IColumn[]; + selectionDetails: string; + isModalSelection: boolean; + isCompactMode: boolean; + isComposing: boolean; + composeModelId: string[]; + isLoading: boolean; + refreshFlag: boolean; + hasText: boolean; +} + +export interface IModel { + key: string; + modelId: string; + modelName: string; + createdDateTime: string; + lastUpdatedDateTime: string; + status: string; + iconName?: string; +} + +function mapStateToProps(state: IApplicationState) { + return { + recentProjects: state.recentProjects, + connections: state.connections, + appSettings: state.appSettings, + project: state.currentProject, + }; +} + +function mapDispatchToProps(dispatch) { + return { + actions: bindActionCreators(projectActions, dispatch), + applicationActions: bindActionCreators(applicationActions, dispatch), + appTitleActions: bindActionCreators(appTitleActions, dispatch), + }; +} + +@connect(mapStateToProps, mapDispatchToProps) +export default class ModelComposePage extends React.Component { + private selection: Selection; + private allModels: IModel[]; + private refreshClicks: boolean; + private selectedItems: any[]; + private listRef = React.createRef(); + private composeModalRef: React.RefObject = React.createRef(); + + constructor(props) { + super(props); + const columns: IColumn[] = [ + { + key: "column1", + name: strings.modelCompose.column.icon.name, + className: "composed-icon-cell", + isIconOnly: true, + ariaLabel: strings.modelCompose.columnAria.icon, + fieldName: "icon", + minWidth: 20, + maxWidth: 20, + isResizable: true, + onRender: (model: IModel) => { + return ; + }, + headerClassName: "list-header", + }, + { + key: "column2", + name: strings.modelCompose.column.id.headerName, + fieldName: strings.modelCompose.column.id.fieldName, + minWidth: 250, + maxWidth: 250, + isResizable: true, + onColumnClick: this.handleColumnClick, + onRender: (model: IModel) => { + return {model.modelId}; + }, + }, + { + key: "column3", + name: strings.modelCompose.column.name.headerName, + fieldName: strings.modelCompose.column.name.fieldName, + minWidth: 150, + isResizable: true, + onColumnClick: this.handleColumnClick, + onRender: (model: IModel) => { + return ({model.modelName}); + }, + + }, + { + key: "column4", + name: strings.modelCompose.column.status.headerName, + fieldName: strings.modelCompose.column.status.fieldName, + minWidth: 50, + maxWidth: 100, + isResizable: true, + onColumnClick: this.handleColumnClick, + onRender: (model: IModel) => { + return ({model.status}); + }, + }, + { + key: "column5", + name: strings.modelCompose.column.created.headerName, + fieldName: strings.modelCompose.column.created.fieldName, + minWidth: 150, + maxWidth: 175, + isResizable: true, + isRowHeader: true, + isSorted: false, + isSortedDescending: true, + onColumnClick: this.handleColumnClick, + onRender: (model: IModel) => { + return {new Date(model.createdDateTime).toLocaleString()}; + }, + }, + { + key: "column6", + name: strings.modelCompose.column.lastupdated.headerName, + fieldName: strings.modelCompose.column.lastupdated.fieldName, + minWidth: 175, + maxWidth: 175, + isResizable: true, + onColumnClick: this.handleColumnClick, + onRender: (model: IModel) => { + return ({new Date(model.lastUpdatedDateTime).toLocaleString()}); + }, + }, + ]; + + this.state = { + modelList: [], + nextLink: "*", + composedModelList: [], + columns, + selectionDetails: this.handleSelection(), + isModalSelection: false, + isCompactMode: false, + isComposing: false, + composeModelId: [], + isLoading: false, + refreshFlag: false, + hasText: false, + }; + + this.selection = new Selection({ + onSelectionChanged: () => { + this.setState({ + selectionDetails: this.handleSelection(), + }); + }, + }); + } + + public async componentDidMount() { + const projectId = this.props.match.params["projectId"]; + if (projectId) { + const project = this.props.recentProjects.find((project) => project.id === projectId); + await this.props.actions.loadProject(project); + this.props.appTitleActions.setTitle(project.name); + } + + if (this.props.project) { + this.getModelList(); + } + document.title = strings.modelCompose.title + "-" + strings.appName; + } + + public componentDidUpdate(prevProps, prevState) { + if ((prevState.isComposing === true && + prevState.isComposing !== this.state.isComposing) || this.state.refreshFlag) { + if (this.props.project) { + this.getModelList(); + } + this.refreshClicks = false; + this.setState({ + refreshFlag: false, + }); + } + } + + public render() { + const {modelList, isCompactMode, columns} = this.state; + const dark: ICustomizations = { + settings: { + theme: getDarkGreyTheme(), + }, + scopedSettings: {}, + }; + const onRenderDetailsHeader: IRenderFunction = (props, defaultRender) => { + if (!props) { + return null; + } + const onRenderColumnHeaderTooltip: IRenderFunction = + (tooltipHostProps) => ( + + ); + return ( + + {defaultRender!({ + ...props, + onRenderColumnHeaderTooltip, + })} + + ); + }; + + return ( + + +
+ +
+
+ + + {this.state.isComposing ? + + : +
+ + + {this.state.nextLink && this.state.nextLink !== "*" && !this.state.hasText && +
+ { + this.state.isLoading ? + + + : + + + + Load next page + + } +
} +
+ } +
+
+
+ +
+
+ ); + } + + private getKey = (item: any, index?: number): string => { + return item.key; + } + + private getModelList = async () => { + try { + this.setState({ + isLoading: true, + }); + + let composedModels = this.state.composedModelList; + + composedModels=this.reloadComposedModel(composedModels); + + let composedModelIds = []; + let predictModelFlag = false; + if (this.state.composeModelId.length !== 0) { + composedModelIds = this.getComposedIds(); + predictModelFlag = true; + if (composedModelIds.indexOf(this.state.composeModelId[0]) === -1) { + const idURL = constants.apiPreviewPath + "/" + this.state.composeModelId[0]; + const newComposeModel = await this.getComposeModelByURl(idURL); + composedModels.push(newComposeModel); + composedModelIds.push(this.state.composeModelId[0]); + } + } + const res = await this.getResponse(); + let models = res.data.modelList; + const link = res.data.nextLink; + + models.map((m) => m.key = m.modelId); + models = models.filter((m) => composedModelIds.indexOf(m.modelId) === -1); + + const newList = composedModels.concat(models); + + this.allModels = newList; + if (predictModelFlag) { + const updatedProject = this.buildUpdatedProject( + this.state.composeModelId[0], + ); + await this.props.actions.saveProject(updatedProject, false, false); + } + this.setState({ + modelList: newList, + nextLink: link, + composedModelList: composedModels, + }, () => { + this.setState({ + isLoading: false, + }); + }); + } catch (error) { + console.log(error); + } + } + + private buildUpdatedProject = (modelId: string): IProject => { + return { + ...this.props.project, + predictModelId: modelId, + }; + } + + private reloadComposedModel = (models: IModel[]): IModel[] => { + models.forEach(async (m) => { + if (m.status !== "ready" && m.status !== "invalid") { + const url = constants.apiPreviewPath + "/" + m.modelId; + const newModel = await this.getComposeModelByURl(url); + const newStatus = newModel.status; + m.status = newStatus; + } + }) + return models; + } + + private getComposeModelByURl = async (idURL) => { + const composedRes = await this.getResponse(idURL); + const composedModel: IModel = composedRes.data.modelInfo; + composedModel.iconName = "combine"; + composedModel.key = composedModel.modelId; + return composedModel; + } + + private getNextPage = async () => { + try { + if (this.allModels.length <= 5000) { + if (this.state.nextLink.length !== 0) { + this.setState({ + isLoading: true, + }); + const nextPage = await this.getModelsFromNextLink(this.state.nextLink); + let currentList = this.state.modelList; + const composedModels = this.state.composedModelList; + const composedIds = this.getComposedIds(); + currentList = currentList.filter((m) => composedIds.indexOf(m.modelId) === -1); + let reorderList = this.copyAndSort(currentList, strings.modelCompose.column.id.fieldName, false); + reorderList = composedModels.concat(reorderList); + + let nextPageList = nextPage.nextList; + nextPageList = nextPageList.filter((m) => composedIds.indexOf(m.modelId) === -1); + + nextPageList.map((m) => m.key = m.modelId); + const newList = reorderList.concat(nextPageList); + + this.allModels = newList; + const newCols = this.state.columns; + newCols.forEach((ncol) => { + ncol.isSorted = false; + ncol.isSortedDescending = true; + }); + this.setState({ + modelList: newList, + nextLink: nextPage.nextLink, + columns: newCols, + }, () => { + this.setState({ + isLoading: false, + }); + }); + } + } + } catch (error) { + console.log(error); + } + } + + private getComposedIds = () => { + const composedIds = []; + this.state.composedModelList.forEach((m) => {composedIds.push(m.modelId); }); + return composedIds; + } + + private getModelsFromNextLink = async (link: string) => { + const res = await this.getResponse(link); + return { + nextList: res.data.modelList, + nextLink: res.data.nextLink, + }; + } + + private async getResponse(nextLink?: string) { + const baseURL = nextLink === undefined ? url.resolve( + this.props.project.apiUriBase, + constants.apiPreviewPath, + ) : url.resolve( + this.props.project.apiUriBase, + nextLink, + ); + const config = { + headers: { + "Access-Control-Allow-Origin": "*", + "withCredentials": "true", + }, + }; + + try { + return await ServiceHelper.getWithAutoRetry( + baseURL, + config, + this.props.project.apiKey as string, + ); + } catch (err) { + this.setState({ + isLoading: false, + }); + ServiceHelper.handleServiceError(err); + } + } + + private handleColumnClick = (event: React.MouseEvent, column: IColumn): void => { + const {columns, modelList} = this.state; + const newColumns: IColumn[] = columns.slice(); + const currColumn: IColumn = newColumns.filter((col) => column.key === col.key)[0]; + newColumns.forEach((newCol: IColumn) => { + if (newCol === currColumn) { + currColumn.isSortedDescending = !currColumn.isSortedDescending; + currColumn.isSorted = true; + } else { + newCol.isSorted = false; + newCol.isSortedDescending = true; + } + }); + const newList = this.copyAndSort(modelList, currColumn.fieldName!, currColumn.isSortedDescending); + this.setState({ + columns: newColumns, + modelList: newList, + }); + } + + private copyAndSort(modelList: IModel[], columnKey: string, isSortedDescending?: boolean): IModel[] { + const key = columnKey; + if (key === strings.modelCompose.column.created.fieldName || key === strings.modelCompose.column.lastupdated.fieldName) { + return (modelList.slice(0) + .sort((a, b): number => { + if (isSortedDescending) { + if ((new Date(a.createdDateTime)).getTime() < (new Date(b.createdDateTime)).getTime()) { + return 1; + } else { + return -1; + } + } else { + if ((new Date(a.createdDateTime)).getTime() > (new Date(b.createdDateTime)).getTime()) { + return 1; + } else { + return -1; + } + } + })); + } else if (key === strings.modelCompose.column.name.fieldName) { + return ( + modelList.slice(0).sort((a,b) => { + if (a.modelName && b.modelName) { + return isSortedDescending ? b.modelName.localeCompare(a.modelName) : -b.modelName.localeCompare(a.modelName) + } else if (a.modelName || b.modelName) { + if (a.modelName) { + return -1; + } else if (b.modelName) { + return 1; + } + } else { + return -1; + } + }) + ) + } else { + return (modelList.slice(0) + .sort((a: IModel, b: IModel) => ((isSortedDescending ? a[key] < b[key] : a[key] > b[key]) ? 1 : -1 ))); + } + } + + private handleSelection = (): string => { + return "item selected"; + } + + /** Handle filter when text changes in filter field */ + private onTextChange = (ev: React.FormEvent, text: string): void => { + this.setState({ + modelList: text ? + this.allModels.filter(({ modelName, modelId }) => (modelId.indexOf(text.toLowerCase()) > -1) || + (modelName !== undefined ? modelName.toLowerCase().indexOf(text.toLowerCase()) > -1 : false)) : this.allModels, + hasText: text ? true : false, + }); + } + + private onComposeButtonClick = () => { + this.composeModalRef.current.open(this.selectedItems); + } + + private onComposeConfirm = (composeModelName: string) => { + this.setState({ + isComposing: true, + }); + const selections = this.selectedItems; + this.handleModelsCompose(selections, composeModelName); + } + + private passSelectedItems = (Items) => { + this.selectedItems = Items; + } + + /** Handle the operation of composing a new model */ + private handleModelsCompose = async (selections: any[], name: string) => { + setTimeout( async () => { + try { + const idList = []; + selections.forEach((s) => idList.push(s.modelId)); + const payload = { + modelIds: idList, + modelName: name, + }; + const link = constants.apiPreviewComposePath; + const composeRes = await this.post(link, payload); + const composeModelId = this.getComposeModelId(composeRes); + const newCols = this.state.columns; + newCols.forEach((ncol) => { + ncol.isSorted = false; + ncol.isSortedDescending = true; + }); + this.setState({ + isComposing: false, + composeModelId: [composeModelId], + columns: newCols, + }); + } catch (error) { + this.setState({ + isComposing: false, + }) + throw new AppError(ErrorCode.ModelNotFound, error.message); + } + }, 5000); + } + + /** get the model Id of new composed model */ + private getComposeModelId = (composeRes: any): string => { + const location = composeRes["headers"]["location"]; + const splitGroup = location.split("/"); + return splitGroup[splitGroup.length - 1]; + } + + private async post(link, payload): Promise { + const baseURL = url.resolve( + this.props.project.apiUriBase, + link, + ); + + try { + return await ServiceHelper.postWithAutoRetry( + baseURL, + payload, + {}, + this.props.project.apiKey as string, + ); + } catch (err) { + ServiceHelper.handleServiceError(err); + } + } + + private onRefreshButtonClick = () => { + if (!this.refreshClicks) { + this.refreshClicks = true; + setTimeout(() => { + if (!this.state.refreshFlag && this.refreshClicks) { + const newCols = this.state.columns; + newCols.forEach((ncol) => { + ncol.isSorted = false; + ncol.isSortedDescending = true; + }); + this.setState({ + refreshFlag: true, + columns: newCols, + }); + } + }, 1); + } + } +} diff --git a/src/react/components/pages/modelCompose/viewSelection.tsx b/src/react/components/pages/modelCompose/viewSelection.tsx new file mode 100644 index 000000000..df02b6667 --- /dev/null +++ b/src/react/components/pages/modelCompose/viewSelection.tsx @@ -0,0 +1,136 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +// tslint:disable:no-empty-interface +import { BaseComponent, IRefObject } from "@uifabric/utilities"; +import { ISelection } from "@fluentui/react"; +import * as React from "react"; + +export interface IViewSelection { } + +export interface IViewSelectionProps + extends React.HTMLAttributes { + componentRef?: IRefObject; + + /** + * The selection object to interact with when updating selection changes. + */ + selection: ISelection; + + items: any[]; + + columns: any[]; + + isComposing: boolean; + + refreshFlag: boolean; + + passSelectedItems: (items: any[]) => void; +} + +export interface IViewSelectionState { } + +export class ViewSelection extends BaseComponent< + IViewSelectionProps, + IViewSelectionState + > { + private selectedIndices: any[]; + private selectedItems: any[] = []; + constructor(props: IViewSelectionProps) { + super(props); + this.state = {}; + this.selectedIndices = []; + } + + public render() { + const { children } = this.props; + return
{children}
; + } + + public componentWillUpdate(nextProps: IViewSelectionProps, nextState: IViewSelectionState) { + this.saveSelection(); + } + + public componentDidUpdate(prevProps: IViewSelectionProps, prevState: IViewSelectionState) { + if (prevProps.columns === this.props.columns) { + this.restoreSelection(); + } + if (prevProps.isComposing === true && (prevProps.isComposing !== this.props.isComposing)) { + this.props.selection.setAllSelected(false); + this.selectedIndices = []; + this.selectedItems = []; + } + + if (this.props.refreshFlag) { + this.props.selection.setAllSelected(false); + this.selectedIndices = []; + this.selectedItems = []; + } + + this.props.passSelectedItems(this.selectedItems); + } + + public passSelectedItems() { + return this.selectedItems; + } + + private toListIndex(index: number) { + const viewItems = this.props.selection.getItems(); + const viewItem = viewItems[index]; + return this.props.items.findIndex((listItem) => listItem === viewItem); + } + + private toViewIndex(index: number) { + const listItem = this.props.items[index]; + const viewIndex = this.props.selection + .getItems() + .findIndex((viewItem) => viewItem === listItem); + return viewIndex; + } + + private saveSelection(): void { + const newIndices = this.props.selection + .getSelectedIndices() + .map((index) => this.toListIndex(index)) + .filter((index) => this.selectedIndices.indexOf(index) === -1); + + const unselectedIndices = this.props.selection + .getItems() + .map((item, index) => index) + .filter((index) => this.props.selection.isIndexSelected(index) === false) + .map((index) => this.toListIndex(index)); + + this.selectedIndices = this.selectedIndices.filter( + (index) => unselectedIndices.indexOf(index) === -1, + ); + this.selectedIndices = [...this.selectedIndices, ...newIndices]; + + const items = []; + this.selectedIndices.forEach((i) => { + const item = this.props.items[i]; + if (!items.includes(item)) { + items.push(item); + } + }); + + this.selectedItems = items; + this.props.passSelectedItems(this.selectedItems); + } + + private restoreSelection(): void { + const indicesList = []; + this.selectedItems.forEach((i) => { + const index = this.props.items.indexOf(i); + if (index !== -1) { + indicesList.push(index); + } + }); + + const indices = indicesList + .map((index) => this.toViewIndex(index)) + .filter((index) => index !== -1); + for (const index of indices) { + this.props.selection.setIndexSelected(index, true, false); + } + } +} diff --git a/src/react/components/pages/predict/predictPage.tsx b/src/react/components/pages/predict/predictPage.tsx index d6cd4be6a..7a008f7ed 100644 --- a/src/react/components/pages/predict/predictPage.tsx +++ b/src/react/components/pages/predict/predictPage.tsx @@ -68,6 +68,15 @@ export interface IPredictPageState { isPredicting: boolean; file?: File; highlightedField: string; + modelList: IModel[]; + modelOption: string; +} + +export interface IModel { + modelId: string; + createdDateTime: string; + lastUpdatedDateTime: string; + status: string; } function mapStateToProps(state: IApplicationState) { @@ -109,6 +118,8 @@ export default class PredictPage extends React.Component = React.createRef(); @@ -123,6 +134,7 @@ export default class PredictPage extends React.Component { axios.get("/analyze.py").then((response) => { - const modelID = _.get(this.props.project, "trainRecord.modelInfo.modelId") as string; + const modelID = this.props.project.predictModelId as string; if (!modelID) { throw new AppError( ErrorCode.PredictWithoutTrainForbidden, @@ -637,7 +649,7 @@ export default class PredictPage extends React.Component { - const modelID = _.get(this.props.project, "trainRecord.modelInfo.modelId"); + const modelID = this.props.project.predictModelId; if (!modelID) { throw new AppError( ErrorCode.PredictWithoutTrainForbidden, @@ -859,6 +871,8 @@ export default class PredictPage extends React.Component { if (response.data.status.toLowerCase() === constants.statusCodeSucceeded) { resolve(response.data); + // prediction response from API + console.log("raw data", JSON.parse(response.request.response)); } else if (response.data.status.toLowerCase() === constants.statusCodeFailed) { reject(_.get( response, diff --git a/src/react/components/pages/train/trainPage.tsx b/src/react/components/pages/train/trainPage.tsx index 7e312dbda..470850855 100644 --- a/src/react/components/pages/train/trainPage.tsx +++ b/src/react/components/pages/train/trainPage.tsx @@ -5,7 +5,7 @@ import React from "react"; import { connect } from "react-redux"; import { RouteComponentProps } from "react-router-dom"; import { bindActionCreators } from "redux"; -import { FontIcon, PrimaryButton, Spinner, SpinnerSize} from "@fluentui/react"; +import { FontIcon, PrimaryButton, Spinner, SpinnerSize, TextField} from "@fluentui/react"; import IProjectActions, * as projectActions from "../../../../redux/actions/projectActions"; import IApplicationActions, * as applicationActions from "../../../../redux/actions/applicationActions"; import IAppTitleActions, * as appTitleActions from "../../../../redux/actions/appTitleActions"; @@ -25,7 +25,6 @@ import url from "url"; import PreventLeaving from "../../common/preventLeaving/preventLeaving"; import ServiceHelper from "../../../../services/serviceHelper"; import { getPrimaryGreenTheme } from "../../../../common/themes"; -import { SkipButton } from "../../shell/skipButton"; export interface ITrainPageProps extends RouteComponentProps, React.Props { connections: IConnection[]; @@ -74,6 +73,8 @@ function mapDispatchToProps(dispatch) { @connect(mapStateToProps, mapDispatchToProps) export default class TrainPage extends React.Component { + private modelName: string = ""; + constructor(props) { super(props); @@ -131,23 +132,29 @@ export default class TrainPage extends React.Component

Train a new model

- {this.state.hasCheckbox && -
- - - {strings.train.backEndNotAvailable} - -
} {!this.state.isTraining ? ( - - -
{strings.train.title}
-
+
+ + Model Name + + + + + +
+ {strings.train.title}
+
+
) : (
, text: string) => { + this.modelName = text; + } + private handleTrainClick = () => { this.setState({ isTraining: true, @@ -233,7 +244,7 @@ export default class TrainPage extends React.Component { const baseURL = url.resolve( this.props.project.apiUriBase, - constants.apiModelsPath, + constants.apiPreviewPath, ); const provider = this.props.project.sourceConnection.providerOptions as any; const trainSourceURL = provider.sas; @@ -245,6 +256,7 @@ export default class TrainPage extends React.Component
Model information
-
Model ID
+
Model ID:

{this.props.modelInfo.modelId}

-
Created date and time
+
Model Name:
+

+ {this.props.modelInfo.modelName} +

+
Created date and time:

{new Date(this.props.modelInfo.createdDateTime).toLocaleString()}

-
Average accuracy
+
Average accuracy:

{(this.props.averageAccuracy * 100).toFixed(2) + "%"}

diff --git a/src/react/components/shell/mainContentRouter.tsx b/src/react/components/shell/mainContentRouter.tsx index 21da626d5..92927d1d9 100644 --- a/src/react/components/shell/mainContentRouter.tsx +++ b/src/react/components/shell/mainContentRouter.tsx @@ -9,6 +9,7 @@ import TrainPage from "../pages/train/trainPage"; import ConnectionPage from "../pages/connections/connectionsPage"; import EditorPage from "../pages/editorPage/editorPage"; import ProjectSettingsPage from "../pages/projectSettings/projectSettingsPage"; +import ModelComposePage from "../pages/modelCompose/modelCompose"; import { PredictPageRoute } from './preditcPageRoute'; @@ -27,6 +28,7 @@ export function MainContentRouter() { + diff --git a/src/react/components/shell/sidebar.tsx b/src/react/components/shell/sidebar.tsx index b582e8f76..b590e9fc1 100644 --- a/src/react/components/shell/sidebar.tsx +++ b/src/react/components/shell/sidebar.tsx @@ -39,6 +39,13 @@ export function Sidebar({ project }) { +
  • + + + +
  • { @@ -49,6 +50,7 @@ export default class ServiceHelper { } } else { // Network Error + toast.warn("Over rate limitation, please try again later",{autoClose: 10000}) throw new AppError( ErrorCode.HttpStatusNotFound, "Cannot resolve the host name. Please make sure the service endpoint is correct.", diff --git a/yarn.lock b/yarn.lock index 02a4c6016..851a32b5b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1185,6 +1185,14 @@ global-agent "^2.0.2" global-tunnel-ng "^2.7.1" +"@fluentui/date-time-utilities@^7.0.1": + version "7.0.1" + resolved "https://registry.yarnpkg.com/@fluentui/date-time-utilities/-/date-time-utilities-7.0.1.tgz#56658cec84aaa7abc4cd771408a555d97b9e2af3" + integrity sha512-vAjTPl6ut0Y0Cqv1HCe7XK4Nb9kVAjj03wBj7cUFV2cDuRMkhEdKhTj3FE31GqAgB1jqCtSyitD5lRICcvjdvQ== + dependencies: + "@uifabric/set-version" "^7.0.13" + tslib "^1.10.0" + "@fluentui/keyboard-key@^0.2.1": version "0.2.1" resolved "https://registry.yarnpkg.com/@fluentui/keyboard-key/-/keyboard-key-0.2.1.tgz#2317b29b9f62e2b3c6777a095550ab1c1bd98558" @@ -1192,26 +1200,35 @@ dependencies: tslib "^1.10.0" -"@fluentui/react-focus@^7.12.5": - version "7.12.5" - resolved "https://registry.yarnpkg.com/@fluentui/react-focus/-/react-focus-7.12.5.tgz#ed3d218109f86b4e3f4debe54b0d029ee1ccfc57" - integrity sha512-K2DrfMI74pkuGeiqstWgqGVnMg836CE3eJ3fydkXa19+6lUjkB4yDrSgwrQkvvEXixCiJyfvAxyGuFJ74EKQwA== +"@fluentui/react-focus@^7.12.9": + version "7.12.9" + resolved "https://registry.yarnpkg.com/@fluentui/react-focus/-/react-focus-7.12.9.tgz#b4bca394efa57ab1c5e1719796697ed5ea8aecff" + integrity sha512-1VeyUEw/kC+2DMlrpzrB9Ylv0cT0sLE6aL1hUODZ3EorvyCYAUWaXitbMmIfJdUif2434EwSY25eFHO+M7Yo3A== dependencies: "@fluentui/keyboard-key" "^0.2.1" "@uifabric/merge-styles" "^7.14.1" "@uifabric/set-version" "^7.0.13" - "@uifabric/styling" "^7.12.15" - "@uifabric/utilities" "^7.20.3" + "@uifabric/styling" "^7.13.0" + "@uifabric/utilities" "^7.21.0" tslib "^1.10.0" -"@fluentui/react-icons@^0.1.24": - version "0.1.24" - resolved "https://registry.yarnpkg.com/@fluentui/react-icons/-/react-icons-0.1.24.tgz#40ee3a8adb2ffcd84f9cfbeac51870f14303961f" - integrity sha512-PNAN1CjfBNL6p0fViftKDK8xcVnt05psEhO9wzCSOld9iw2eGu3JEObzolKGGoASvsiPJEQxbGOHZPuasVKVFg== +"@fluentui/react-icons@^0.1.28": + version "0.1.28" + resolved "https://registry.yarnpkg.com/@fluentui/react-icons/-/react-icons-0.1.28.tgz#64a54187794b0843b38f6beda71457368b1d6a8d" + integrity sha512-MoXrCFPFwuXjt4B/YPaFm53sP/g9bEJNdkp0bIgCAVnFoLlsnt5/qcpdqlWE2stzWGGQizwexpANHe4v81akpQ== dependencies: + "@microsoft/load-themed-styles" "^1.10.26" "@uifabric/set-version" "^7.0.13" - "@uifabric/styling" "^7.12.15" - "@uifabric/utilities" "^7.20.3" + "@uifabric/utilities" "^7.21.0" + tslib "^1.10.0" + +"@fluentui/react@^7.117.2": + version "7.121.0" + resolved "https://registry.yarnpkg.com/@fluentui/react/-/react-7.121.0.tgz#367a645cb33f2f052dd950fa5759f802f2b204c2" + integrity sha512-4m5gws5yVlS+g8+0oqZMdgKJlKXQ5tp0l/maCsAYCNKg1rIOktN97FZrGRgkaoMkMRZ+1f851eg6bTcryJD3bQ== + dependencies: + "@uifabric/set-version" "^7.0.13" + office-ui-fabric-react "^7.121.0" tslib "^1.10.0" "@fortawesome/fontawesome-free@^5.12.0": @@ -1829,24 +1846,24 @@ semver "^7.3.2" tsutils "^3.17.1" -"@uifabric/foundation@^7.7.22": - version "7.7.22" - resolved "https://registry.yarnpkg.com/@uifabric/foundation/-/foundation-7.7.22.tgz#acf0d8f3766da83c21977f35216eb9aa6872cef1" - integrity sha512-vTWhNT0wz0VB5DagSCPWQ+7d8Yv7AYOGsvOgPzS6FzA3pxSLX6xmN00ica1gCCZBz4xZn4Yw7YjcyF8bDARqSw== +"@uifabric/foundation@^7.7.26": + version "7.7.26" + resolved "https://registry.yarnpkg.com/@uifabric/foundation/-/foundation-7.7.26.tgz#216432abbf6d88926599e74653cd7167198fdae0" + integrity sha512-9pvmRYEINsXnLxnuCPf3M0Wxlxbs/ltP/hqYwSS0pYso/zO8BFNMMNvN9DFr67RE33bfpOreruKSKi+lj3TKjg== dependencies: "@uifabric/merge-styles" "^7.14.1" "@uifabric/set-version" "^7.0.13" - "@uifabric/styling" "^7.12.15" - "@uifabric/utilities" "^7.20.3" + "@uifabric/styling" "^7.13.0" + "@uifabric/utilities" "^7.21.0" tslib "^1.10.0" -"@uifabric/icons@^7.3.48": - version "7.3.48" - resolved "https://registry.yarnpkg.com/@uifabric/icons/-/icons-7.3.48.tgz#c1c0ddb700eac7ed20d4d766fa6df880ed8a7eb2" - integrity sha512-DUhRluQrYAvvyElr5F/Gzkscl+gArgeEtLVJsjqXAsop8SAvJCEVCnSxVEZVDOOlsytEVVPpxiHGkaDow+26fg== +"@uifabric/icons@^7.3.52": + version "7.3.52" + resolved "https://registry.yarnpkg.com/@uifabric/icons/-/icons-7.3.52.tgz#1aa0e57b677e5853e563883cdc2b96ae89e14b0d" + integrity sha512-vOb1wmNMyGj6xVbbb7eqv6izY8n/1lDSqDmGyDWwbvwbrDI7+GBQWmDcdV5D/YWI9YwN/xKevhbVrLVq4IPGZg== dependencies: "@uifabric/set-version" "^7.0.13" - "@uifabric/styling" "^7.12.15" + "@uifabric/styling" "^7.13.0" tslib "^1.10.0" "@uifabric/merge-styles@^7.14.1": @@ -1857,13 +1874,13 @@ "@uifabric/set-version" "^7.0.13" tslib "^1.10.0" -"@uifabric/react-hooks@^7.4.5": - version "7.4.5" - resolved "https://registry.yarnpkg.com/@uifabric/react-hooks/-/react-hooks-7.4.5.tgz#7f6424a62d357c370f5b7c294c1755bc9239f3d5" - integrity sha512-OLEBII+7x4rlTWjQ6hvMWS+CuWRJgvEfCsUcFMu5D5teXTr0bZQThyW8oVvkqKsNmF2JdwwqT6dSwhwgarlFzA== +"@uifabric/react-hooks@^7.4.6": + version "7.4.6" + resolved "https://registry.yarnpkg.com/@uifabric/react-hooks/-/react-hooks-7.4.6.tgz#50619764bdf9e019be46916229d7d350c4c3d6d6" + integrity sha512-QuVPRv5kQHGtMUYt7yeKbYFAiFdes60UUNJTfe9orwKCJF0ahVIvqthj7s1gCthZxnPTxZPxOyqZjnWi6uPnLA== dependencies: "@uifabric/set-version" "^7.0.13" - "@uifabric/utilities" "^7.20.3" + "@uifabric/utilities" "^7.21.0" tslib "^1.10.0" "@uifabric/set-version@^7.0.13": @@ -1873,21 +1890,21 @@ dependencies: tslib "^1.10.0" -"@uifabric/styling@^7.12.15": - version "7.12.15" - resolved "https://registry.yarnpkg.com/@uifabric/styling/-/styling-7.12.15.tgz#599fdc0fde0b3c8af221dc2fd68052a41c42275f" - integrity sha512-mp/nRyE5Ig6a8BPEkXy/F8ckeQptQCm1SOQDPB1VT1PlknHoOXqqTVn2e1e2DGpWcpFIw4dbodqym3rgpUqkjA== +"@uifabric/styling@^7.13.0": + version "7.13.0" + resolved "https://registry.yarnpkg.com/@uifabric/styling/-/styling-7.13.0.tgz#b1b97bc163643117ff56768d49412a994da36b05" + integrity sha512-2cimQu2oiZ74uC1L9n6nWVFWPBlgVop+n2E3mGhw5cS+rYt5u/Rw2ntMd4JtoXWJ9tLet6gGAMTjZPFy1QEryw== dependencies: "@microsoft/load-themed-styles" "^1.10.26" "@uifabric/merge-styles" "^7.14.1" "@uifabric/set-version" "^7.0.13" - "@uifabric/utilities" "^7.20.3" + "@uifabric/utilities" "^7.21.0" tslib "^1.10.0" -"@uifabric/utilities@^7.20.3": - version "7.20.3" - resolved "https://registry.yarnpkg.com/@uifabric/utilities/-/utilities-7.20.3.tgz#b2a784ad1fd8f054429fda8880a28e5932e75c1e" - integrity sha512-Amg+qdnNKx0yxjoEFHanM2jTCYfCZAlHPDHcS+BCiSwzeQrEPhlOrtZVPJtagNVhXLFiC09uDsmE/BF6dHb+ww== +"@uifabric/utilities@^7.21.0": + version "7.21.0" + resolved "https://registry.yarnpkg.com/@uifabric/utilities/-/utilities-7.21.0.tgz#c4810a32756e85b08a42d3fd19dda5fff197d1fb" + integrity sha512-K+BjTPzW56+N8JAbC4a32EnlGVEOUCk7q2CpBxsbRkf21tVsUX91rs4hMRTow+TMjK6eJudlqSotd3se1v9KPw== dependencies: "@uifabric/merge-styles" "^7.14.1" "@uifabric/set-version" "^7.0.13" @@ -8700,21 +8717,22 @@ obuf@^1.0.0, obuf@^1.1.2: resolved "https://registry.yarnpkg.com/obuf/-/obuf-1.1.2.tgz#09bea3343d41859ebd446292d11c9d4db619084e" integrity sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg== -office-ui-fabric-react@^7.86.1: - version "7.117.3" - resolved "https://registry.yarnpkg.com/office-ui-fabric-react/-/office-ui-fabric-react-7.117.3.tgz#8ff2fa9a78f59d1f81a60cf8c928122c2b134aa2" - integrity sha512-MnJgfBj3hv2pBiL4VOHUQBDSa9datsVzbtGNo055UHWjdmlIA5PZkpopLoXJ6Ri08xy3SUdJt2IzpA6XbT4gZQ== +office-ui-fabric-react@^7.121.0: + version "7.121.0" + resolved "https://registry.yarnpkg.com/office-ui-fabric-react/-/office-ui-fabric-react-7.121.0.tgz#5f5b8349b4fe8a05bde71649b9714209d0ee7060" + integrity sha512-V8jOux8KzSmkECffRXxGQNzcQPnAyGDG6MwBbm2pRd6cYpvBl2nL6QxsZkKOEuC3A+EfLLV160fMry44Ldc81Q== dependencies: - "@fluentui/react-focus" "^7.12.5" - "@fluentui/react-icons" "^0.1.24" + "@fluentui/date-time-utilities" "^7.0.1" + "@fluentui/react-focus" "^7.12.9" + "@fluentui/react-icons" "^0.1.28" "@microsoft/load-themed-styles" "^1.10.26" - "@uifabric/foundation" "^7.7.22" - "@uifabric/icons" "^7.3.48" + "@uifabric/foundation" "^7.7.26" + "@uifabric/icons" "^7.3.52" "@uifabric/merge-styles" "^7.14.1" - "@uifabric/react-hooks" "^7.4.5" + "@uifabric/react-hooks" "^7.4.6" "@uifabric/set-version" "^7.0.13" - "@uifabric/styling" "^7.12.15" - "@uifabric/utilities" "^7.20.3" + "@uifabric/styling" "^7.13.0" + "@uifabric/utilities" "^7.21.0" prop-types "^15.7.2" tslib "^1.10.0" @@ -13488,6 +13506,11 @@ yargs@^15.3.1: y18n "^4.0.0" yargs-parser "^18.1.1" +yarn@^1.22.4: + version "1.22.4" + resolved "https://registry.yarnpkg.com/yarn/-/yarn-1.22.4.tgz#01c1197ca5b27f21edc8bc472cd4c8ce0e5a470e" + integrity sha512-oYM7hi/lIWm9bCoDMEWgffW8aiNZXCWeZ1/tGy0DWrN6vmzjCXIKu2Y21o8DYVBUtiktwKcNoxyGl/2iKLUNGA== + yauzl@^2.10.0: version "2.10.0" resolved "https://registry.yarnpkg.com/yauzl/-/yauzl-2.10.0.tgz#c7eb17c93e112cb1086fa6d8e51fb0667b79a5f9"