From d059580cfefa053670c45c5d8ec7bf250bc4db27 Mon Sep 17 00:00:00 2001 From: alex-krasn <64093224+alex-krasn@users.noreply.github.com> Date: Fri, 17 Jul 2020 15:33:44 -0700 Subject: [PATCH] Alex krasn/feature share project (#344) * added share butoon to "Additional actions" * Merge branch 'master' of https://github.com/microsoft/OCR-Form-Tools into alex-krasn/feature-share-project * added sharing functinality * runbook and rephrase input placeholder * fix: piplene error - updated package.json and yarn.lock * fix on pipeline erro r * fix: deletes log * input styles in .scss * handle string decode error * handle error for no shared connection * enable share button only on provider type "azureBlobStorage" * strings * feat: devider for commandbar * fix: icons * add separator and hidding connetction type when chosen another one * fix: initial state of modal * hide separator on connection choice * + warning for no connections, and added localization for strings * fix: connections check * fix: check if project in predict page * fix: canvas state * updated: manual * fix: add new URL() in try-catch * fix: class name affecting some browser's extensions * fix: for project tokens with same name * + string for error message and change color of toast ot red * new error message and color * refactor and renaming * fix: user shared project name * refactor; fix: indents, whitespaces, grammar. * add: check if have cloud connections * fix: fix grammer for share project message Co-authored-by: stew-ro --- docs/manual_testing/manual-test-runbook.md | 154 ++++++---- src/assets/sass/fabric-icons-inline.scss | 280 +++++++++--------- src/common/localization/en-us.ts | 13 + src/common/localization/es-cl.ts | 13 + src/common/strings.ts | 13 + src/common/utils.ts | 11 + src/config/fabric-icons.json | 136 ++++----- .../cloudFilePicker/cloudFilePicker.scss | 18 ++ .../cloudFilePicker/cloudFilePicker.tsx | 118 ++++++-- .../components/pages/editorPage/canvas.scss | 4 + .../pages/editorPage/canvas.test.tsx | 1 + .../components/pages/editorPage/canvas.tsx | 39 ++- .../pages/editorPage/canvasCommandBar.tsx | 16 +- .../pages/editorPage/editorPage.tsx | 4 +- .../components/pages/homepage/homePage.tsx | 10 +- .../components/pages/predict/predictPage.tsx | 7 +- src/redux/actions/projectActions.ts | 31 +- src/registerIcons.ts | 3 +- yarn.lock | 5 + 19 files changed, 574 insertions(+), 302 deletions(-) create mode 100644 src/react/components/common/cloudFilePicker/cloudFilePicker.scss diff --git a/docs/manual_testing/manual-test-runbook.md b/docs/manual_testing/manual-test-runbook.md index 78300cd55..bcb422308 100644 --- a/docs/manual_testing/manual-test-runbook.md +++ b/docs/manual_testing/manual-test-runbook.md @@ -1,29 +1,56 @@ # Test Runbook +## **Feat: support project sharing via string** + +> ### Feature description ### + +- Support project sharing bettween users who have access to same storage container + +> ### Use Case ### + +**`As`** a user +**`I want`** to be able to share to a project via shared string +**`So`** receiving user don't have to manually copy-paste project info into app settings + +> ### Acceptance criteria ### + +#### Scenario One #### + +**`Given`** I've opened a project, clicked on "..." dropdow in Canvas Commandbar +**`When`** I click "Share Project" I should see tha message that shared string been saved to my clipboard +**`Then`** I can paste the string from clipboard +#### Scenario Two #### + +**`Given`** I've received the string with a project +**`When`** I open the home page of the FOTT and click on "Open Cloud Project" icon, I can paste the string to the input field and click "OK" +**`Then`** FOTT should open the shared project as expected. + +___ + ## Fix: check invalid connection provider options before project actions > ### Feature description ### - check connection provider options are valid before creating a project -- check connection provider options are valid before opening a recent project +- check connection provider options are valid before opening a recent project > ### Use Case ### -**As** a user -**I want** a notification when I try to open or create a project with invalid provider options -**So** I know how to fix invalid provider options issue +**As** a user +**I want** a notification when I try to open or create a project with invalid provider options +**So** I know how to fix invalid provider options issue > ### Acceptance criteria ### #### Scenario One #### -**Given** I've created a connection with invalid provider options (e.g. invalid SAS token for Azure provider). -**When** I try to create a new project with that connection. +**Given** I've created a connection with invalid provider options (e.g. invalid SAS token for Azure provider). +**When** I try to create a new project with that connection. **Then** a notification will be displayed telling me my connection is invalid. #### Scenario Two #### -**Given** I've created a connection with invalid provider options (e.g. invalid SAS token for Azure provider). -**When** I try to open a recent project that now has an invalid connection provider options (e.g. the Azure container was deleted) +**Given** I've created a connection with invalid provider options (e.g. invalid SAS token for Azure provider). +**When** I try to open a recent project that now has an invalid connection provider options (e.g. the Azure container was deleted) **Then** a notification will be displayed telling me my connection is invalid. ___ @@ -35,22 +62,24 @@ ___ > ### Use Case ### -**As** a user -**I want** to release my project as a distributable +**As** a user +**I want** to release my project as a distributable **So** I can easily set up FOTT > ### Acceptance criteria ### #### Scenario One #### -**Given** I've updated dependencies. -**When** I run `yarn release`. + +**Given** I've updated dependencies. +**When** I run `yarn release`. **Then** a distributable installer should be created in the releases folder. #### Scenario Two #### -**Given** I've created a distributable installer. -**When** I execute the installer. + +**Given** I've created a distributable installer. +**When** I execute the installer. **Then** a the FOTT desktop application should install and run as expected. ___ @@ -62,15 +91,15 @@ ___ > ### Use Case ### -**As** a user -**I want** to delete a document and it's files through FOTT +**As** a user +**I want** to delete a document and it's files through FOTT **So** I don't have to delete the document through a storage provider #### Scenario One #### -**Given** I've selected a document in the editor page. -**When** I click the overflow menu item on the canvas command bar and then click "Delete document." -**Then** FoTT should delete the document in the storage provider, remove it from FOTT's current project, and select the project's first document. +**Given** I've selected a document in the editor page. +**When** I click the overflow menu item on the canvas command bar and then click "Delete document." +**Then** FoTT should delete the document in the storage provider, remove it from FOTT's current project, and select the project's first document. ___ @@ -82,39 +111,39 @@ ___ > ### Use Case ### -**As** a user -**I want** to use FoTT's existing features through a desktop app +**As** a user +**I want** to use FoTT's existing features through a desktop app **So** I don't have to use a browser to use FoTT -**As** a user -**I want** to use files in my local file system +**As** a user +**I want** to use files in my local file system **So** I can keep all files on premise > ### Acceptance criteria ### #### Scenario One #### -**Given** I've installed new dependencies and started FoTT in Electron. -**When** I click a command item in the title bar. -**Then** FoTT should perform the command as expected. +**Given** I've installed new dependencies and started FoTT in Electron. +**When** I click a command item in the title bar. +**Then** FoTT should perform the command as expected. #### Scenario Two #### -**Given** I've installed new dependencies and started FoTT in Electron. -**When** I perform an action for any existing feature. -**Then** FoTT should perform as expected (the same as through a browser). +**Given** I've installed new dependencies and started FoTT in Electron. +**When** I perform an action for any existing feature. +**Then** FoTT should perform as expected (the same as through a browser). #### Scenario Three #### -**Given** I've installed new dependencies and started FoTT in Electron. -**When** I create a new connection with local file system as the provider. -**Then** I should be able to create a project with the created connection. +**Given** I've installed new dependencies and started FoTT in Electron. +**When** I create a new connection with local file system as the provider. +**Then** I should be able to create a project with the created connection. #### Scenario Four #### -**Given** I've installed new dependencies and started FoTT in Electron. And, I have an existing project in my local file system. -**When** I click "Open local project" on the home page and select the existing project. -**Then** FoTT should load the project as expected. +**Given** I've installed new dependencies and started FoTT in Electron. And, I have an existing project in my local file system. +**When** I click "Open local project" on the home page and select the existing project. +**Then** FoTT should load the project as expected. ___ @@ -126,16 +155,16 @@ Enable reordering tags quickly > ### Use Case ### -**As** a user -**I want** to be able to move though tags list quickly +**As** a user +**I want** to be able to move though tags list quickly **So** I can reorder long list of tags faster > ### Acceptance criteria ### #### Scenario One #### -**Given** I've opened a project containing documents with long tags list. -**When** I clicking fast on tags buttons 'Move tag up' or 'Move tag down' +**Given** I've opened a project containing documents with long tags list. +**When** I clicking fast on tags buttons 'Move tag up' or 'Move tag down' **Then** it moves without visible jittering. ___ @@ -150,22 +179,22 @@ Adding the following buttons to the canvas command bar: > ### Use Case ### -**As** a user -**I want** to rerun OCR on documents +**As** a user +**I want** to rerun OCR on documents **So** I can update OCR results > ### Acceptance criteria ### #### Scenario One #### -**Given** I've opened a project containing documents and I'm on the Tag Editor page. -**When** I click "Run OCR on current document" in 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 current document" in the canvas command bar **Then** I should see "Running OCR..." for the current docucment. When running OCR finishes, I should be able to view the document's updated OCR JSON file. #### Scenario Two #### -**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 +**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. ___ @@ -178,35 +207,34 @@ ___ > ### 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 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 +**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 +**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. +**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 +**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/src/assets/sass/fabric-icons-inline.scss b/src/assets/sass/fabric-icons-inline.scss index 1c879ad0a..b309758af 100644 --- a/src/assets/sass/fabric-icons-inline.scss +++ b/src/assets/sass/fabric-icons-inline.scss @@ -1,139 +1,141 @@ -/* - 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,d09GRgABAAAAAB+UAA4AAAAANsgAA9maAAAAAAAAAAAAAAAAAAAAAAAAAABPUy8yAAABRAAAAEgAAABgLdt/0mNtYXAAAAGMAAABMAAAAvLCJ7WcY3Z0IAAAArwAAAAgAAAAKgnZCa9mcGdtAAAC3AAAAPAAAAFZ/J7mjmdhc3AAAAPMAAAADAAAAAwACAAbZ2x5ZgAAA9gAABXkAAAkjI5cHb5oZWFkAAAZvAAAADUAAAA2Ah3Oi2hoZWEAABn0AAAAGwAAACQQAggCaG10eAAAGhAAAABPAAAAfhZgD/dsb2NhAAAaYAAAAHwAAAB86rL0BG1heHAAABrcAAAAHQAAACAAXgJGbmFtZQAAGvwAAAP4AAAJ+pSV8VZwb3N0AAAe9AAAABQAAAAg/1EAtHByZXAAAB8IAAAAiQAAANN4vfIOeJxjYGH/wjiBgZWBgXUWqzEDA6M0hGa+yJDGJMTBysrFyMQIBgxAIMCAAL7BCgoMDo+n/ljCAeZDSAawOhYIT4GBAQATKglUeJxjYGBgZoBgGQZGIMnA+AbIYwTzWRgvAOkCBgcGVgaDx1Mfr3+84/G3J9Oe8z7ne87/XPC50HPh59LPZZ7LP1d8rvPc6rndc+/nvs+Dn+c8L3jB/0LuxYQX018sfrHmxYkXb14qvlR+afTS7ZXVG/E3J96qvm17u/vt4XeZ7/98YP6g+dHu47JPUz8zfGb6bPhtzXezH0v+/2dgwLBRAIuNXmAbs+E2TiNgYwZuG2UWy3TJNMm4Si+T+CLxU5JV4pPEN4mPEu8kXkq8Borcl7gkcUPisMQpiT0SSyUWSbCIvxDvEG8WrxAvEA8VNxL7J/ZX7L3YHdGXIrzCscKMQguFcoXSBA8JGAloCTDzfeTr4p3Da8xrwMvM2cnJwD4ZEtr0BYxs9LdzsAEAFeK3unicY9BiCGUoYGhgWMXIwNjA7MB4gMEBiwgQAACqHAeVeJxdj79Ow0AMxnMktIQnQDohnXUqQ5WInemGSyTUJSUM56WA1Eqk74CUhcUDz+JuGfNiCMwR/i62v8/6fL9zp/nJfHacpUcqKVacN+Gg1AsO6u2Z/fkhT+82ZWFM1XlW92XBagmia04X9U2waMjQ9ZZMbR4ftpwtYpfFjvDScNKGTuptAHaov8cd4lU8ksUjhBLfT/F9jEv6tSxWhtOLJqwD916z86gBTMVjE3j0GhB/yKQ/dWcT42w5ZdvATnOCRJ/KAvdEmoT7S49/9aCS/4b7bci/q0H1Tdz0FvSHYcGCsKGXZ9tQCRpg+Q6E/GTGAAEAAgAIAAr//wAPeJyVWgt4FNd1vmfuPFZIWmm0Wq1YoRWr1exKQlqEVqsFIUsrWQgBNhBEjFkBAQdjUAyOH4CxsbmQYMBAsEu+2KGunUSR2yQmDrab1CX9orRuSUxIXZRHY7X5Gr+axCGJv6QNWs0MPffOrlgWO0m1zNw7d+77nvP/55yBSORZQuRPKrsIJRohCT2oG0E9+Cz9qfl16evWUqLsSj/6aXk5wT8gHnhHu6Q2EwUffAWQAO1kyvSYnhQ9qTZjjl5KmduxHv658EcKsZ43qIf0YDyox3QXMZPMTNIxJmdSOmYmsf7vyO+0Yq2Y1y8ArQAi2HcB+OQh6YWUdbN1c0p6wVqekl6UXkzJ5JpHazlWweFkohJVS2tpkiD3kkPkL3Hk8gpvuepVqKFqHoXWqqHacIgannAEi9rC8bb2uOFR2hNY1Noea62IYaUKn+GBLoi3hSOJiBKFiCeiRSFUq2oRzeMGzdB8bvCWV/g0nxEAH/UlAhBrbU/4ErQLEkoCWgOSt9wthWqjUrytS/K0dmEaxWc3lgck9Q8AQJ+xf1zp77P3f76grAD/fd7e3+evtH/8DFAA23oGGvAt7Mu+hX34FhqesQ9Lsiz96hb7vZqecPdrn/a1+CrnVp58rTucnG2/d8uvnLdQcv1bKLnF/IvlR7YsXLjlyPJsmtg40NAwsDGRSeW6/99krNyF2P+JY+GI08P96tqp5r69bqrScO60eGqxnImJlEh4xsxFNIZSWsKlClBSIa4EnYyLWMTGS2OTKMjAhZlNWMScoAYlE8ywiGVIE5TLSbafQuzHS6qwr2KpAJppAQQLaEQxNNCBJyoDZuNlEpM4OZvJ2H4yIL0hvWHh/T27xC55jz85OXkM/2ycKk8lnI99Au6Gu6dO3H/58v2YZ3A3z/HpSVSz1CGuRwUSCrs2Yq4316eoRS2J0i+YG1LUNvmScSGMMo3JjHhIhCzE2eZJWPBPPAOpiobKy0PRqkxKq/5UgcxyHv6slPDzAdxXlZFmMsTPJzuNYGYesXhWB2LBrFJAPJsLZt95s/OOTS9EJTcdHe6Orbmv1ya9962JdQ8fvSm5fmTP4sV7RtZLJJuzkn9WNYWw0M171vAavO6aPTeHGC/Pvuep/WfUEWeTxbkGcgPpIcQoR4yJgBsBIyrjIuRYa0BGDNICGuQBAQ16gy5SG/B7zN9sfPuhTS89tX9da+u6/U+9tMnJr15R8uHdx088srUrPbb74lNDQ09d3J1Jvysxi/XPavTQ0hWrc9s4+Yfe3liy+r6jJw7vWCK9kNNMpBJjOficwJOqkP2IaJJbkkO1dVEJZ1mGUMYBO6S7Kc4VV9GFq5EQDROK/iyUn9ux5ODZt+z08eN2+q2zB5csPfLKg/avJcJ2/sMjA8mdXzz31t4H3z73xZ3JTV997/Fa7fXmb9lT9j89l6nN24LyqWPg+sW3+phNoGH+l8Z/v99p4bT+DFS89df1D5Jr9pjzUzHOF9VdQYZyGMJFLgud18gUswdTMEgHU7zMRbAQDPhqyv4qXZXi3DVIiHYa+cHFOQbXxklGGzQvMfNSip7G3++o53JaU8WjOUicNqp22kVEGykBSGGg4TtzMEU9jHroi+YgPZ0yL2nq5TT18Kle1YPZ5MYcbZ1Wg1ZfF0WxmGaGOZAVCy08rSHqQyuPb7uhc9uxFVNs+OzRlSuPnh2Ozq3ftvfY0rv++cmP6vH1/fX1/evjrP+BkfX9B7avnrlscOXRbZ2d246ulFnnHcc+xJ94K966oOujfXXLj3zT8vA2vC3qxQP9jTd9rDfEx+B1Bb/DKKGKpVkcl4wCzvDKiClRO2VuoF+gHJ1SHKdEXYkSqg5N18XDyEGu9XSEjqRMatLMGQIRezKTVCOKEQjQazalGzJmB7SF50JQQ4rVXisqK1I5vkwKAFPx8TWVpJlCppDqXa7z6a+fd7mkwqLacKQ0i0WlkXBt0Wt01BxC2J9ihSHXq6+6QoXiXAYJw/MfIkWI+tzKiOCY/KdFQC8AkZUJnuagxa4Qiw2m1GYL9QyYRLQhc1VqlTnB9Y4aq1LWIDV43ikRnOLIqJuUI6PM4baHqoHCTQl+1oCmQoFkRGkEuuj0cbtIa0t6wjr0tGK07O1d9MBQfLJ/+LMheYE9MdUCS2Y0J1dFFXtmtM4bXLBsTnjBvKaZ6pnevdjmaWkPtmmNDz0wtSv02WG5A4x04aroqmTzDPrGzKZ5C8Jzli0IeuuiMwVvOnMLCW0XgORGVQ9HgBtE8bYERIFjVb6poiTzQMX8jcfvD+eATh4s4XquwS/zt56w3++h+jS+TZF8OELBGIdRrdmxKVHH8GCap8at8RSM03FlfOqiFE3ZUXqRL0MlDHWYr6UGV9NM5pE4WYBrCunTwqSH4sFp9qiGBikehKBeXoHPaO2F9BhkLo2wQFt/vUnq+9sCeMRceoBxOZIyfM8FKOyXiT9sEcYgc6ksneRNeGNljLGs5DEUmnTSHw77x2TswLkc2Zdwxmhac/5efD1/52+7p1VGaQmAN9QWxWNyy5hpT3RJdTJZcYxruKOvXNOzKJBNT4A/fT5Z38gG9j137vU773z93HP7BlhjffJ8GvwKyW3Ku7JYbmOeymzxo+c/OXhm3a7vf/mRdS0t6x758vd3rTsz+Mnzj3K+kwBRUW0WWFyEWh8Pej3cCeCwCGiiNFsnrZN4cAzGcafouPUp6Z6U3ah8Kb0GfiTaM36Iov08bI9k73HkUa2GEG6GOr0RIRRIyhhLrjjJhpc1NA2s27huoIll0iwHs4Zlw+wkQ7wVmRWrD9+9MbV2830DY2OY/UgqhdksZzsd5fAJ1wdEaIM7Ce/P2kr+0QSv+jLaJSHcH0je1pt52vENIFcICgkK0fY8PcrTs4ffh7NFO87b8pUr5IoLNG7iFqJMtaFg4TmgkPPJBUHMLhinFQtRgmojbkB+ibUm9KiTqfC1ixfaFYqWYdpWpCk8OErsh/n0Hr7rMYCvfap/ZC3A4zva9i8FGBrpe/QlgE9/jH7Ctm2TSgdsSWKmfQDsA+bGjQd2tM8/sF4eaGo+0N42fGAdCj3XUSY4s5DoxOcwNp9azBOLGFwdxaUHNTaFNjIdAsaM0VGTSQbi7hAXJBtRFU3tiSkDUOFs/I1O4GyZhGicZuIMmRjDQyqRUQhMk6ujW9h/ARjcRlDG8sxV26DcomcaseI45HllLN+WlZIm1qDsb6XzJrPiwg7AZbmIfEEwmTcY5xaGfOHChWvsE02slzhr0zPrxDOJuYi9ZfIUnFK3WKemYvIF+CzeyQU4ZW9Rt8ApXqhuuWBiX4BDafvQNynDVRlXsULjHXZRX60GWgLa0TulnFywUP27hoH22Yj0lWkiv1PZ3DLfqPaaN9Ueoi9553zYnLQPbaAuE5VfY7PblzQYC1qaKqf8KqmM1lUwrHioFitWBxrtLeblDbCHalMk4yOJ/a1CHVlIulFLrlENt0w5ME2bhlGZxlAxUJ/p2MH/+dHzD/X1PfT8j/7nYG7+3h+8/OSdnZ13PvnyD+6954fZ/A9fYPzv+upOXiOi0j1XG2Qbc2pGgMjhtyqH3+SAVJZvzkp1nsw8Q5l5y+Q4qG+efWTJkkfOvulYstknUI/vffvcCLdFR869vTcn7yLvWzvT02V2XYNMnk9RgeyeFgorwY/2YVYzhLAYsYjzyG1TVGh+4yqCqn+FSCj+MGqx0VEgqA5EZswi8v79FqH7uVHEBUcRNdnoFLq5INrJKMeMizsTfiQfX/i9EWGZ5oAZnbYKnFO+breSkLTHmD0GSZb8gM3ZO/zKV45tTiQ2H/vKK8M5eTnptIOkRD5ogybZdc0y+aztiPwZIFHSmtUJDXcsQ/Oy4ymixqOxhRuIawjhhqIZyfxzQ+Vue/3kGcHuG5t7YvUNc2uk/+JqniYQ128w6L9bszPl9uc0kv4bmym3Ktwc8AdmmS8rguA9s2pneXgfCFArpOFgGIgo4hEHwk8UD0Djc6QCBUoQl3woj9xKiXBr0OMgsx7XxZbrXj04UwpmD1+H7IWHgXg3KXDOZpmYA81c2RiEiSedRvxK42gZWuBJhlys6Udxsdw7c/wTadxFlNO4qbpAM5+Glr5moOUV4V6fCzH4Yso+CdtTF837U7DdPpm6aA1JoyhzaJVtx/LxcXyPxePCHBP+TmbtJWjhzyV9ZPNVPw1CnpgnFAl53FACsQg6kdz80mOOZexYQpFQhHs9uXSrTEN6hc/LI3Ccgit4qM3rhqteX7r4nnd3/vLe+X94AL2PNd++lSKtvbplC/iXxVoWNZX7Wm5qvyUKUDqrYWYoOquoyD8nUN0cKJXub13VMbu2e+38We3+ilhVqN0oX9pX3ear7tW4j8FQf0CS7rOOfQ52zyifkU4XeGdYv5CetF+fEzW6Vs5pWbuoYdmDlXNmewItndX+WH1lhTGvygouXNWaSPWEZhR+pNQzs7Ej2LvaW3ar7ua+1TiCAPc/C/BBAxrycAdUOY0OKp6P/VwKLsEltTgFH7psapp4tj283Q+IqjyPflZBNuZKYx5QVg/ZHrtsCFZhYxiUbk7ZHriEfWnSZUuYadO2siz8Zh29npnC88VjyTIwcMshc8kTkjGJ3IuuxISCEGOi0CB2MOdCqZhIGy4yYU9wFwxtZMaNEuciZDquhuOVkQpEtwBHmHgBxIWAc9OEB+ogR9qxS2bFpAvchbIwL6EZIvF4AEo3KiHKusKetmvt2qdRpgck8h2JfUciMJAWegDEicfxvDM+X+//4vhzEd1Wcm8f0LLTfFpEFTeUMi2SCIsbOj2RhK9d3NAhT/i0Crzlh2gEHDtGVQi9U9AeUNW98/aGYm27Y4GamkBsd1sshAXvW2ovzXoJmdTeKvaSyEOSwVdg2BNg/clurpbar+V0JlIZ3ZZJIo7GRkMJuxQ+/Di8o5yWT0/7U8ppiysreCSPPGhxvbYvSZlYRZYTROw/q7Fcs9DNQRFydAyzuL/c+BT8tZgMkbvIJ671ZCLo2+oZrz6juWhz0lyjTAtQXxeFPEWHnA3n5UZ+wDyZjcIt2Lq6o/CjZ589Hp+/oS/MS3p3rmkN922Yz59b1+zs5TXDN25IzLjz9M/2F3UM3jG1J3/PgOWX0GTeQckk23dk0Yb5aufmw6vtXdjb1gW8jI+zYOtgRxEtcsqys7sDy/b/7PSdMxIbbgxLY3mdpm/NK/h5/jyEDHvISW27dgm1tQY9JLS7oK29G0Djvgk67BF01ZXWTIkQUfQTy9USgASH0wof+MTHC759vBRIX/eb0p43ukfXLjnSYb+cgv6mZhhL7mw+bHcebp6Hi6FfamrGStahTCVYnLLjzfP4kng1eIUm1452v2EderO7r7nJ/vsULO448od5/M3h5p1JviXyTR1HlvBK0h5RCfpT5nn+DrdmHh9JyBo31BQuPz5Sx2VNmLUhLiOcDXT+eQh1LSsdCuu44/gqNnjsjg4UbLR2WPaA0R9IDj6xa0WROUWVohW7nhiEsSw1oqWRPaNrY4cOF3+gdZiFpD9qFUKxw6l/3ApMZ3hXBIYyuPgw8mM7oq+B3toytAYSiP/C28Th2xOgCndXSjiqgQVuMCJZelS1SFhxQgGY5aVOeGBpSt0wZCfa7+hZeWxrZ13IHnvoUda4/K5+GOfefF1XjT2ie2DgYElNCXf2ZelgI42LyMBPtt/2zaHb7vtv9hj4J1/tqW9U/yM1OTIE3w3VdW49trLnjnZIbrN/Ozp0ZuREh3WYBwY8Oqyv6fq3g5LMgwjY5cFPnHdiBGHjtngbjxqseiHHL5MQPGZwtkGKCXnxaNExoGOXEa8Z99YvZ8Ip2ECZ5qliZA2DNJBmx4cS0Rz+/UKf/jaQyertMR5yoPGYl3NEmmVCGEp8avpbB48S8nxnn5QZkvBtUQmvy4fPenu8B55P/7QWiIjvOYSmkCzecWysEJFL4fvxS7BoLGO9hdBu4zzn5UypIjFKgkmZQ6KCUe0JNNS5uyLWTRnnUk51DpEiiRHOumLcoulxG1Fabkas3Ub2kcfJCDlDzpGfkHdzvEI3rYZs5OsGDqQRB3H5ZJyQVw66yr5glwK1YazIP3ZiSxVE/K99IbTyGIhH3LFTpzTRRT08MDJXhAexSQ2CjUe0rQEnZhKeC2Gn7RwQbWKiBZ+H0yQgoaQ6r7qBtxRgpUJbtwiM4mOJUIQwFZ84SkNxo7pplptD4hURmHNXz6k24qHSoacuQsJmYT8QPxrfPOrNI9jGgp4FEUBjIIIZwxbhN7zMdxjP8VuVP+wrK4w0RoqymS+G/WUl29162F+q73CXDob9eo2ry5VJlI3Zlv4DYT+vX1jmi/iLwjxTYb/nD5e6d+il/rDu3l5i/xW2qNH94UyiMh6x6d5x+6ZYfbJnWezFLL6/FFvak6yPbbp9RzdfZzqZ5FOlp/ga+FrWstvXbNo9OLh705rbGZ2ZmQDerI1iQXjZU+Gq8hqXouvFIiOX6cXSQX+44MaCahx6Rq8rAB/xh8tKPu4urRKJ9fnptoX+cHGZrrhml1cZ7rIy2VUj4Us94OqdgUk19gE9pe6Pl5Thk0jEl/sMdt6A9vwKso58nOwmR8kz5EXyjyiHRFCPMN6VivKrvN+eMNRszhPOWvDBq3BG8QU6HHpthPKoOJ4/l4UO8FFhokGOLcCJTAlqkIiCkVd8nRlxXRS1wmegrxF0g6Z0gWNVOMZftor2tXAjxwSv124Anmlbv7gBwG7wejlILOl6vp2XLtoVkvpCuxbxMr//e02Ndre1TZrb/FhiU1DaYH/B+Jj8lKu8QJmhnJnXUu75yXXfW+23bSLBE99ubLKue3V7fvCJsvzPq913h2B3odc19U2XtxB2h+6m3fzV7FlI77ND2U+4L9dtFBFc+q5apAo8XLSuTSvW3qV8DYv3BKUDi27l5X2dnX28qHFF0DyV2FwHH7JLaijRrV8rbU3wL6C2tFuWBCCVe+iINDt/fqzG/tfH6zZ/I798Kn/e1nVLA7aw7WyR3+32F51tWyhAN9ih4yo8V78ic740iCHkzkNmkVrxtSgvuufh3/sBuUVBcwcBmcf51GTe+KOAyGr/0hq3f47IO2QiAo9SAybQQx6yCHqz7xf3G0WnxydFoYLy+ArWZyY6vuicDOXow7U+fg9i9Gay8yo3TF9ITzRzKfyTFiKrVqsKsOb4qAcRYRNxEbtQ9FoB515A+ucfX3XxTVwvr/AFKJddrBoPYnsFp8X/Y8H0JaJnwhmiUmFxUWT+wBz0W02lurd3YcXAMjSLpMahx7bSQ4sPDfdMkcp4bK5e4S+m0hN9u+Z2bKuG5Kx5vYb5UstcHY0++nvJ5dKgsqFGt/Zpje3zy1XGwxBXXT3IybP+6K3N9auXtBeiNhr6ghPr5XsX3fb0Pb0q1C69Z+UqIFpJZZlvTrQ1MJmOt3R+ZoX8PaN33qy0u/P2sLuqwq3Kqgre+d03zq4aWNLj+T/fDhNueJxjYGRgYGC+OWvnOlOReH6brwzcHAwgsP/vwQYQfevfs+L//xkYOBjB4pwMTCAKAJFzDSIAAAB4nGNgZGDgYAABDob/QMDByMDIgAqYAFyDBBoAeJxj1WJYxsHAIMzAwPCZgY0BBJgZGhhgIBgIGRiXMjGD2SBwGSTLGMwIJP//h6lkhJEgPliM8TLjFQiPEagDKCPMBDMTZroqAwMAoLQOcQAAAAAWACoAQgBkAVQBeAG6AdACJAKeAvoDUAN0A44DqAQGBBwEMgR0BKIE8AVABVYFsAYWBjYGeAbYBygHWgeSB6AHxggKCGAItAj0CVAJogn4CiAKtArQCuwLJAtkC+gL/gwUDMYNNA1uDbAOJg5ADowOyBAMEUgRlBJGeJxjYGRgYLBleMPAwwACjGCSC4QZI0FMACFWAaYAAAB4nLVUzYocNxCu2Rl7NzheQiDgow4mrJehx15j7NinxT8n72VtFnwJaLo13cI9LSGp3XTIIUcf8hi5GPIUIYEcc/YT+JxTjqkqqWd2PROzCWSaUX8q1e9XpQaAG6OvYQTxdxf/EY/gS9xFvAO78DThMcqfJzxB/G3CV+BzsAlfhS/g+4R34Rv4MeE9+Ap+S/gaHML7hK+Pfh1NEt6Hw53fMcpo8hnuip0/Ex7BzfFZwjuwP/4u4THK3yY8QfxzwlfgxviPhK+CGH9IeBfcZC/hPTicDH6uwcvJTwlfH7+d/JXwPrzc++GXd+Lo9p374kTnznizCOKxcdY4GbRpMnFc1+JUl1Xw4lR55d6oInsm507n4uTJ8yNx7L0K/lSVbS3d5sGm5Ew5j57F3ezBvXhKh/HshSqNEtoLKYKThVpK91qYhQiVOpdf6UxrSZybpZWNVj7bmnwVgn04m3Vdly2H8wxtZqG3pnTSVv1sYZrgZ2tz31pba1UIOsjEK9OKpexF6xUmgYmRWAQjcqdkUFNRaG9r2U+FbAphncbTHFUUvqUXVrmlDgHdzXsuota5asgXHnhh3AAWFGG6Wap1pmjzMBXEPNpOyWYIoBvRVTqvzmXWYVDd5HVbYJtW2Zum7sWBviXUco65rNXRw6eyZfVCN6VwygfsFLG6DkDmK1+PmIEDjVGCWlILnMaohema2sjiInsyUqUclWMwFK5tsG0QhaIySadStb3IKA5j0yd1agg6RH4qPdeYc3b5bsM7EHAEt+EO3Ed0AhpycGDA438BAWWPETm887RKlGhEDWR4cgw1PgJOUVZChWeedwrfCrXf4Fqg5jO0m+OefFOMJ/hlOWJ7z5pkR1YltOhPouZlLC6jc8Z5+JSzwC9dBg/g3gXbwfK83QvOxuAqUIeqkvgPzECB0iVn+RplxBKdVKy7jb+S9y0yOGjn+F7iXmJOmtnK/gXzxHNA6UOY4dPxk6G/j+2zFGeGuGcvJfux6KFH6YK9UbWzrdE952yxI5r7KFYW1PtXXJNgJnp8t8xdZCIyNmiTzHDVDjWoDgVT3BesZ7njPUuID4pjuTPRNk9eVNpL9m25r1Rz4DOymnMeQydqroishryihecuuA3JYlXD9FJdtbwv0CbH/ZT5ijMf405XcT6uQPMkdsxTjut2zrpUKWnnWE3Lc1ds5Z5sakYHqH8L3zSh88TLNu8xh//K7dp7wZ5KlDme45Du1DCr2yoYom/m9ejcDFAlsZbA8YZbQP5jrQVKOq7c8K381OzJC1OluC8mrbGqiFu+WS1bUrZDNwc/pFnzTf7nGY1fxiZ1Zu19uCE6sUzzQ/nOmenY2//hbv8NhsQ4mnicY2BmAIP/fgzlDJjAFgApZwIFeJzbwKDNsImRk0mbcRMXiNzO1ZobaqvKwKG9nTs12EFPBsTiifCw0JAEsXidzbXlhUEsPh0VGREeEItfTkKYjwPEEuDj4WRnAbEEwQDEEtowoSDAAMhi2M4IN5oJbjQz3GgWuNGscKPZ5CShRrPDjeaAG80JN3qTMCO79gYGBdfaTAkXAMQBKBoAAAA=') 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"; } -@mixin ms-Icon--ChromeRestore { content: "\E923"; } -@mixin ms-Icon--ChromeMinimize { content: "\E921"; } -@mixin ms-Icon--System { content: "\E770"; } -@mixin ms-Icon--SquareShape { content: "\F1A6"; } - -// 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 } -.ms-Icon--ChromeRestore:before { @include ms-Icon--ChromeRestore } -.ms-Icon--ChromeMinimize:before { @include ms-Icon--ChromeMinimize } -.ms-Icon--System:before { @include ms-Icon--System } -.ms-Icon--SquareShape:before { @include ms-Icon--SquareShape } +/* + 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--Combine { content: "\EDBB"; } +@mixin ms-Icon--TextField { content: "\EDC3"; } +@mixin ms-Icon--SortUp { content: "\EE68"; } +@mixin ms-Icon--SortDown { content: "\EE69"; } +@mixin ms-Icon--OpenFolderHorizontal { content: "\ED25"; } +@mixin ms-Icon--Table { content: "\ED86"; } +@mixin ms-Icon--DocumentManagement { content: "\EFFC"; } +@mixin ms-Icon--TextDocument { content: "\F029"; } +@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--AddTo { content: "\ECC8"; } +@mixin ms-Icon--Hide3 { content: "\F6AC"; } +@mixin ms-Icon--WarningSolid { content: "\F736"; } +@mixin ms-Icon--BranchMerge { content: "\F295"; } +@mixin ms-Icon--StatusCircleCheckmark { content: "\F13E"; } +@mixin ms-Icon--AlertSolid { content: "\F331"; } +@mixin ms-Icon--Plug { content: "\F300"; } +@mixin ms-Icon--PlugConnected { content: "\F302"; } +@mixin ms-Icon--ChevronDown { content: "\E70D"; } +@mixin ms-Icon--ChevronUp { content: "\E70E"; } +@mixin ms-Icon--Edit { content: "\E70F"; } +@mixin ms-Icon--Add { content: "\E710"; } +@mixin ms-Icon--Cancel { content: "\E711"; } +@mixin ms-Icon--More { content: "\E712"; } +@mixin ms-Icon--Link { content: "\E71B"; } +@mixin ms-Icon--Settings { content: "\E713"; } +@mixin ms-Icon--ReceiptProcessing { content: "\E496"; } +@mixin ms-Icon--Filter { content: "\E71C"; } +@mixin ms-Icon--ZoomOut { content: "\E71F"; } +@mixin ms-Icon--CheckboxComposite { content: "\E73A"; } +@mixin ms-Icon--CheckMark { content: "\E73E"; } +@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--Search { content: "\E721"; } +@mixin ms-Icon--Refresh { content: "\E72C"; } +@mixin ms-Icon--Share { content: "\E72D"; } +@mixin ms-Icon--Download { content: "\E896"; } +@mixin ms-Icon--Help { content: "\E897"; } +@mixin ms-Icon--Rename { content: "\E8AC"; } +@mixin ms-Icon--ZoomIn { content: "\E8A3"; } +@mixin ms-Icon--View { content: "\E890"; } +@mixin ms-Icon--MapLayers { content: "\E81E"; } +@mixin ms-Icon--Tag { content: "\E8EC"; } +@mixin ms-Icon--Copy { content: "\E8C8"; } +@mixin ms-Icon--ChevronLeft { content: "\E76B"; } +@mixin ms-Icon--ChevronRight { content: "\E76C"; } +@mixin ms-Icon--Home { content: "\E80F"; } +@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"; } +@mixin ms-Icon--ChromeRestore { content: "\E923"; } +@mixin ms-Icon--ChromeMinimize { content: "\E921"; } +@mixin ms-Icon--System { content: "\E770"; } +@mixin ms-Icon--SquareShape { content: "\F1A6"; } + +// Classes +.ms-Icon--Combine:before { @include ms-Icon--Combine } +.ms-Icon--TextField:before { @include ms-Icon--TextField } +.ms-Icon--SortUp:before { @include ms-Icon--SortUp } +.ms-Icon--SortDown:before { @include ms-Icon--SortDown } +.ms-Icon--OpenFolderHorizontal:before { @include ms-Icon--OpenFolderHorizontal } +.ms-Icon--Table:before { @include ms-Icon--Table } +.ms-Icon--DocumentManagement:before { @include ms-Icon--DocumentManagement } +.ms-Icon--TextDocument:before { @include ms-Icon--TextDocument } +.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--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--StatusCircleCheckmark:before { @include ms-Icon--StatusCircleCheckmark } +.ms-Icon--AlertSolid:before { @include ms-Icon--AlertSolid } +.ms-Icon--Plug:before { @include ms-Icon--Plug } +.ms-Icon--PlugConnected:before { @include ms-Icon--PlugConnected } +.ms-Icon--ChevronDown:before { @include ms-Icon--ChevronDown } +.ms-Icon--ChevronUp:before { @include ms-Icon--ChevronUp } +.ms-Icon--Edit:before { @include ms-Icon--Edit } +.ms-Icon--Add:before { @include ms-Icon--Add } +.ms-Icon--Cancel:before { @include ms-Icon--Cancel } +.ms-Icon--More:before { @include ms-Icon--More } +.ms-Icon--Link:before { @include ms-Icon--Link } +.ms-Icon--Settings:before { @include ms-Icon--Settings } +.ms-Icon--ReceiptProcessing:before { @include ms-Icon--ReceiptProcessing } +.ms-Icon--Filter:before { @include ms-Icon--Filter } +.ms-Icon--ZoomOut:before { @include ms-Icon--ZoomOut } +.ms-Icon--CheckboxComposite:before { @include ms-Icon--CheckboxComposite } +.ms-Icon--CheckMark:before { @include ms-Icon--CheckMark } +.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--Search:before { @include ms-Icon--Search } +.ms-Icon--Refresh:before { @include ms-Icon--Refresh } +.ms-Icon--Share:before { @include ms-Icon--Share } +.ms-Icon--Download:before { @include ms-Icon--Download } +.ms-Icon--Help:before { @include ms-Icon--Help } +.ms-Icon--Rename:before { @include ms-Icon--Rename } +.ms-Icon--ZoomIn:before { @include ms-Icon--ZoomIn } +.ms-Icon--View:before { @include ms-Icon--View } +.ms-Icon--MapLayers:before { @include ms-Icon--MapLayers } +.ms-Icon--Tag:before { @include ms-Icon--Tag } +.ms-Icon--Copy:before { @include ms-Icon--Copy } +.ms-Icon--ChevronLeft:before { @include ms-Icon--ChevronLeft } +.ms-Icon--ChevronRight:before { @include ms-Icon--ChevronRight } +.ms-Icon--Home:before { @include ms-Icon--Home } +.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 } +.ms-Icon--ChromeRestore:before { @include ms-Icon--ChromeRestore } +.ms-Icon--ChromeMinimize:before { @include ms-Icon--ChromeMinimize } +.ms-Icon--System:before { @include ms-Icon--System } +.ms-Icon--SquareShape:before { @include ms-Icon--SquareShape } diff --git a/src/common/localization/en-us.ts b/src/common/localization/en-us.ts index 24edff1fd..d95fa5612 100644 --- a/src/common/localization/en-us.ts +++ b/src/common/localization/en-us.ts @@ -42,6 +42,7 @@ export const english: IAppStrings = { openCloudProject: { title: "Open Cloud Project", selectConnection: "Select a Connection", + pasteSharedUri: "Please paste shared project string here", }, recentProjects: "Recent Projects", deleteProject: { @@ -397,6 +398,7 @@ export const english: IAppStrings = { } }, farItems: { + share: "Share Project", zoom: { zoomOut: "Zoom out", zoomIn: "Zoom in", @@ -611,6 +613,17 @@ export const english: IAppStrings = { } }, + shareProject: { + errors: { + cannotDecodeString: "Cannot decode shared string! Please, check if your string has been modified.", + connectionNotFound: "Connection not found. Add shared project's connection to your connections.", + noConnections: "Connection is required for project sharing", + tokenNameExist: "Warning! You already have token with same name as in shared project. Please create a new token, and update the existing project which uses ''${sharedTokenName}'' with new token name." + }, + copy: { + success: "String for sharing your project has been saved to clipboard. In order to use it, paste it in appropriate section of the 'Open Cloud Project' popup.", + } + }, }; /*eslint-enable no-template-curly-in-string, no-multi-str*/ diff --git a/src/common/localization/es-cl.ts b/src/common/localization/es-cl.ts index 8cf2216f0..76eea45cf 100644 --- a/src/common/localization/es-cl.ts +++ b/src/common/localization/es-cl.ts @@ -43,6 +43,7 @@ export const spanish: IAppStrings = { openCloudProject: { title: "Abrir Proyecto de la Nube", selectConnection: "Select a Connection", + pasteSharedUri: "Pegue la cadena de proyecto compartida aquí", }, deleteProject: { title: "Borrar Proyecto", @@ -400,6 +401,7 @@ export const spanish: IAppStrings = { } }, farItems: { + share: "Compartir proyecto", zoom: { zoomOut: "Alejar", zoomIn: "Acercarse", @@ -613,6 +615,17 @@ export const spanish: IAppStrings = { message: "Error al enviar solicitud a Azure Blob Container. Problemas comunes: \n • SAS URI no válida \n • Cross-Origin Resource Sharing (CORS) no está configurado del lado del servidor \n • Error de red", } }, + shareProject: { + errors: { + cannotDecodeString: "¡No se puede decodificar la cadena compartida! Por favor, verifique si su cadena ha sido modificada.", + connectionNotFound: "Conexión no encontrada. Agregue la conexión del proyecto compartido a sus conexiones.", + noConnections: "Se requiere conexión para compartir proyectos", + tokenNameExist: "¡Advertencia! Ya tiene token con el mismo nombre que en el proyecto compartido. Cree un nuevo token y actualice el proyecto existente que usa ''${sharedTokenName}'' con el nuevo nombre del token.", + }, + copy: { + success: "La cadena para compartir su proyecto se ha guardado en el portapapeles. Para usarlo, péguelo en la sección correspondiente de la ventana emergente 'Open Cloud Project'.", + } + } }; /*eslint-enable no-template-curly-in-string, no-multi-str*/ diff --git a/src/common/strings.ts b/src/common/strings.ts index 3e0688335..ceb8d8c30 100644 --- a/src/common/strings.ts +++ b/src/common/strings.ts @@ -43,6 +43,7 @@ export interface IAppStrings { openCloudProject: { title: string; selectConnection: string; + pasteSharedUri: string; }, deleteProject: { title: string; @@ -393,6 +394,7 @@ export interface IAppStrings { }, }, farItems: { + share: string, zoom: { zoomOut: string, zoomIn: string, @@ -514,6 +516,17 @@ export interface IAppStrings { modelCountLimitExceeded: IErrorMetadata, requestSendError: IErrorMetadata, }; + shareProject: { + errors: { + cannotDecodeString: string, + connectionNotFound: string, + noConnections: string, + tokenNameExist: string, + }, + copy: { + success: string, + } + } } interface IErrorMetadata { diff --git a/src/common/utils.ts b/src/common/utils.ts index 516519823..79beae8a0 100644 --- a/src/common/utils.ts +++ b/src/common/utils.ts @@ -346,3 +346,14 @@ export function getNextColor(tags: ITag[]) { return tagColors[randomIntInRange(0, tagColors.length - 1)]; } + +export function getSasFolderString(sas:string): string { + return sas.substr(0, sas.indexOf("?")) +} + + +export function fixedEncodeURIComponent(str: string) { + return encodeURIComponent(str).replace(/[!'()*]/g, (c) => { + return '%' + c.charCodeAt(0).toString(16) + }) + } diff --git a/src/config/fabric-icons.json b/src/config/fabric-icons.json index 5d9a41ab7..87293ef19 100644 --- a/src/config/fabric-icons.json +++ b/src/config/fabric-icons.json @@ -6,6 +6,14 @@ "chunkSubsets": false, "hashFontFileName": true, "glyphs": [ + { + "name": "Combine", + "unicode": "EDBB" + }, + { + "name": "TextField", + "unicode": "EDC3" + }, { "name": "SortUp", "unicode": "EE68" @@ -15,29 +23,21 @@ "unicode": "EE69" }, { - "name": "Table", - "unicode": "ED86" + "name": "OpenFolderHorizontal", + "unicode": "ED25" }, { - "name": "TextField", - "unicode": "EDC3" + "name": "Table", + "unicode": "ED86" }, { - "name": "Combine", - "unicode": "EDBB" + "name": "DocumentManagement", + "unicode": "EFFC" }, { "name": "TextDocument", "unicode": "F029" }, - { - "name": "StatusCircleCheckmark", - "unicode": "F13E" - }, - { - "name": "DocumentManagement", - "unicode": "EFFC" - }, { "name": "CircleRing", "unicode": "EA3A" @@ -54,10 +54,6 @@ "name": "Documentation", "unicode": "EC17" }, - { - "name": "OpenFolderHorizontal", - "unicode": "ED25" - }, { "name": "AddTo", "unicode": "ECC8" @@ -75,36 +71,56 @@ "unicode": "F295" }, { - "name": "PlugConnected", - "unicode": "F302" + "name": "StatusCircleCheckmark", + "unicode": "F13E" + }, + { + "name": "AlertSolid", + "unicode": "F331" }, { "name": "Plug", "unicode": "F300" }, { - "name": "AlertSolid", - "unicode": "F331" + "name": "PlugConnected", + "unicode": "F302" }, { - "name": "Refresh", - "unicode": "E72C" + "name": "ChevronDown", + "unicode": "E70D" }, { - "name": "CheckboxComposite", - "unicode": "E73A" + "name": "ChevronUp", + "unicode": "E70E" + }, + { + "name": "Edit", + "unicode": "E70F" + }, + { + "name": "Add", + "unicode": "E710" + }, + { + "name": "Cancel", + "unicode": "E711" }, { "name": "More", "unicode": "E712" }, + { + "name": "Link", + "unicode": "E71B" + }, { "name": "Settings", "unicode": "E713" }, { - "name": "Link", - "unicode": "E71B" + "name": "ReceiptProcessing", + "unicode": "E496" }, { "name": "Filter", @@ -115,25 +131,13 @@ "unicode": "E71F" }, { - "name": "Search", - "unicode": "E721" + "name": "CheckboxComposite", + "unicode": "E73A" }, { "name": "CheckMark", "unicode": "E73E" }, - { - "name": "ChevronRight", - "unicode": "E76C" - }, - { - "name": "ChevronLeft", - "unicode": "E76B" - }, - { - "name": "Cancel", - "unicode": "E711" - }, { "name": "Up", "unicode": "E74A" @@ -151,61 +155,61 @@ "unicode": "E753" }, { - "name": "Add", - "unicode": "E710" + "name": "Search", + "unicode": "E721" }, { - "name": "ChevronUp", - "unicode": "E70E" + "name": "Refresh", + "unicode": "E72C" }, { - "name": "ReceiptProcessing", - "unicode": "E496" + "name": "Share", + "unicode": "E72D" }, { - "name": "ChevronDown", - "unicode": "E70D" + "name": "Download", + "unicode": "E896" }, { - "name": "Edit", - "unicode": "E70F" + "name": "Help", + "unicode": "E897" }, { - "name": "Copy", - "unicode": "E8C8" + "name": "Rename", + "unicode": "E8AC" }, { "name": "ZoomIn", "unicode": "E8A3" }, { - "name": "Rename", - "unicode": "E8AC" + "name": "View", + "unicode": "E890" + }, + { + "name": "MapLayers", + "unicode": "E81E" }, { "name": "Tag", "unicode": "E8EC" }, { - "name": "View", - "unicode": "E890" + "name": "Copy", + "unicode": "E8C8" }, { - "name": "Download", - "unicode": "E896" + "name": "ChevronLeft", + "unicode": "E76B" }, { - "name": "Help", - "unicode": "E897" + "name": "ChevronRight", + "unicode": "E76C" }, { "name": "Home", "unicode": "E80F" }, - { - "name": "MapLayers", - "unicode": "E81E" - }, { "name": "KeyPhraseExtraction", "unicode": "E395" diff --git a/src/react/components/common/cloudFilePicker/cloudFilePicker.scss b/src/react/components/common/cloudFilePicker/cloudFilePicker.scss new file mode 100644 index 000000000..a4306859c --- /dev/null +++ b/src/react/components/common/cloudFilePicker/cloudFilePicker.scss @@ -0,0 +1,18 @@ +.shared-string-input-container { + padding: 1rem; + .input-uri { + padding: 1rem; + .form-control{ + font-size: 90%; + } + } +} + +.separator { + margin: 0 1rem; + div{ + background-color: #32383e; + color: white; + font-weight: 600; + } +} diff --git a/src/react/components/common/cloudFilePicker/cloudFilePicker.tsx b/src/react/components/common/cloudFilePicker/cloudFilePicker.tsx index f63521740..3590c5174 100644 --- a/src/react/components/common/cloudFilePicker/cloudFilePicker.tsx +++ b/src/react/components/common/cloudFilePicker/cloudFilePicker.tsx @@ -3,11 +3,13 @@ import React from "react"; import { toast } from "react-toastify"; -import { Button, Modal, ModalBody, ModalFooter, ModalHeader } from "reactstrap"; +import { Button, Modal, ModalBody, ModalFooter, ModalHeader, InputGroup, Input } from "reactstrap"; import { strings, interpolate } from "../../../../common/strings"; -import { IConnection, StorageType, ErrorCode, AppError } from "../../../../models/applicationState"; +import { IConnection, StorageType, ErrorCode, AppError, ISecurityToken } from "../../../../models/applicationState"; import { StorageProviderFactory } from "../../../../providers/storage/storageProviderFactory"; import CondensedList, { ListItem } from "../condensedList/condensedList"; +import "./cloudFilePicker.scss" +import { Separator } from "@fluentui/react"; /** * Properties for Cloud File Picker @@ -18,12 +20,17 @@ import CondensedList, { ListItem } from "../condensedList/condensedList"; */ export interface ICloudFilePickerProps { connections: IConnection[]; - onSubmit: (content: string) => void; - + onSubmit: (content: string, token?: {}) => void; onCancel?: () => void; fileExtension?: string; } +interface ISharedStringData { + sasFolder: string; + token: ISecurityToken; + projectName: string; +} + /** * State for Cloud File Picker * @member isOpen - Cloud File Picker is open @@ -42,6 +49,10 @@ export interface ICloudFilePickerState { selectedFile: string; okDisabled: boolean; backDisabled: boolean; + pastedUri: string; + pasting: boolean; + sharedStringData: ISharedStringData; + haveCloudConnections: boolean; } /** @@ -50,7 +61,7 @@ export interface ICloudFilePickerState { */ export class CloudFilePicker extends React.Component { - constructor(props) { + constructor(props: Readonly) { super(props); this.open = this.open.bind(this); @@ -63,6 +74,8 @@ export class CloudFilePicker extends React.Component×; - return( + return ( {this.state.modalHeader} - - {this.state.condensedList} - + {!this.state.selectedConnection && + <> +
+
Shared Project String
+ {!this.state.haveCloudConnections && +
{strings.shareProject.errors.noConnections}
+ } + + + +
+ + } + {(!this.state.selectedConnection && !this.state.pastedUri) && or + } + {!this.state.pastedUri && + {this.state.condensedList} + } {this.state.selectedFile || ""} - {this.state.backDisabled ? - - : + disabled={this.state.okDisabled}>Ok + {this.state.backDisabled && !this.state.pastedUri ? + : } @@ -100,7 +132,7 @@ export class CloudFilePicker extends React.Component 0, }; } @@ -133,6 +170,52 @@ export class CloudFilePicker extends React.Component providerOptions["sas"].includes(sasFolder)); + if (connection) { + return connection; + } + toast.error(strings.shareProject.errors.connectionNotFound); + return null + } + + private getSharedUriParams(sharedString: string) { + try { + return JSON.parse(window.atob(sharedString)); + } catch (error) { + toast.error(strings.shareProject.errors.cannotDecodeString); + return null; + } + } + + private handlePasteUri(ev) { + this.setState({ pasting: true, pastedUri: ev.target.value }) + } + + private handleChangeUri(ev) { + if (this.state.pasting) { + this.setState({ pastedUri: ev.target.value, okDisabled: false }); } } @@ -140,6 +223,7 @@ export class CloudFilePicker extends React.Component { project: MockFactory.createTestProject(), lockedTags: [], hoveredLabel: null, + appSettings: null, }; const assetPreviewProps: IAssetPreviewProps = { diff --git a/src/react/components/pages/editorPage/canvas.tsx b/src/react/components/pages/editorPage/canvas.tsx index 3a053182e..3950b5310 100644 --- a/src/react/components/pages/editorPage/canvas.tsx +++ b/src/react/components/pages/editorPage/canvas.tsx @@ -9,7 +9,7 @@ import { EditorMode, IAssetMetadata, IProject, IRegion, RegionType, AssetType, ILabelData, ILabel, - ITag, IAsset, IFormRegion, FeatureCategory, FieldType, FieldFormat, + ITag, IAsset, IFormRegion, FeatureCategory, FieldType, FieldFormat, ISecurityToken, } from "../../../../models/applicationState"; import CanvasHelpers from "./canvasHelpers"; import { AssetPreview } from "../../common/assetPreview/assetPreview"; @@ -30,15 +30,20 @@ import Alert from "../../common/alert/alert"; import * as pdfjsLib from "pdfjs-dist"; import Polygon from "ol/geom/Polygon"; import HtmlFileReader from "../../../../common/htmlFileReader"; -import { parseTiffData, renderTiffToCanvas, loadImageToCanvas } from "../../../../common/utils"; +import { parseTiffData, renderTiffToCanvas, loadImageToCanvas, getSasFolderString, fixedEncodeURIComponent } from "../../../../common/utils"; import { constants } from "../../../../common/constants"; import { CanvasCommandBar } from "./canvasCommandBar"; import { TooltipHost, ITooltipHostStyles } from "@fluentui/react"; +import { IAppSettings } from '../../../../models/applicationState'; +import { toast } from "react-toastify"; +import { strings } from "../../../../common/strings"; + pdfjsLib.GlobalWorkerOptions.workerSrc = constants.pdfjsWorkerSrc(pdfjsLib.version); const cMapUrl = constants.pdfjsCMapUrl(pdfjsLib.version); export interface ICanvasProps extends React.Props { + appSettings: IAppSettings, selectedAsset: IAssetMetadata; editorMode: EditorMode; project: IProject; @@ -52,8 +57,9 @@ export interface ICanvasProps extends React.Props { onCanvasRendered?: (canvas: HTMLCanvasElement) => void; onRunningOCRStatusChanged?: (isRunning: boolean) => void; onTagChanged?: (oldTag: ITag, newTag: ITag) => void; + runOcrForAllDocs?: (runForAllDocs: boolean) => void; + shareProject?: () => void; onAssetDeleted?: () => void; - runOcrForAllDocs?: (runForAllDocs:boolean) => void; } export interface ICanvasState { @@ -106,6 +112,8 @@ export default class Canvas extends React.Component project: null, lockedTags: [], hoveredLabel: null, + appSettings: null, + shareProject: null, }; public state: ICanvasState = { @@ -217,6 +225,8 @@ export default class Canvas extends React.Component handleRunOcr={this.runOcr} handleAssetDeleted={this.props.onAssetDeleted} handleRunOcrForAllDocuments={this.runOcrForAllDocuments} + handleShareProject={this.shareProject} + connectionType={this.props.project.sourceConnection.providerType} /> this.imageMap = ref} @@ -295,6 +305,29 @@ export default class Canvas extends React.Component this.props.runOcrForAllDocs(true); } + // creates URI for sharing project + private shareProject = (): void => { + const project: IProject = this.props.project; + const sasFolder: string = getSasFolderString(project.sourceConnection.providerOptions["sas"]); + const projectToken: ISecurityToken = this.props.appSettings.securityTokens + .find((securityToken) => securityToken.name === project.securityToken); + const shareProjectString: string = JSON.stringify({ + sasFolder, + projectName: project.name, + token: { name: project.securityToken, key: projectToken.key } + }); + + this.copyToClipboard(shareProjectString) + } + + private async copyToClipboard(value: string) { + const clipboard = (navigator as any).clipboard; + if (clipboard && clipboard.writeText) { + await clipboard.writeText(btoa(value)); + toast.success(strings.shareProject.copy.success); + } + } + public updateSize() { this.imageMap.updateSize(); } diff --git a/src/react/components/pages/editorPage/canvasCommandBar.tsx b/src/react/components/pages/editorPage/canvasCommandBar.tsx index 6d5583429..509888994 100644 --- a/src/react/components/pages/editorPage/canvasCommandBar.tsx +++ b/src/react/components/pages/editorPage/canvasCommandBar.tsx @@ -11,11 +11,13 @@ interface ICanvasCommandBarProps { handleRunOcr: () => void; handleRunOcrForAllDocuments: () => void; handleLayerChange: (layer: string) => void; + handleShareProject: () => void; + connectionType: string; handleAssetDeleted?: () => void; layers: any; } -export const CanvasCommandBar: React.FunctionComponent = (props) => { +export const CanvasCommandBar: React.FunctionComponent = (props:ICanvasCommandBarProps) => { const dark: ICustomizations = { settings: { theme: getDarkGreyTheme(), @@ -95,6 +97,18 @@ export const CanvasCommandBar: React.FunctionComponent = iconProps: { iconName: "More" }, subMenuProps: { items: [ + { + key: "shareProject", + text: strings.editorPage.canvas.canvasCommandBar.farItems.share, + disabled: props.connectionType !== "azureBlobStorage", + iconProps: { iconName: "Share" }, + className: props.connectionType !== "azureBlobStorage" ? "disabled" : "", + onClick: () => props.handleShareProject(), + }, + { + key: 'divider_0', + itemType: ContextualMenuItemType.Divider, + }, { key: "runOcrForCurrentDocument", text: strings.editorPage.canvas.canvasCommandBar.farItems.additionalActions.subIMenuItems.runOcrOnCurrentDocument, diff --git a/src/react/components/pages/editorPage/editorPage.tsx b/src/react/components/pages/editorPage/editorPage.tsx index e52361786..4b18ae1f0 100644 --- a/src/react/components/pages/editorPage/editorPage.tsx +++ b/src/react/components/pages/editorPage/editorPage.tsx @@ -271,7 +271,9 @@ export default class EditorPage extends React.Component + runOcrForAllDocs={this.loadOcrForNotVisited} + appSettings={this.props.appSettings} + > this.loadSelectedProject(JSON.parse(content))} + onSubmit={(content, sharedToken?) => this.loadSelectedProject(JSON.parse(content), sharedToken)} fileExtension={constants.projectFileExtension} /> @@ -155,9 +155,11 @@ export default class HomePage extends React.Component { - await this.props.actions.loadProject(project); - this.props.history.push(`/projects/${project.id}/edit`); + private loadSelectedProject = async (project: IProject, sharedToken?: {}) => { + const loadedProject = await this.props.actions.loadProject(project, sharedToken); + if (loadedProject !== null) { + this.props.history.push(`/projects/${project.id}/edit`); + } } private freshLoadSelectedProject = async (project: IProject) => { diff --git a/src/react/components/pages/predict/predictPage.tsx b/src/react/components/pages/predict/predictPage.tsx index d8a7c4ce3..b05adff47 100644 --- a/src/react/components/pages/predict/predictPage.tsx +++ b/src/react/components/pages/predict/predictPage.tsx @@ -132,10 +132,11 @@ export default class PredictPage extends React.Component project.id === projectId); - await this.props.actions.loadProject(project); - this.props.appTitleActions.setTitle(project.name); + if (project) { + await this.props.actions.loadProject(project); + this.props.appTitleActions.setTitle(project.name); + } } - document.title = strings.predict.title + " - " + strings.appName; } diff --git a/src/redux/actions/projectActions.ts b/src/redux/actions/projectActions.ts index 9fd752e43..b8c685c4d 100644 --- a/src/redux/actions/projectActions.ts +++ b/src/redux/actions/projectActions.ts @@ -13,15 +13,19 @@ import { IAssetMetadata, IProject, ITag, + ISecurityToken, } from "../../models/applicationState"; import { createAction, createPayloadAction, IPayloadAction } from "./actionCreators"; import { appInfo } from "../../common/appInfo"; +import { saveAppSettingsAction } from "./applicationActions"; +import { toast } from 'react-toastify'; +import { strings, interpolate } from "../../common/strings"; /** * Actions to be performed in relation to projects */ export default interface IProjectActions { - loadProject(project: IProject): Promise; + loadProject(project: IProject, token?: {}): Promise; saveProject(project: IProject, saveTags?: boolean, updateTagsFromFiles?: boolean): Promise; deleteProject(project: IProject): Promise; closeProject(): void; @@ -39,15 +43,34 @@ export default interface IProjectActions { * Dispatches Load Project action and resolves with IProject * @param project - Project to load */ -export function loadProject(project: IProject): +export function loadProject(project: IProject, sharedToken?: ISecurityToken): (dispatch: Dispatch, getState: () => IApplicationState) => Promise { return async (dispatch: Dispatch, getState: () => IApplicationState) => { const appState = getState(); const projectService = new ProjectService(); + let projectToken: ISecurityToken; // Lookup security token used to decrypt project settings - const projectToken = appState.appSettings.securityTokens - .find((securityToken) => securityToken.name === project.securityToken); + if (sharedToken) { + projectToken = sharedToken; + const existingToken = appState.appSettings.securityTokens.find((token) => token.name === projectToken.name); + + if (!existingToken) { + // if we do not have project sharedToken, we need update security tokens in appState + dispatch(saveAppSettingsAction({ + securityTokens: [ + ...appState.appSettings.securityTokens, + sharedToken + ] + })); + } else if (existingToken.key !== sharedToken.key) { + const reason = interpolate(strings.shareProject.errors.tokenNameExist, {sharedTokenName: sharedToken.name}) + toast.error(reason, { autoClose: false, closeOnClick: false }); + return null; + } + } else { + projectToken = appState.appSettings.securityTokens.find((token) => token.name === project.securityToken); + } if (!projectToken) { throw new AppError(ErrorCode.SecurityTokenNotFound, "Security Token Not Found"); diff --git a/src/registerIcons.ts b/src/registerIcons.ts index 85b31e644..8a66d30cf 100644 --- a/src/registerIcons.ts +++ b/src/registerIcons.ts @@ -22,7 +22,6 @@ export function registerIcons() { Settings: "\uE713", Link: "\uE71B", Search: "\uE721", - Refresh: "\uE72C", CheckMark: "\uE73E", Up: "\uE74A", Down: "\uE74B", @@ -61,10 +60,12 @@ export function registerIcons() { MapLayers: "\uE81E", BookAnswers: "\uF8A4", Cancel: "\uE711", + Refresh: "\uE72C", Documentation: "\uEC17", More: "\uE712", ReceiptProcessing: "\uE496", KeyPhraseExtraction: "\uE395", + Share: "\uE72D", ChromeRestore: "\uE923", ChromeMinimize: "\uE921", System: "\uE770", diff --git a/yarn.lock b/yarn.lock index db3e34ac6..7ebbfbf96 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9259,6 +9259,11 @@ peek-readable@^3.1.0: resolved "https://registry.yarnpkg.com/peek-readable/-/peek-readable-3.1.0.tgz#250b08b7de09db8573d7fd8ea475215bbff14348" integrity sha512-KGuODSTV6hcgdZvDrIDBUkN0utcAVj1LL7FfGbM0viKTtCHmtZcuEJ+lGqsp0fTFkGqesdtemV2yUSMeyy3ddA== +peek-readable@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/peek-readable/-/peek-readable-3.1.0.tgz#250b08b7de09db8573d7fd8ea475215bbff14348" + integrity sha512-KGuODSTV6hcgdZvDrIDBUkN0utcAVj1LL7FfGbM0viKTtCHmtZcuEJ+lGqsp0fTFkGqesdtemV2yUSMeyy3ddA== + pend@~1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/pend/-/pend-1.2.0.tgz#7a57eb550a6783f9115331fcf4663d5c8e007a50"