From aece59ecdd46942396f6b09cd8ebc6f3419b4461 Mon Sep 17 00:00:00 2001 From: Dawid Poliszak Date: Thu, 24 Oct 2024 11:57:17 +0200 Subject: [PATCH] [Nu-1778] provide activities panel (#6979) * NU-1778 provide activities panel --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Dzuming <9945753+Dzuming@users.noreply.github.com> --- .../fixtures/sampleActivitiesResponse.ts | 301 +++++ .../fixtures/sampleMetadataResponse.ts | 135 +++ ...ctivities should display activities #0.png | Bin 0 -> 22500 bytes ...ctivities should display activities #1.png | Bin 0 -> 29583 bytes ...ctivities should display activities #2.png | Bin 0 -> 37338 bytes ...ctivities should display activities #3.png | Bin 0 -> 33791 bytes ...isplay used fragment graph in modal #4.png | Bin 60431 -> 57512 bytes ...isplay used fragment graph in modal #8.png | Bin 61782 -> 71431 bytes ...hould work with 'last deployed' tag #0.png | Bin 3070 -> 0 bytes ...hould work with 'last deployed' tag #1.png | Bin 3070 -> 0 bytes designer/client/cypress/e2e/activities.cy.ts | 106 ++ designer/client/cypress/e2e/fragment.cy.ts | 4 +- designer/client/cypress/e2e/process.cy.ts | 8 +- designer/client/cypress/e2e/undo.cy.ts | 12 - designer/client/cypress/support/process.ts | 50 +- designer/client/package-lock.json | 60 + designer/client/package.json | 5 +- designer/client/src/actions/actionTypes.ts | 2 + .../src/actions/nk/scenarioActivities.ts | 43 + designer/client/src/actions/reduxTypes.ts | 3 + .../src/components/comment/CommentContent.tsx | 68 +- .../components/modals/AddAttachmentDialog.tsx | 66 ++ .../components/modals/AddCommentDialog.tsx | 55 + .../modals/CompareVersionsDialog.tsx | 64 +- .../modals/GenerateTestDataDialog.tsx | 2 +- .../components/modals/SaveProcessDialog.tsx | 2 + .../processAttach/AddAttachment.tsx | 20 +- .../AddAttachment_deprecated.tsx | 40 + .../components/processAttach/AttachmentEl.tsx | 32 +- .../processAttach/AttachmentEl_deprecated.tsx | 27 + .../processAttach/ProcessAttachments.tsx | 4 +- .../src/components/themed/InputWithIcon.tsx | 3 +- .../toolbarSettings/TOOLBAR_COMPONENTS_MAP.ts | 2 + .../toolbars/activities/ActivitiesPanel.tsx | 181 +++ .../activities/ActivitiesPanelFooter.tsx | 50 + .../activities/ActivitiesPanelRow.tsx | 72 ++ .../toolbars/activities/ActivitiesSearch.tsx | 66 ++ .../ActivityPanelRowItem/ActivityItem.tsx | 106 ++ .../ActivityItemHeader.tsx | 223 ++++ .../ActivityPanelRowItem/DateItem.tsx | 24 + .../ActivityPanelRowItem/ToggleButtonItem.tsx | 33 + .../activities/ActivityPanelRowItem/index.ts | 3 + .../activities/helpers/activityItemColors.ts | 69 ++ .../toolbars/activities/helpers/date.ts | 19 + .../extendActivitiesWithUIData.test.ts | 1040 +++++++++++++++++ .../helpers/extendActivitiesWithUIData.ts | 141 +++ .../helpers/handleToggleActivities.ts | 43 + .../helpers/mergeActivityDataWithMetadata.ts | 16 + .../components/toolbars/activities/index.ts | 1 + .../components/toolbars/activities/types.ts | 79 ++ .../activities/useActivitiesSearch.test.ts | 126 ++ .../activities/useActivitiesSearch.ts | 177 +++ .../toolbars/creator/SearchHighlighter.tsx | 11 +- .../process/buttons/CompareButton.tsx | 12 +- .../process/buttons/MigrateButton.tsx | 10 +- .../process/buttons/UnArchiveButton.tsx | 4 +- .../process/buttons/useArchiveHelper.ts | 4 +- .../test/buttons/AdhocTestingButton.tsx | 2 +- .../client/src/containers/Notifications.tsx | 2 + designer/client/src/http/HttpService.ts | 47 +- .../client/src/reducers/processActivity.ts | 15 + .../src/reducers/selectors/activities.ts | 13 + .../client/src/stylesheets/SelectStyled.ts | 6 +- .../src/windowManager/ContentGetter.tsx | 9 + .../client/src/windowManager/WindowKind.tsx | 2 + .../client/test/CompareVersionDialog-test.tsx | 2 + .../main/resources/defaultDesignerConfig.conf | 4 +- .../activities/{canceled.svg => cancel.svg} | 0 .../scenariotoolbar/ToolbarPanelConfig.scala | 1 + .../ui/process/test/ScenarioTestService.scala | 10 +- docs/Changelog.md | 1 + docs/MigrationGuide.md | 3 + ...gResultAwareTypeInformationDetection.scala | 6 +- .../SerializerWithSpecifiedClass.scala | 1 + 74 files changed, 3627 insertions(+), 121 deletions(-) create mode 100644 designer/client/__mocks__/fixtures/sampleActivitiesResponse.ts create mode 100644 designer/client/__mocks__/fixtures/sampleMetadataResponse.ts create mode 100644 designer/client/cypress/e2e/__image_snapshots__/electron/Linux/Activities should display activities #0.png create mode 100644 designer/client/cypress/e2e/__image_snapshots__/electron/Linux/Activities should display activities #1.png create mode 100644 designer/client/cypress/e2e/__image_snapshots__/electron/Linux/Activities should display activities #2.png create mode 100644 designer/client/cypress/e2e/__image_snapshots__/electron/Linux/Activities should display activities #3.png delete mode 100644 designer/client/cypress/e2e/__image_snapshots__/electron/Linux/UndoRedo should work with 'last deployed' tag #0.png delete mode 100644 designer/client/cypress/e2e/__image_snapshots__/electron/Linux/UndoRedo should work with 'last deployed' tag #1.png create mode 100644 designer/client/cypress/e2e/activities.cy.ts create mode 100644 designer/client/src/actions/nk/scenarioActivities.ts create mode 100644 designer/client/src/components/modals/AddAttachmentDialog.tsx create mode 100644 designer/client/src/components/modals/AddCommentDialog.tsx create mode 100644 designer/client/src/components/processAttach/AddAttachment_deprecated.tsx create mode 100644 designer/client/src/components/processAttach/AttachmentEl_deprecated.tsx create mode 100644 designer/client/src/components/toolbars/activities/ActivitiesPanel.tsx create mode 100644 designer/client/src/components/toolbars/activities/ActivitiesPanelFooter.tsx create mode 100644 designer/client/src/components/toolbars/activities/ActivitiesPanelRow.tsx create mode 100644 designer/client/src/components/toolbars/activities/ActivitiesSearch.tsx create mode 100644 designer/client/src/components/toolbars/activities/ActivityPanelRowItem/ActivityItem.tsx create mode 100644 designer/client/src/components/toolbars/activities/ActivityPanelRowItem/ActivityItemHeader.tsx create mode 100644 designer/client/src/components/toolbars/activities/ActivityPanelRowItem/DateItem.tsx create mode 100644 designer/client/src/components/toolbars/activities/ActivityPanelRowItem/ToggleButtonItem.tsx create mode 100644 designer/client/src/components/toolbars/activities/ActivityPanelRowItem/index.ts create mode 100644 designer/client/src/components/toolbars/activities/helpers/activityItemColors.ts create mode 100644 designer/client/src/components/toolbars/activities/helpers/date.ts create mode 100644 designer/client/src/components/toolbars/activities/helpers/extendActivitiesWithUIData.test.ts create mode 100644 designer/client/src/components/toolbars/activities/helpers/extendActivitiesWithUIData.ts create mode 100644 designer/client/src/components/toolbars/activities/helpers/handleToggleActivities.ts create mode 100644 designer/client/src/components/toolbars/activities/helpers/mergeActivityDataWithMetadata.ts create mode 100644 designer/client/src/components/toolbars/activities/index.ts create mode 100644 designer/client/src/components/toolbars/activities/types.ts create mode 100644 designer/client/src/components/toolbars/activities/useActivitiesSearch.test.ts create mode 100644 designer/client/src/components/toolbars/activities/useActivitiesSearch.ts create mode 100644 designer/client/src/reducers/selectors/activities.ts rename designer/server/src/main/resources/web/static/assets/activities/{canceled.svg => cancel.svg} (100%) diff --git a/designer/client/__mocks__/fixtures/sampleActivitiesResponse.ts b/designer/client/__mocks__/fixtures/sampleActivitiesResponse.ts new file mode 100644 index 00000000000..da5737f80a5 --- /dev/null +++ b/designer/client/__mocks__/fixtures/sampleActivitiesResponse.ts @@ -0,0 +1,301 @@ +import { ActivitiesResponse } from "../../src/components/toolbars/activities/types"; + +export const sampleActivitiesResponse: ActivitiesResponse["activities"] = [ + { + id: "48f383f9-ccdd-46b6-9b33-5f6693165755", + user: "admin", + date: "2022-01-22T06:09:44.313094Z", + scenarioVersionId: 1, + comment: null, + attachment: { + file: { + id: 1, + status: "AVAILABLE", + }, + filename: "324.log", + lastModifiedBy: "admin", + lastModifiedAt: "2022-01-22T06:09:44.313094Z", + }, + additionalFields: [], + overrideIcon: null, + overrideDisplayableName: null, + overrideSupportedActions: null, + type: "ATTACHMENT_ADDED", + }, + { + id: "a2576467-9bf9-4a92-b71f-be95b84d59f6", + user: "admin", + date: "2024-09-22T09:53:40.875721Z", + scenarioVersionId: 3, + comment: { + content: { + value: "tests save", + status: "AVAILABLE", + }, + lastModifiedBy: "admin", + lastModifiedAt: "2024-09-22T09:53:40.875721Z", + }, + attachment: null, + additionalFields: [], + overrideIcon: null, + overrideDisplayableName: "Version 3 saved", + overrideSupportedActions: null, + type: "SCENARIO_MODIFIED", + }, + { + id: "15c0e8a9-d1c5-47dd-bf28-8a08217fff5b", + user: "admin", + date: "2024-09-25T09:55:04.309Z", + scenarioVersionId: 4, + comment: { + content: { + value: "122", + status: "AVAILABLE", + }, + lastModifiedBy: "admin", + lastModifiedAt: "2024-09-25T09:55:04.309Z", + }, + attachment: null, + additionalFields: [], + overrideIcon: null, + overrideDisplayableName: "Version 4 saved", + overrideSupportedActions: null, + type: "SCENARIO_MODIFIED", + }, + { + id: "ec245dc5-f84c-4880-8727-b8c999c35a3f", + user: "admin", + date: "2024-09-26T10:26:28.293423Z", + scenarioVersionId: 6, + comment: null, + attachment: null, + additionalFields: [], + overrideIcon: null, + overrideDisplayableName: null, + overrideSupportedActions: null, + type: "SCENARIO_ARCHIVED", + }, + { + id: "1ba9d6c6-c61a-42f5-8311-4d0f287867f7", + user: "admin", + date: "2024-09-26T10:26:42.999982Z", + scenarioVersionId: 6, + comment: null, + attachment: null, + additionalFields: [], + overrideIcon: null, + overrideDisplayableName: null, + overrideSupportedActions: null, + type: "SCENARIO_UNARCHIVED", + }, +]; + +export const sampleActivitiesWithRepetiveResponse: ActivitiesResponse["activities"] = [ + { + id: "56a7dd49-778b-468b-8e33-99bd176218aa", + user: "admin", + date: "2024-09-25T06:09:03.470213Z", + scenarioVersionId: 1, + comment: { + content: { + value: "test", + status: "AVAILABLE", + }, + lastModifiedBy: "admin", + lastModifiedAt: "2024-09-25T06:09:03.470213Z", + }, + attachment: null, + additionalFields: [], + overrideIcon: null, + overrideDisplayableName: null, + overrideSupportedActions: null, + type: "COMMENT_ADDED", + }, + { + id: "48f383f9-ccdd-46b6-9b33-5f6693165755", + user: "admin", + date: "2024-09-25T06:09:44.313094Z", + scenarioVersionId: 1, + comment: null, + attachment: { + file: { + id: 1, + status: "AVAILABLE", + }, + filename: "324.log", + lastModifiedBy: "admin", + lastModifiedAt: "2024-09-25T06:09:44.313094Z", + }, + additionalFields: [], + overrideIcon: null, + overrideDisplayableName: null, + overrideSupportedActions: null, + type: "ATTACHMENT_ADDED", + }, + { + id: "a2576467-9bf9-4a92-b71f-be95b84d59f6", + user: "admin", + date: "2024-09-25T09:53:40.875721Z", + scenarioVersionId: 3, + comment: { + content: { + value: "tests save", + status: "AVAILABLE", + }, + lastModifiedBy: "admin", + lastModifiedAt: "2024-09-25T09:53:40.875721Z", + }, + attachment: null, + additionalFields: [], + overrideIcon: null, + overrideDisplayableName: "Version 3 saved", + overrideSupportedActions: null, + type: "SCENARIO_MODIFIED", + }, + { + id: "15c0e8a9-d1c5-47dd-bf28-8a08217fff5b", + user: "admin", + date: "2024-09-25T09:55:04.309Z", + scenarioVersionId: 4, + comment: { + content: { + value: "122", + status: "AVAILABLE", + }, + lastModifiedBy: "admin", + lastModifiedAt: "2024-09-25T09:55:04.309Z", + }, + attachment: null, + additionalFields: [], + overrideIcon: null, + overrideDisplayableName: "Version 4 saved", + overrideSupportedActions: null, + type: "SCENARIO_MODIFIED", + }, + { + id: "0182764d-c568-403e-88ea-24942a091af5", + user: "admin", + date: "2024-09-26T07:17:19.892192Z", + scenarioVersionId: 5, + comment: { + content: { + value: "test", + status: "AVAILABLE", + }, + lastModifiedBy: "admin", + lastModifiedAt: "2024-09-26T07:17:19.892192Z", + }, + attachment: null, + additionalFields: [], + overrideIcon: null, + overrideDisplayableName: "Version 5 saved", + overrideSupportedActions: null, + type: "SCENARIO_MODIFIED", + }, + { + id: "2cf1f252-3be1-413f-ad62-ccb311683744", + user: "admin", + date: "2024-09-26T10:08:00.895385Z", + scenarioVersionId: 6, + comment: { + content: { + value: "test12345", + status: "AVAILABLE", + }, + lastModifiedBy: "admin", + lastModifiedAt: "2024-09-26T10:08:00.895385Z", + }, + attachment: null, + additionalFields: [], + overrideIcon: null, + overrideDisplayableName: "Version 6 saved", + overrideSupportedActions: null, + type: "SCENARIO_MODIFIED", + }, + { + id: "c0745a74-937c-4682-b9c8-cb94f619eb14", + user: "admin", + date: "2024-09-26T10:11:29.657265Z", + scenarioVersionId: 6, + comment: { + content: { + value: "Scenario migrated from local by admin", + status: "AVAILABLE", + }, + lastModifiedBy: "admin", + lastModifiedAt: "2024-09-26T10:11:29.657265Z", + }, + attachment: null, + additionalFields: [], + overrideIcon: null, + overrideDisplayableName: "Version 6 saved", + overrideSupportedActions: null, + type: "SCENARIO_MODIFIED", + }, + { + id: "35fa34fe-7f07-4b50-98fb-58d19d5b7704", + user: "admin", + date: "2024-09-26T10:13:11.571064Z", + scenarioVersionId: 6, + comment: { + content: { + value: "Scenario migrated from local by admin", + status: "AVAILABLE", + }, + lastModifiedBy: "admin", + lastModifiedAt: "2024-09-26T10:13:11.571064Z", + }, + attachment: null, + additionalFields: [], + overrideIcon: null, + overrideDisplayableName: "Version 6 saved", + overrideSupportedActions: null, + type: "SCENARIO_MODIFIED", + }, + { + id: "4e8c9f40-7c2c-434e-8528-68dccf66d5d1", + user: "admin", + date: "2024-09-26T10:22:45.494475Z", + scenarioVersionId: 6, + comment: { + content: { + value: "Scenario migrated from local by admin", + status: "AVAILABLE", + }, + lastModifiedBy: "admin", + lastModifiedAt: "2024-09-26T10:22:45.494475Z", + }, + attachment: null, + additionalFields: [], + overrideIcon: null, + overrideDisplayableName: "Version 6 saved", + overrideSupportedActions: null, + type: "SCENARIO_MODIFIED", + }, + { + id: "ec245dc5-f84c-4880-8727-b8c999c35a3f", + user: "admin", + date: "2024-09-26T10:26:28.293423Z", + scenarioVersionId: 6, + comment: null, + attachment: null, + additionalFields: [], + overrideIcon: null, + overrideDisplayableName: null, + overrideSupportedActions: null, + type: "SCENARIO_ARCHIVED", + }, + { + id: "1ba9d6c6-c61a-42f5-8311-4d0f287867f7", + user: "admin", + date: "2024-09-26T10:26:42.999982Z", + scenarioVersionId: 6, + comment: null, + attachment: null, + additionalFields: [], + overrideIcon: null, + overrideDisplayableName: null, + overrideSupportedActions: null, + type: "SCENARIO_UNARCHIVED", + }, +]; diff --git a/designer/client/__mocks__/fixtures/sampleMetadataResponse.ts b/designer/client/__mocks__/fixtures/sampleMetadataResponse.ts new file mode 100644 index 00000000000..72ddca28c2b --- /dev/null +++ b/designer/client/__mocks__/fixtures/sampleMetadataResponse.ts @@ -0,0 +1,135 @@ +import { ActivityMetadataResponse } from "src/components/toolbars/activities/types"; + +export const sampleMetadataResponse: ActivityMetadataResponse = { + activities: [ + { + type: "SCENARIO_CREATED", + displayableName: "Scenario created", + icon: "/assets/states/success.svg", + supportedActions: [], + }, + { + type: "SCENARIO_ARCHIVED", + displayableName: "Scenario archived", + icon: "/assets/process/archived.svg", + supportedActions: [], + }, + { + type: "SCENARIO_UNARCHIVED", + displayableName: "Scenario unarchived", + icon: "/assets/process/success.svg", + supportedActions: [], + }, + { + type: "SCENARIO_DEPLOYED", + displayableName: "Deployment", + icon: "/assets/states/deploy.svg", + supportedActions: ["delete_comment", "edit_comment"], + }, + { + type: "SCENARIO_PAUSED", + displayableName: "Pause", + icon: "/assets/states/stopping.svg", + supportedActions: ["delete_comment", "edit_comment"], + }, + { + type: "SCENARIO_CANCELED", + displayableName: "Cancel", + icon: "/assets/states/stopping.svg", + supportedActions: ["delete_comment", "edit_comment"], + }, + { + type: "SCENARIO_MODIFIED", + displayableName: "New version saved", + icon: "/assets/states/success.svg", + supportedActions: ["delete_comment", "edit_comment", "compare"], + }, + { + type: "SCENARIO_NAME_CHANGED", + displayableName: "Scenario name changed", + icon: "/assets/states/success.svg", + supportedActions: [], + }, + { + type: "COMMENT_ADDED", + displayableName: "Comment", + icon: "/assets/states/success.svg", + supportedActions: ["delete_comment", "edit_comment"], + }, + { + type: "ATTACHMENT_ADDED", + displayableName: "Attachment", + icon: "/assets/states/success.svg", + supportedActions: ["download_attachment", "delete_attachment"], + }, + { + type: "CHANGED_PROCESSING_MODE", + displayableName: "Processing mode change", + icon: "/assets/states/success.svg", + supportedActions: [], + }, + { + type: "INCOMING_MIGRATION", + displayableName: "Incoming migration", + icon: "/assets/states/success.svg", + supportedActions: ["compare"], + }, + { + type: "OUTGOING_MIGRATION", + displayableName: "Outgoing migration", + icon: "/assets/states/success.svg", + supportedActions: ["delete_comment", "edit_comment"], + }, + { + type: "PERFORMED_SINGLE_EXECUTION", + displayableName: "Processing data", + icon: "/assets/states/success.svg", + supportedActions: ["delete_comment", "edit_comment"], + }, + { + type: "PERFORMED_SCHEDULED_EXECUTION", + displayableName: "Processing data", + icon: "/assets/states/success.svg", + supportedActions: [], + }, + { + type: "AUTOMATIC_UPDATE", + displayableName: "Automatic update", + icon: "/assets/states/success.svg", + supportedActions: ["compare"], + }, + { + type: "CUSTOM_ACTION", + displayableName: "Custom action", + icon: "/assets/states/success.svg", + supportedActions: [], + }, + ], + actions: [ + { + id: "compare", + displayableName: "Compare", + icon: "/assets/states/error.svg", + }, + { + id: "delete_comment", + displayableName: "Delete", + icon: "/assets/states/error.svg", + }, + { + id: "edit_comment", + displayableName: "Edit", + icon: "/assets/states/error.svg", + }, + { + id: "download_attachment", + displayableName: "Download", + icon: "/assets/states/error.svg", + }, + { + id: "delete_attachment", + displayableName: "Delete", + icon: "/assets/states/error.svg", + }, + ], +}; diff --git a/designer/client/cypress/e2e/__image_snapshots__/electron/Linux/Activities should display activities #0.png b/designer/client/cypress/e2e/__image_snapshots__/electron/Linux/Activities should display activities #0.png new file mode 100644 index 0000000000000000000000000000000000000000..1b60fe9ab59aa46975887a10327b015f375c9613 GIT binary patch literal 22500 zcmd43WmuhCmNlA$6Ceb4C%C(N@ZfI20t6?xYl6E3cY?dSgy3$$JvhPL4_+)DT(T-J!55HVPFMk!9Vq2Nl_scm*@NKuRS7PK3zY} zI3CR3m>Sn{*K=>M^`>$vlaWNf+lUv9MO=)Nk`#l%_$rb^5+{;Hyv?04Y6)-XRB?9W zalL>0RDO3id$rx1lAKhw(0X-Nc)tHs6M-yzk3dIGj*fv5ogOWwt4l~D412;^sieAZ z`RU~J{KxV#A_lP^)0dP`T5v}Hg#{kMpT}`US+=i}<1nmcaR;{!+_~-6=yFR+RNS17 zWCo4H717_llhR+G<|iZ1|M^|^b(52&?>F%abF$nVeJ-~(x;w|T$xIV0EAacIzCIvofO^s{cG2q#Lf zV;)-XOWR4|mTE1JMx&WfUtDMNQ`68GT>2QHV`7Xq=tGGkdWMx_mjBubuEyuL#q7MZ z{@%C$a&XKstx7JPdvxqeWQF^aqEu}|8?pW>3OW{+eRgB0SJzmnvKAPxxw^e)^;HI{ zOf%~-4bkD@?423=y;(=<(WzepVzQyu#Sw^UC?aAf9sH2D6dV0H9%bjM>;;{i>KfGT zyMOJj$vp4~{9%pwt)c){+iZ+tg=2y0t%H#e;jsCDV^Z03^5IuiX zCsS-90Q)_=MMq)vcB8Vq;9R77Z{)_*2`SE{sGigTuY=ExwD+o;2Wr}U{n|~myg{iM zEHRCQumNh!O1cpEFd`?TV7@uPAtR$ujPH@Y;n>DGTbF=n4fU(*`RZXo zk+5X2T;VkR5hVvhbXN}g04*V9SXa_P<*3bpV zH3w||X6LMehQ!Gl_(SL`^D-G1E9^1`Ic)9JwBtfeU(ggk@c#~fj8AThvFZi$$ z%MJtG+Mb*{Timgn6?21jq;NT7@LH${Pha9a+r5s)M*aHrW;Njg9Ua}Sc_dKc_&OV@ z()CW4rgK96GP_|`xUgHWgn~gJIL#gDY%z#(j4j+C)V~!b0)+rC5;^M>GQb8y#j$}u ztDh!#Aj=!Fwu3{Eg%d@^V2Ne2Nc@l~6j6j>V+vr2{UmFOXR^Qn>z@ZIe#jkU5SM`Z zFYi&Ii7u@rP%FX^hE*iQbuP#&#_D{L(c31(tBtSCtR39eRll%pCvXcd3~NiPcBp%i zrzkX~V}9IzTyFY4pZuF*KDUQ0zluYoa7kl2M3mlm)U4`&J?99ruLJnJowrr)NFZ9 zu_YJ|wj09ok(`!tx z36n{G>O~f2)FvTkCm=qACbE|Qe!U|LrjpC`l8^IjXsp-e_YDfZAc2&dI7Piku21r` z*%$TN_jIq5Mr*rhV{0Am_zaZFm~-|7k)6w|%MVkz2nKFtd4s~GKlC2@?$3Hgl@ZLp z=~}YmXNZ15;i~NHAauS^>Wi>@r?8@*j4tY zE+wPBe*64Gz(px3;(`f64E?SbTwD9+`AY(x5pcqe5=?YfYDh>(q0FC`7>XlO6llT7 zrN8PNt2NXDCp6C=q-dnFU%W`Z*f{;o4@+DWFy0As^5HFIL>fyKa27Q)6YOxDi%w)}h|@IzMZ+*$SA!I|{;42)piE6$BgzL-)%B;b`*s7DMV6bK4Y zXhcX$cG#|cJRo?}2BLA>2+0BQAl+ z6OUMQ3IzpccoN6H0o^uSE9vP#9x}MJ(c_B@adtH0((eYsDBGwqaEkNafafxZX|I?2 z>3x?HGhGZw_>S|%*hscA7ubA*GEIHqtdbwHunP1)vGehQH-b;kz{tw|uhzt0ODCwg zsHqw6(?bgJV_ICZdaD_%Ez+s=vR~v%is;*IC%(mOS|J@?F{nB-f9d%LygqQ*(=y zQPv1C-5yb6mT&wdk32fSyq^9{T-DGlT%><+gRy1xy}jtD7b1vAgUc<6m&LOMo-t4M z9{bjX$mxi;V~MH2n-TirNX*FW=Z?VRz(craBO0#oeL_%JNG=FgoLE?PXQwWQN$rz` zn=h#>XHh%Sp?aC%31(rQwqz@+l;&!WU{mT2R=@p_1EJ1vs!5Qk<#=BiU%)FFEx(hB z{ifT**4D{jK83Mrr#Y^RoJ`StaK!WD_6Cdj>|3jkrDhF{O;NF_QdU-k6M`x7fpzqS zI)f<#lVBixiT1+3Ra6B18YW&0&WFC(tQ0^xz4(f;&+n3&@_V|F9xdHHd4We4U#rZEUQ#G~w zI$K^0RTb?p{SJG1!^&8jUwx8RlQ&jg&}``)p;*b|Skqbs58m2g@f^$^Js@ugX7Nj@HZj1_$#?IG0nzswup# z>l-p416=xBFAFN9Ttw>JKKs7DF?WV7ka0y2ql@1? z;d`h5S93H7!XQ^sv~q>W8#DGW{q9!$vlRx8OwPh$K~FDmk~A$>9HZKy+zh$!oT7D? zcnvG6YmH8&N7r^f$hYzcoV$|+O?+D|(RkohQ&VebYy==A%-XjJ5=%|zXCqS-(O+7~ zX7i<{;a6(VZERvG%z@so{ce9?8q%pr&88ex+TH1|27lvsf_9Aq^TDVEueac|>^$zF zHFQ~zW5%Agn(YahYG0(r0JZ|q7{Cx{07~f34O~nXYlY6S<}n=%V{yA~@o_i0YDdcF z=zby+csfQ9aM?4eJK}jOY`_AbA%FzT7PotRf~S%U;-Yq}T2A}xgE-0sdi9vzxi=38 z+*nS*&F%f;&pT(N)gbmUz+iAY{}EfmxT_9w5>9u5A-#lKK@m zj6SVPIvwzKbPq-^B3yA^ZhE-<;hkq@DA>`#sEqUH6oA&NV0MoRzLfwWJo6tz`2RF& z|C8V@0!#b|U0YljoBJ;xDgN@t#r3pfC;y$KY%i00J5r7HgwL1A%dP<_a%So{YcCB+ zV>Lv*QMZIo#4x7&>O@#3lwQ|H1i+&id@4P;@1rbK9>nXc(}9_-yn7>{n;wS89y{Cc zM@y@@4gD_9E6M)FmA-`Jbi$#hOG=V#gUb5q%|;Vw<@|W8%nE&JSa(Ex5m;O6u?8q` zuZMs&^=}+a6=!F|ogV@%PyI6=jg8R88VTeP++smk?2=LqjtDng>N&XPw2*mP=7%f; zaG!RQU3huIyh1`45TT^=Q5=jDf+r2Rp52mEj~10dJ@Q1XUUH~YvJR!v9tLxK29j&V9A%H$&IZv!4lL+ z7)i{{PGCYQ0S|B`1xP1g=IgpXSXlTv599vF%N!h}sHrR$t3o%6y|#_%FkeIKz=*Bt z5gv}mIhCeV1K2N@@j73UR1X~;o`rk~=EORn zIFvQu^T@%@1nXzP^@U6ZVs|9_{S9t(vG(4Dm<3yoeUjED=bjIs%Nq&l-Q(?q#9ggI z*Q!J0BMS$$wf`U{rI!B3 zTTNBIk~5ZdFE#SiqoR066Nz9#5G4S^3Wa2cBU3nBtWkIfsW^R^NlVn!)cXC?b247{ zp6@wLM-Q-h&9TedSmLY11=ypUR}$YD-E20On@(j>#}&$bBElR^>rWR{tJ)28b#Ti> zcrx?S5y+UYZ9(aPr*Te-h#=Rm{UtjAa92cFp+@Y3hpIruQ1zV z!1QQ-5!&6ck=*{$TEW4qJtNQYap6>RLasWgb zklqelMm=BySSaRZj9J@af$H@ej#EiB&UO@W{aa?)?A-jZ*0CDq)A8$oOe+E$Fv9G7 zi;bu_!!Q}1VltL|cxv?e_ZCN}I2gYA8x=y6=jXpB=0Z&s$~lxN!h6ew_(`%2Z!AYR zQ<2)jR-@Cugt=durQ#D@Kj!P9bjaYw(J}KA%5yTpx5>@<6AK*hHxK~7 z4gjfo1ioN6PNMon5et1@P)tUA@^YpvRQkJ{SD z6wxFlWjn`eBt_EQE{%#B8;{r5=JpruKTD%TFH&(bW*iXTF`Fi1EHyhq99vk(w~`+W zNMk*1+@0cWVR1z)p9W}MrVHfM)DTDc*rSjHQ+gZEuy8=rbn(yN%iHdld9U!vs4_`c z9vYxUB0tLwX&w_yC-;;@E*t+oRkd716}~DXNV-_ng2z31;b`v4`a74o<#?A@R6BgF zFHgu7(=oWo0PZf+~nu+pX!5_z=?rhECADeJEOSY3w< zH2^@dMXQQwiL+c*=mhp@xP*tX0|O)9R1_7N!8DYn@}Y_UAQds4&mksidBFXRla$JX z*Asq8#3Q#fFW|xEh>D1K`NvbGFZrMvaOvwAcyf_HMsjj;qEnLtvS_eJQky@AR-@H= zw!rTE{6f%-n7PNp&Q~wILH#1OtQzicAArdEKyf{K06;@3AtZ8A4V5q zD3AApC~r>ptf}Ktze>>)Fdb0Hpd zN?cR0{MFVrd%KIfE6d$_AxO=H0mBC~e|vVmquw_-y8YPWC6jW>l3RB)FMnl4@O!zI zRL|>yJ|K3IZN%T7^|qUndazzd$D_qUX<&L9I&yakxr2@-ZV0$+C=+NF8{;?s_(3?r zUQnRpjx=8BNlWO(^qHRbO3D1}a*0pS9D8a}yK8-70{yS`SLp!g>|>-N z1B_&Ajvrnc4_ZKIC|cyjLGU-7Jxqtgv4EvJL|a~b0{q0g-|h~UGjMno7Jz`1c$;wP zr_&&+DbIHatE}h!3gjZZyn#MG!ecjBVu}i?F99G*cQt>*IgeQt*-LaFR%>47r`zDS zI+?_@xU}R_P@{V1$;8E6Hyo7S@59GU10$U3=@%>6C0U83Aki(4u)9YELWVTxq108 zXKfF`eFYi;f^!*Z?ugOJ@$C(YBEjL(KeFA}(Z%!+vo-+$CM^P7hE+%JV2reS*~!*j z2CHKaKJ?{YaA`O~I^iKa;V~>ovq$kJj8{Kc_lBF_O>S)KaWWmOyRw~^N`Q4Z+V&X1 zzQ;LHul~kps&svL_afzH=gc330TA{$f{H{$f>Qnol1(?3#kIRvp-R%6!*<#i5bbq( zj17qhr(~st5ix%c%{;TtOqQRwzQ6HKbG-5dV8#y(Zu9|wh(J~i3_<2Rv571LoI*m^ zhDzQ3qP(yY92^|9sw!1(0UYPg@kR_WMXEuAN(HGIM|3eE6#^RJML&=nLI^I9-zuQL z1Z*ArH!Ozn={6jO5jkvtnnZ&_eu5#N{H2ub|nPN8zmzujF_1_ZfcM7Y#9#? zVz^J=&|no6{}bnySt56!r8qq7FS~FF7&{KzMXJ9;scfQgj>kLHeoQfcnozdS{XG;g zH{So1xivz4(V1Ug+ZPpk>0}lOT=x?_NN533=SAcEro!GGxS)c9A0r2VJXvcKYg-nA zB_5E^_DSD)VvBCn#iS*2$@*XD zB4WR5`@gv3=OFiH`?NoZb>{ts?w$p}R+rVe{A-(OVGheV|2$2U$x;Am{Z9|~&|L0( z8E*{lbOAKz?;BdX{;iPAak&|KBZ!RYl~k4Yc=pmuvpG0p+7h(d_XooPx8o(>(RYFL zj2X*~e0IE`kj9a&%Fzn;kAiX$i10giZ>qOE2jkQ3THXD!A001wX}RM0`J3;p9S0jeFRzxa%O$lyYHbvPmeV&gHNcnl zohW~EFx{3H(nopScIV&OD|fbb);U+>$O&-+>X8gGZrf;pjY1K=*CUv(&7%vK`uShd zOZacwjI581`kf+V_p;%BvK#m6qE%|}GO^DkgdL78N?8)b0GP~24 zd4N;fnRQ{QwqpVd5!s%fP{huhXGAU{7VdTAkEpNla)9U%>+Z%@Qkno^D94GO@xP?C z2)$8hG19tL0${m8+>T!ZKW?0;kp6nS4xQcwuhFn(4g3nUquF>33eop77Cny#{%FO@ zdmL58MjV})Mht>9Ek$&HrKU1_+K{Zw%nI8A~7H( zBiB7vA}Indi@V&rgCZZ5m>QDx77$w4;3`>mbu7Td705sw2Ea#v8pD3UQ=bhI^T&`Z zDh&%<;h@v;1}I>T%!hN|?!TK+E;tkY=_VadUu#H7y^|gMa8jiT;D7Yj_z{zdCrMCT z16~c3EMIg^M|p<1J5jY+&-Y(0!8DHV`wwE@MN(GQ9m;zNJrDKP2#0uTzN?`RpsM(1 zg|fp4(1Qv|s4>bnH!}z_Y4PH!`o;u!qbD>5UzTQo}7gSlejBG8F(89 zKua^EK%!tFqxQl*)TNCmqh(+ozuc<~>F$N?KSPe+HG5 zijLuzoM~)KxUsa>N(8!Q$h#4loIWK;e6FmkYM*Kkclw^jg=rhrvG#&SDmQi4^;YR4 zH3>bhKZ!K4a8OZUpVGVcPH_I+FDI|gH{}Xqm1WhftIn#Bg<(aUyg1U!mKR^e+1XV% zWhym0k>%$Kx)>`Vnh9v+KJMpAOxSD>XiN|J=9-st7# z0>7LC7y*WhEf2VuXA3OiU4*r-n1b`+FFzx&hNiV_p4`RE#j9Co(*aeW4l0`C5w} zW%-Mqsi~x(AX7~G_af%z&?6%eL#a0`yv~Gq5|M}?r)F_JgcA?jkyvWd)o#TdOy#Df zqM_NEkk>Qnjp*dNx52XfRGK8{P5xrE_H#xK8?#xb8M5H`IPlTtB>zSASP~ugi%_>d#cb-%wE31*NUdpqz zP*yJYOOlDa)*oNVEt0rS&;ah?aXihBt#?MCkWKFXO5Vn7G+^4%L6XRIVi-a2dcCr@ z$*@3mFlIcP$Yi-C1Bcg%=hdr1zNh;)Qni+IK`Y)X-F~lFxop>7hy?J3>F-XKOUe#! zY$ga2bGcxFv8K6thJ_`;!cuk3s?$_se069DhAOmd@BVT@u1M8BSB+jC-rwIJpU(wa zL}OMpD{JkYq$Ef%ZGJ1Er>7sr&Np9i0ax^RkMVZ(`s56|qoYhATbvy}jkn$ajIyY> zZhnra$;ECd+o#>o@No73Fkd93<%V1iJ8S|1>3}_D40nHPY>Ze|Hgb>S?2J{W9lR-i zaw4fxyd@0278C2p zt;wF0krCVr~L{@=d#7+l|bv+%qfNA&4%|-Ww@130v%G+=BaXaXkJUuEq94$vc zBba}qf`fuSf5Xoonwa>^nHW0HUzIOM{@KGvQSJ+sNCps7oR@5HAiElGI9Zm2_(@WnS_amB{Uu$YMZftBQoA*V*^!N3(cXnc^ zt5Z51@`;CHes5@QM#W1JwK*05Hs^YO@(h#lF^yP+kc^)n4+)8LaWR$4Y5zqaQXt8n ziA4KCGf%T~#>aM_6^%A8#z?-OD4m^1&SX41(T(=%)lLTt_xI-Zq<|`n6tvRsjW9gl zn~AHnaIUslwVo_L@&~Ok($p+0(Wvy#mF7gop2bykEP%Crt*;+V6(W3fSo#Xq)IU3$ zKOg|X!v5NZDj+NjpFuZlOg{dKV0~Oq6F4a9ASaEpNa9JS=C~KG?r-fV~D%z!# zFDEOd(}XT^cPY!<(8TkzLZ847QbDxRU+;kmBV3AHTQlhB%%-k&JtI!{sDh!_5)cv> zN8BEjAQ2U#`g_B&%cj@DBt0ZR666c00<)Ov|Jqw5Cqb^f&)*!#v08RK8uRS!W>5K?~8$%T0sa+ckH#ByBK*g)To3S$oZFzPi^ z?N>(pON#-OpDbN5DFLh4$aXkp7+#VzyNt<5>0hydN?|>^{$Bjm)B%P7Y)!VbPWU~^^@T8T z1`)%LZt>MVQ!FnY^|yG8;Z0?5XOWn{4oX-mg4xw49Cfcr16xSASmymRMli;Dq_fM4 zJMObzy6_gmTN(^cIiz7d_N}_b%MB*eOf=0+6f|Lse@ucel(8N37C+N^*P? zX?w8WH^jKQS?Zc9RdIZWYyw~IRFPj-jbZJ-p=ci4hLH4#rMpS4W$KJ4q95YqphjfgF9s4stYJ3X|h_v1EHEX>TXw;-M)7Ou!- zxMTf>Y-2}e_oKwOm+|Q|Ic5m7;&nCOR229vFbPK~i6u;wD0$)NE6!DjWi2$At8x*X zaOn>Bg2V!`I5usl>Jl4~collZzNz?{t9ev#B5}I=V5U?368Ab( z)0Uy$@_p+r7h${F`iyrZKUTpMObw*HqtLh^S;r?|JUDb1jIVF;byuPT!;8=~Fh7-b=5*`hskNh?Mb z(!ojjTEiymnTVr%T-`G6;U!IJ3!8vv-IyfRO3kIwBR^lgJD+Rfvyq_tQ_u z$AqlC#ey6^Z)&0T57f_fwpU_fj~U6Su<6J%_wE_}pO`_z+p$8a;#3PJit+-!jdiqX zI4Gc}wb}6x`UJKP)aS!7|Di``um7+&trcYDl@W)6W2`GdQGhFgbzcbYIh7s7c zlGhLJSS~Plv1bNvVLYs*vu|6cJm*y#G~h&1mKb)AdV8xmk<)~MHb0NbvWP=O87CN% zUT$c*o;yCKvnk$|&e*XewApIq#5XsYoS!@w6{XV=SAz=rB(aH@8pgPK5anUsTrjk= z`c{fiW-&Q%^@6~4|GU}{p3c5YNi@5(9A?G2192REJ$Cv^Y-rqz*LPfFxn(`P&328+ zx*Zw8gVUN`b)PxdOR<+b0`NSYn?7s8oWd5BpIC9aldwv;ScxF|lLG}@G^Et4jM)1A zf|TTugfry-uIe>!5SI=13O@`Ot~DtBT^ZJN@wr}k4=k~$7*No4t$a8bAo>S?^8e_! zR0gQ)-(XqLf{E(*@X1KTN{GQ(I5!oQbnpg~ak2(D+Ba4uoTDUpi}vRK-9J00+gf*OUeiX-mRDqz{}?Mbh*<%)MsM46KN=gKU7l;U@T_Wj;7zV~ zdf(!J*aoiY`|*8texBvimgS2kzPBF|5CLI&JbgYI6!JZSxWWPkP}N`D z**PQ9_y+TcDw2Z}j-*t0LizQhk7QSy_<)aQ?`fhyn1@DsXxzjf>%9$fdhHnKto>9gXx9>AfyGo{0mGkR-jNP^qn9y_@mIl|4an*6BPuB@$u$hD-Viq|{QMrD4jGh#7aOGAd9fJ8ZL)WE_9l-D6=Kw`B0^9%-3ZjBS4>LN3E)&5>}fBu)Q-dtTIt?K*x?gggIPFUU4v=g0o0@AuuOart??EsTF6O)$XjfOG`n%fv>WO8v;I5lu|^RsV4BC)UV^ME z?rDHg&f4Pv^`5*HQl#;QRZkE_3RdN#z9#of^_d<*s%Ya!(J*E|`>-I@Ll2$@*#VGn zWPpOn7#po$!7dw3dq1s2;@A~J@{Gl>US+JtyFsZ{;eGA_i-~C+Pz6e4GX4WF-#}wL zRJ6iT<+eltuU_7LyqbS@eVit#Qmz|rufN*zc-Xs0kD^|Fa+2^miDT1D;Ul29N8Hvz)Z$+As< zP&G)-JDFP~2f`?(`~F8zI1U1$N%16972N@r&p_v?g{@$J_)y%k@8hU+tq9vunI z77xKfZB|K>V~b|17b}tH_o$#^MTdh~XhlazKW4JZv(41bfxs>yA4U!lk&9=30?=_0 z8F1vF37rv3fjhG!8A@O+fimSBcO#(EQ0@7ji-0mQ;6Gw92=L@V(I5cGVNK_|kp4^= z(2%Zn2FAX40uNy{IPw70+L@Uj6jc3{LW*oib++TtAJe)GzQva90d!KpqvF@_3eb-b zg)FFsf!+%&r(*+gZ&s-{D`3`{&S7_g0O}_0k_Gz|(!f3g=FB5s?a&BP0b(2x13Q}H z8SAZ9P71(`k3Nb}B4=fx11)G8T!V)j(%SOsHql#6plr`dPI(Qak3vDsrB3W|RsTUu zAt)LV@VUVe59L4-x_?hd%2t#JMCU*#)-gPk&J5Wkkn8on^$VbRfrf_wS5*d-1b~EX z)p-Bl0Q+v&9dHmpgPrM_g2^}jq%S2U1$4(73!BCLgN&K~jMaw-HvhFD9bc3<&tckq zzQeoAg4C*VCZI|S57xVZrg_%=7(0OSR!}C;iC$5G%k%KXp!!3^>YnCN75&Du@P}Pc z*yuuPD_LLLd`HoCO$SI~8TT_WOwanS4Sr-&jaX&Ei8L_@(GsD^~qasAY<641si7QTl9HBNZc6l?;boRuL2{| zw$l37Z?*#CEEk(npfAkV_jlfblEi~MOeN4lc|Ca2UhGY0MO%9sGivvIGt75!-DkC& zuM3nzs>w2{?(sODdjU#L68V>rKW zqoT)K$bQJqfjtC3jFtjCzt*^;mE`IZ_+O~^L#4*GwNmNx8o-_mZZA`$`i8>IIy`P{e2D(Vsz1m7Zu7_leefuO`Cf@x9 zJe!S%ikmzfqu3Xmv;{OcGFCP;y1OxxQ(YZUw=%o0vH*)Nka0&M%m%s>hvmWFIvv3}t3V7pMo-#_4T+Qr}FID89h zd-Z4L4mKksOhLlKzeJ+{gWV7R$V30HpQiZAV{wnE?#iN|AXefPIo<3z&dKi?6k!z| znz#=(3DJ|KwnUq%Go1-v(gJ}xbe@goORcfUPHoK4d(QCF#QSRpd`tWWf>t*NLx}F_ z@`QweICyH0<7DqvuwjOey+N?ud;bkDFAQ-J4Tz-d1Tzo z;1MsK=$F+LFZTv`g~8!*qFmwKBK!BExo&5 zh@U|_CF9MAQ`kzaLs!NsFJv1U$B-3Iz6#o)ttOOlNQJa5FDX}IH~Vw2Wosi;+G zoRyjyNyX(FW6oxW;92cwEuEO08U&ttueQA(4j&fn8^rYH?{4mGo}Yo36*M4Y>)V`b z1#1!#GMmzpaf+Bvw3&njlUZE(G6!eAXScoW4f&&H+Hxs9NFTdo;rex6DKBav2KMJdods-6#)mH( zu~1z2h{oP6QO#L+t6N(kX7WmK{nRi_r7{}wrtd!aAwTtxATUr~v3Y%8)|Nu>*86&y ztEB68t7v|G7afx#VQc&9y+!H(KH_5nbjonR#9VbZms#of`!m6s9%JQ1aMjPsPEW8y zj5k$kaG>DucW`0mF}xf(d2Yk(0f>4rX?TlYd;<&L=fWZ2`Bihe3d#{X`e5^0z8SqF z5(XAk>uav0oA&FCoN>mQTc1E&XUk~Y)&qA2@NI4(7DBwoMZ2=1@6b_*6-E6-QBY9D z{_}z3{hqWK{r@uPHP`$M@AA-+|xrXU(JUJikj*B3Uhh_-bHy|E$enB z`1Ie8Tyyz$?|2hOE-DNz+<2TCA{TaNIwv{u$ZZV+?8rkxMwA5k;G35MkmJ(QEIurJ z@g9GY7YTw5-?AIPH`>@RiHWUDp{=#-#+8yD+*!$oaWtPDkamgj{rpB>`kt+Q)h5dB7c!A|9d=-+&{EX=`R}=xSXfw>L|lGKaQQeV zq?$<;(E$j%`p=4|tw8uU_3?di<1|Mz{b^pfc*}WhZ+%!@I-Zr(i;NbHK^{C4oHfx@by76Y5e*7HYJi>>1)3O%(%H}xfg z;LtJBj4qmnyK2m(K71DZ)-L4Y>ZVW~Km=@^EEoSr4JmU{$v*zTLQD&hGFZ{ThV%yp z8M~&Y;zIUi76Yjdj!|A%Outnjgd?Uw#>5>0aG8pMLBDgWbbmI@PaiumRw>qm%J;Np zi-Zk>nwGXRhaDR_3sn(T@yi&G(7vpk(NZ2gzF@RiC-emqfnZYjlGXYp_)|=LF`(Sp z(XRp5$*( zPGw5q9(9f#yHXf7^uPNmL@LkljzfDy1gYz|TO#JTWa|s^+O47r3&BGDs*?Fs ziI8QnaVozvB5~J+(Ke^4n+Yl5(fJ$1K;N(-G>@7OC}Jj~ip^@Wx97XU7|zZi(o#U* zH`9I5FXxXU(TRieMLVRMeu9fD?Bq~T`a~?^PxL~ zS9h005W7D)E$J!v29@}5lJJpGP|{V3`j-EU%zle8Yo%Hq_^j{f>iwfLDTF$_D4dRn zojo#Z3U1KD$!tAr$NuT}4+})tEHvrz0(wJ(4{lkp9V(qG35C^LXCqyjAeJzDX;(+v z#u(dQ_bI+##PILWLoYN7bann|38Jal99sxA>ozlm6^{^V&XorYosXa2X8ym}I)?JZj`$)5M*EIFqrRQJBl(>QgB&l~mA(|Ohu@E6_J&ko z%WT0-zKTZ=2*h1Sog>zn@V+6YrH$C7ja0tg6Um`ti30-ziA)EiUQ5yM#K7N7W(Wc8 zM%{Jh0d3_-$~_3ZkWp*vZf&n%j_KobLM861HQA;)SWfB4;H~StyNlaMAKCuqRGG@H z+?9Os)WP%%ER)%oR4ENpCV{C|E)TMp;|k=mxU}H@n($BUbvo|0T=?g1MXV|H`()Ic z&k_uq*#v8GX^{%-J{Tyu0nxWC*D=K_*Z8ww%nFXw_?-Q|H2=GSFTUC% zNX65vu8T}yU2Xnah4(|AX6_(0WCI8|?d2U6Lk^Hy{I_fAf41WO?W>&1zPwrC;ahoB zMGv7}{n0*TgvJ%`jt)XiZUb~v&92zHaIFR<;>Qb@{li1RoOANdd=6cT>k?T2VJd`{ zd1sti#j$k2J6mVLeP`-m^beSqUtRCXLgVSy_pB4_t6htvDdQRR#Ds*1g;kuKtX?41 zHYZ@YIh)*XXp$W+Nobf7=(-mosTMbYT@=<0Ne+8IgW31I@-j1#iD`IwIk`HS0H8wB z^*+ zbbMK+eXrBTCI*v%3}5-Vcf6baH}LZaEFKPg-QC@Esl0L4;F3K16fA7Nd3;a$M3Hw6pXQ@(4OcB|t=*1ag7{tc-}_7P3oG zAC~d&QLV$$`X17nsy$~2dm;&aVu0NP&AEd|7H3Mm<+iWcIyUEtCy(=9Xi70HQjM9v zvT52I!lSv2D~i#N?-#Wojgm;;9h^ryPwWu(g}{qteBNNEH21Hz=h&ps7rljI z)uvnY+C!c4e7yk~^~VNBhl%N~woH^&RlgSxT>olm+8Xrsx=z}@a-;Tu?^XBl!>KA+ zKK@&a;l-Ds$5&V6bZpoYbN1UBNfm?>(*(_P8E#SQlvPyp^oAw|s6w^oYv+*|uS`ql zn)p#VnKbH8uw=BH$&_fBo+&riG1KYZLIE%e0@Ki=3$e2pCm3;ziS!(F0D?cfPpU{C zi<GWnpmHDr3&x&q=n5mo=o_3+sA%X6TC{LO|*9`CBJ9_~!SlbKq z86T%#s#7UJQB)+tSwXPDBqU@Z|M_6|dZs&vU0u`y+=a>Hb&h;!wfqOLMp1FLjiq|% z>f3x-)6yILsrLYfWw({t z0$?Q9%LUu18T_&ZFYnBcI;f|tr;Gq(6x`v>Snh$o{!t2QLr!(`xe#`cE{wOyBkfL> zb%W#q#05c(nvXbnH6jw&c${gK)-3@U8A#1nWUMY5?%IqX;J8?y(upIuCq45P{E3*0 z#(4o%)-}YP!Ezqwcc@fLspw>ksY>OS;=?_EQ-59XH(OwbY+eDMH1kEV|q7puO)T zlfihK{@n9XcyUMQV+PM~`GPZPm$km1M2~4%$0LCtu@xe~soFaSD2!)YykIl^6)GhE z5&jdDLc*hH?m6W94)ed+0y-g%FZf{{%>RW=rTqy@=^jj8U<7}fioLvA@=ZyhrL2}? zHXPXoj6ER21kmGTdM`f}yaPE~Woa^iV9@0eE|F=$cRRPHFn-80k!dTLzth59S|)r! zz-M8RKOXlH1GrP-;63~AiQ?C)i%ZlD)cS|{dNEEruvfixIbiLUl`%|~to9bwrofgF zyi@q_%&9r;)x?&9!zDiJnnV?gJ}Cm;Wal%r(k0PB<8cr|PY=u^9(c-zkjDjVVNW3qSL}x%zP9|zQ5odB?Oh;Jy?yK9f0`ZvR4lhfaQR1 z$3f$Sicx|ivSyUq#sHo3`u{g#%^^9MuQM2_sa&+x;*$1$2bjVK0KdYm%15UO-1|oQ zyKBxfUM0S`_Z-J{I+AUV1vS^h-+aP87Wz{22_e(TJ{_!(p9|X9v6TUs&Be{_R}H1B zU>=BQz=w9mCg=qf7t`kDjfXeT()f&b>UL~w?Mi|CE!BzQurt=_8ukmjg-xzn$?}ig zTEVk?n4{b$`=y`^0W6TF=x=i>1PF=MOlyZtYsa>?nGK7X%7FDDlX!{JcK9v_vQ`pn zpgVt66}h{DCD+kkq8u9^S9#x?;qq|}BuX=pYY^Zn?6AIYQg#^p8=%(3g(e)zDmi^@ z8+TtfNa#K(JnUX2P12U;MT;@RTX>O+o~JACxK*cRy=n*6X1CS0aIM8lCWjA1MXoB{ zlSMaa_%ebaAhoKrj{`rMaBVrR6wo%01XAv;$`}u(BWallW5!J9QHF-*Mnxs1j#$kz zfPb~6_c$J;hM##;w6Vr!Rd3HGxJAW6!8E24sg;_XHt2)IaIn1-hwDI6Zu8E&On)B_ zs&c^+pWn#`$6f&J`+k6Al(- zCbiAB@v;N{^W94mKtlBMd3_a=jz77mA8efCo61F#PACVu_`ksDm%#a@!Pl+LtX`*d zs8@%ibTx?Ot&@R8s*9S(-JFXuO^(02k{tz=6%>LXY!Y6oK6cA&Gfa8g4TF0OaLWC% zA7^*C#F6m&^7R;mEMzVBw%~Ur(Dd}I!P5-){eGu%C4~b*3)6!um>d+?8x47Z$PSyC zB3=DfP`b53;j5AojsQU#&Ka`Wfs{fhY;lo6-JB3VVuI08bqmx6~=MJFe8R0 zBaKrS(hxbdBvZOfCFB^*a;nips>ykrWyLT~izxfnwf42I{cW%Pd4GG~d9UZ0ndg1p z=l$Qm|8w6q{TV%Z>{rF0EBMJlah?{ViSZCV;hM7}q>BCK1ZxD*m9?_5g`U?d|HrHU z6JC`_OL+@V!w-KGy?vk5WuZGNxQXUEO~`7MzYbP{duk9&gS*7HuAyp8$dzD!rc7os zH%C$-o>kmkBg!<$x-SzgDwcJ(vK5JV6wA$Nf0t-3Og-_|6(=tyhnw63G<3J?=slTe zw4w%;h!D|W8pL~sXt`eeCe0*tC7P?y=$1zDF?6nC6Y@b~{#MYYD4Jg-px)IXK0lS? zYqc)O%*-S#PrJbK0-2aX2(0TxMAL73pbNWE7pCGjiW%&(!Ik%Hi@pLYV4f-Y7U<}C z$*n>n-+KjuDfzwEgaq+IHC7_K#kYKTcsK@50f~a)Ci+*8Xr{piLohd|s;@5ZSkoPN z7>!2H!RgFNW7w2)HOBTL%~L+(ezoXKCrned{Z$}YE386`E3CE;%_cnP7heC`Da9$) z)WKZTS0tfO*5ecWiFgc^>voIlug}NhL75J4exiv1erU1rXR->*dV=6H->MiZh#-g+ z+@a3y>BB%+%llpw!iei7o#>Px6il!KVhtTa1HyHV;JVyiS6z5**^gFxcm&7{3RI)IpaOH4+9B zqD5(dwBo&~$y2;4If(E$(M35&oj2oyc~^bfK3HNJTLw6}$OsSfQX)i>IlLWlJ7kaa zTYJq0*b>=Wyw0O0|2)IY5m(+_3r!B!(Vx_YRp0&~DOni#&cD>-XkzY5Qxa-?M(|D^ zH6PaB?`A}LBv1F0U%c{12fA}v1wha0VtcUW?5$e0#o0c3uVu`7ZIB#f8Mt9Tnv?E9 zU>`yX9sM{{cl#R)4asEL20ls*2VXmNJo_NTH?iGZ?ZlvgYdh_V6y(gQoi{wbEMC6( zq|-R>P5b1iL*qym(?st>iF>l#+M+uAoi_VC?>}DG+*8K3dCQCGE|GJ%+Bw8SYqHz# zM_Ke1UfS%kE*HgWX^HE@KaYc*TMggvMqk$!n}IyiB_=bE|iQ0|uUIeO+7r{gR)zXh#E5B7wX9 z%d`Z6Ud^PKLQ2U?@NwM9HvtREng^}OF%o(!3kl6Z^~eyzVjmxjdY4g!p{@5kDu1M zPGZS7odANeQa5FG_+CbvvDwRbPUC!QpWwZu;IpNpqs;R!&oLH1nGZ%Tgr#~b;WlEZ+4!HNaW8&L zKmC~h>b;<-yL$C3H~353em-8me&!5=v!2b%J(8Gf9r$@?DQ{7dv6M?ud*&>e$FHeV zxD&mk4TVHDIXM(9FdQJ9kFp=n^IhttWm5(klEM2p?G0an*sa_KI2pouEzN`Kn#ijM z3?f^P9=wolk-4AW9k`W5LOpa|7)p&_g6rO#TD$CLGuW}pVH~KGY$s_Yuf8|dN>btn z&OR;*KdEYOt5xEyWQqxJQLtTe>M0}Z0*{A5zd;$(++wf}Noj>H^kWmRH?DPcIi>ol z`1$;9oP(mZPPN4@fmSCH?aMW^;)Z%FpX4a3;e`+9(xjyOlS@dCH3;+brI=qRTg(PO z7*R$*JKOsXkIlH}k80RE4PK|aoNfkjt-Hu)X@^-EQVvNL%PJ_`b}KooryW^}BZDtw zNYvkc^tMQ`teM&1Cby3S&nbkhtJz#qeNVIw%(R-&mW;^|!;r44c3SoI->V-qHPYGu z`+kldymrcS6H}ZxQPvZ9j+43#74xZ;^aOtEaM5VUT#tIm|97QFg z%ZO7R9HBfS(P8_!a#Q$J}~*6EcjlVhNy~l?mBIMZEHX$f>0& zm=2Qg9$7C6$R-krCC)!zonOBa_41V|r}9SD1q)kSt*8y-6c879h^p|yWl+Utcv3r~ z3ja>G`Pr|j0Ryekf-DW=Q5TL#+C%4v^>iuFh`kAEq_E!BL0U;zJ0muZJM4uJ$?)7J zdyWoZ7{`CXnL4E1R@_Q>K5D!=|ZqvuHUfc`GmcHQbcT>x<9UP zTGK~JY+n;H*>}``@Aae~*4Pq|=yz=Gf2k^v$xowl?xf(9sp2MF%&5Ih76?ry=|Ed-~K5Fj`N2ohXEa0u=W!66jx6z;su z?H=8qbdUf4-W%`TF>Y1r)Zv`7_g*sRTx(a@J9#NI6ath74<4XNzY$k@@ZjOg2M->F zA|Zey!>_2*!5hR`Lc>{7Nm)TvNlZdt?46pDxW3v0b|y9^cJM9u&xu1?TtvnF;Z6&p zPQ1(A`Q7A2YMV-MXlVXdQF)v>dWSeOND13OOw#l_ZMTIaxvC1b%4066sJE3$^o(zv zU<+O!o?$6|Y82r~b-KBmyzxohpUkMnU8*@;yYWu0J0r;=3&gDUAF%DRtooc5E2XH2 zg+dlsBiH|SX$$tTIfL2OwuB}Sv-QD3y53*!@`Df@Q6$3$l}Z}t#E{CQNj=}>MJ{XO z{T5@M#DVZVUB2NW>Bq8EZ>0PFOuA@G%M5*Uf#%N5mprYrR4x4UB__yrvBlVW(dW|$ z1!DQuWF&{x=!Y!&4(sW13`BKxJUx;l9@0UL?#8s)m<0XqC_zMz+s(|x0kgSz9o8gS z92`hy3a=gVe7*Ba=H^etUg&I|D=+C(9rJ#S2e%asB^WU>Qs%opX!LUJ=;**t8R%Ri zzT$D_fRF{2Gh9bWqJ5Me>dx}0l-v8}KSkXdotbhTwa*GngP^@_@&5*G(j?sF!R?q!L@ng`<{ zc1w3!XC@D<@hG@bHJ>?%d80=ln3^DoVSdnt@uv*j$Bg+h3QW}xJK{m* zHqw?JBM~GAr*GeKS@%w8!J9*I+&D*PqfiidD=}T-BWWmQuS0N~kXYYdmV3AR;Is0m z$3YVljcKdxr2YGaBCLeZ%F=RuUyHz$NcVM<12ZtpL_1##(XUmMNsx+h=8Y_@t>IZV zDgUJZu6qPKkH-^+^Z05};Z5UfBZUmHf8JcyYo8MgSuEk7Lp`A>QEb#wRT^bWWVyf= zO1i<#=)Vn*Z?^s_p<(Rkc*K*DC~h)T!o$y-X@#j2;zd3a6Pwbd?{e@4b-w;bmzLIB zGd1+;j|#ZVodUcsn1^~d1O(}@-!&3kJPkr3vn~1OP?eO%UYuRxfVXo&NXZCTKy*^* zs%4jv{8*zEVwUf0f3N=Uu|^q0Fr7rYZ?*XJea^5uIw-GI%ky9xXMY;6e;N{Uw|$Z# zr)8{KV!12CeWfpNJoZyifOp4`0rD(|i*VL;j~YH|8i>iQ|JIVAO|ns3Gx6`S0O1`H zS)j`>gAxbf0YUK}hh)4($-!4o{>4}S?vMj^El;hHnpq%b76ZfF*DbwU)V*sQ zLl^C(&Ap(#xm9+CIhNR|_G~1w_I=i?b40mssRf>D%W0W+Sq{5qsWtY2f_c?3aY=7Z z=r}iLT`{MhYPRbN1=aBK{jeDPrk;_lHbAh$?~Y1l8}h(U`0()AImNAS(+VEWp}JQsHXtzOz`fQg(>e_rS}9uNmq}N;rM~n z``_P$dT1?>9O|Ep9~fWOmsZ%0p+gvN{I@-<-;sK{?Dq%ujD;!Gbn9o*z0Uf?Jffc95!8)c)sPdvWj~!{n2i!YM%uRol4*bR{bfl z}Z((Y4TL>d}~EZuE;El!Np zlzqRY?N#Bid=Zv_%MjWl0U1yeX*Eh{R8iK#KJ%hf>F*Me>mV5o85xJ}{$4Uhida4oPHA~*v4ohU$bbQ}HZ7wL+v??a zMpdw#l|V5`GcI(l6f~dVv@XuvGmw4A-+Dr5yYAFy#)?r)a7#9Y~EO$zTmdz zcY0K@$fj&?FIg(=_XLtx9s^YOU}0j%m0$Fl8=uqrfi8d5>hQ=5XSZsrunDc+2Id4s zJxDKEmb6=^5DQ6&D;nQOi3oLxE5j0I#x+M6VT?O1zPY*i7QzI%&=NTxp1C>S;(T&3 z|7ol}Sy=j+bL+rF5P(%1*WQIn#RVAa*h~)mI$(zK!nrLrlX%996p8E2@`^a*+Dt@4 zn~P(_NMY%rr#r&lsTOxyfwpr_3d2>(+RlI3nZ`ME&(;xx`PBDu$c2^gs>pvz1l6gLnHRPktA))XDMlcnf?f+R>4W zChB(+F6r#)X^m||DBB-q1v$Ki__xIVpMbDs;KAIk`}`55r5Flmeu2Qb4sM35XrVhh zI--R09SZ?nZ@>U-t0~$SJGwJ5MjEZ8e(nqX0%6M`@w8N;;e5xw&ec-J!h-f$^R0h# zZ>#=rlOf*i$Q2`z(9MPJ&vcFGJ=sHP+aTuVix~eKPx$D@QcZ2$b0@kex4Z!f^UCQY zHXjlYRM*oc^}qi35hsWkRbeu%Fvmn|f#1My!zD(#*(TQL^$8?)5F|7&wmo~d@;Fmo z@LM9q>iI-XE?fjcr4z7V`!8$cZR*^1yK{@{FRlw$b+*TN^;tbv48XBB$ES^{{2AT) zhA7C;VVcLLO5R6@TB&KNBXZgoT>J{Ql1T8y-I6~q2iqwRqzD4XSI4K_Z|RhfHkD$|GN-4Mi!7GM0=Z~970BC{m zZ!&YcC~ulz1+IfRGBWgaXfyV|Iqkn7fWMzrlT&c%KE_sr3=`JGgrVaC6o{8WEN`a9 zRrmQhz08+8+5Vo&Ia#MX1(V~No~d0|axYg(0BuY1w&!i|xyrcjPdWZ6$n(PnSJJ@& zx$ct8#oes^B@BjrVcnuc#}|a@R)$?}?tT%8;Q{_nI$LE+C*;Jb)O08mKi%m)5x|)a z_)}Lk?RWlU((-qWkd#7dmO~pR!pU~WmtC(~T#~K%fDq5KKV}vh%_M%giU9_;fltY9 zWm22%!=4x7+9@cYK*QY+%O8%HY+@vyo7!+_yX{GF=le0YiV_-e(R0}?_JK%JTkYiK zUEqF?51faD;51(7L`#2SUJ!Lg`aDvI?v?d+VK{2ikaT=YfDW=Hb@SIxSOHf~oYc^# zcFYQ+qwKT{3`U1il@_xV_*Is_|CojEOvgX;@yw#9Eq{KZ&A|;D2_gBaQ0fKZwbIXB zzF#NKmW!WR70?Y=nF%$1I&!WWiiF-hmm^pR>M)|Br%u2UyL7s3rMx}4n5?O*IXFFM zZBG{psZXH?NH>YagAXvWt9mH|wj>&;MI{7&kr$hE-oj844*>7UIL7zB%}V$N3JSCa z_fpWF`SI0Rt6}$tHwNwzsUQZ@;0zP#10P)6!=w#bWaMRbBpK7HxD6eb%r9FgbAWU@7ltq*1{2$^AE) zb>!iMnV7KV*YdU`gk1YZuNTud?lY)V0{VuIuFi|IIcyb2Hwr*QV$R%FyTgyj%Bk&5(e4}g6se=QN=;N4syzX)^~$MqnO?Z_fKXzm$Z+BxwWBnX!Oz0h2&3 zApQk26~@BRh*ijwHGmZw^Ez{U`PfSSnJMqN3jK%ZMdBg;(#BuG1N_p`a@2s+^45CD zrd*@k2jDS>_1g3*!s?@*IOK8UqMi(78)v*O<3W3Rz%irtDxb;KF={zuX2KT?)wqDT&e;{{nYUqq3}P_r;akpk|8 zjF&*>UlQhb;w=W&zL*h)Iz~RWhlsR$JQI z$iv1kt;(3|>qmk48w_&~Jx?}TD;3aJ+Z5Iu$xk_40xXXXowDuSoJ@P%=xbw9NrqDf zW?f@5^gm$YrUfPTJEa+mX8oDR@)G{py()MlprWB)> zmX`;@rp6ksMi`XF*bYt)O?s|3i^8>}7Q0DS*4BDp0}a^fM}`V!q21%w@tokPWdu0Y~zjY{gx7LugAXm*!reC%;d`*UZiZ8?Bc zQk^>=QrdKkYS+Y{M&d)dtTbw>N6L*5;%bw8G*K;l_=kdGy4*`RJA)|PwtYkWjoi@J z{Xcn{*Spb3iX*hQYX$CJF)>L4q_z9$NWPktG0NtHV4k60ihfN_hSZ5v4eJ6h?Q`tv zj2KO{ZawD#;shY^AdX5uvQ+6>nsga?pWNBfM$M8WYdu?wG2cQ#oxijnQX*B2rf4B;P{?j|14)H%#17{?g_Tn3BqWlYrvJi~}x5 zzx&Aa7O0;g0bL*f6CDEN;;ic3mY$xEe|?ZVGK+jMoJU;;qyV#lyX3isM|A3r>q*oI z_W~NwDSP^ue=Sv7*p&-;QiDWb{0In?5?5RU-*Nv8DHL*7*i8EJ{j~8{z_t5Qs?~O+ zfI%Th0R`F)K%Cfs`fg{ry`+N#VNT{_Ys79=Hm_8<2`wGKV~@8M@aO+L#Biiy^7m^x z!Mev~W1VRBEc6mO*H0HRgrb4x-)oI|PEAIK^DZo-qsTq*YmV&6v~K?q|mAm1HF zRL6KXmv|hbq>aL7KTo%h1?Y6y&#Xp2ous&B^>z2I0iIj}aoS?2i|DszqRRQ&H$eYu zdOB&F7pRH%V-80t{j;fvuUJkKSQ8!_i>-$smW9VsNw<6M0MCW;XN1OfeB4XHZDw&D$IO){kdib*M?@!;3MKp-(Fpm+y6S8L zdh=)C%Rqt9Uz#v=gPJvB|tM^xV%J!VKX_}t)Bv9 z3$3igo+wl`UH+mwUa+a~p_vo{4T?5n`nvI!HABt|dNRTf^ zwHjk=>QA%1YR-Iq*i4{eQ$-O8_k3Oz&c3pq(JqQ!kPi672lSh@!hJP|%N>t@f%(60 z;>)IA;`8O;09~c0XW;(fX?nkd;M#_8YPJ>eR5no{EwEkYhs8815axtB&xDIjuiGuW zF9xC;4*HF0a)wopP7FKeIvUR4IwYiX8E7ePY^Ik2Oggezx`p=6W;vnI=3iH1>wxbk zoffwNsb5_-oez3HQIkz_JT4zC*?eJL5nfhwdwaHkJUma+qh3NRu8#P$PQQVoe5P`;Bp_u1fn_a2g2Ic@FD;Y5ZQmHNjM2$+0wk7L7xS64Uv z!IA?l1Eis%>9TksQU&q9W`EOt2S1tHDxx|8u})8rZ)|ms>szk55t|5r{2M?o$pRh* zD2o$2v7ke^ZQ5*RoGAD8NE1gxA&Fn73EPrMxqWdm=e7GuhvGfXEis;jq$UgDR~H=RG5!>u`_#)J4qSTkoUZZ+r_7 zy?j^NdVlTFb<*DvwQNdN=n)>%5+;xz^ns{fZ`TmoVNJE^NOupWF=4-1g90Vq167<+ z?Y}tb|4RgWpHhsLsE`i64~z-k`HLz-TzC34Z?F<7V?iR)2~RP2-L>>Ytx!ty=_SHg zbKAT7JQS4js|VH}7APjG%^6eiV$zOnZ*cD#SFp~-==@JeKJFg{z?bROiq}Fie%t4H zIKQ^b`fdG6xcLDY9p5vz4|;-i2RpH17YDJmsiA^$^IvBguIFe#{ZI37{_WnY0K{Xn zqo4CD>v%84CCr!X22I#lFMABHj|R@d1YIDyYNyClvdO*QkeLrpDpcPmORm??vbl~M zpP#!s0aU#2%K;t#feH|VCYv8$Vb<>v?h3@(+KYRb$sFnN@lTydssMJERcx-Jl2U6m z)U1rnlKAnJ@CPXL8TR5`TxgWd##}k0$HmhIt_upLS5%qX-fr5R5d^^?VV#olb&e~n z$0Wte0cMWFQv-ajYi`zyCM3G!VCfs2=loKI46ei$6D`n_ zeiL%xy@r$PEs)X_kkQ&PodlG17^VvUhY(W2HpGm6!?p41dvxtX;uFrue|i8`U}OTV zpyM8Yr0mP*NiBBO#5pHTc15;i>ZYuDpX^2poOi!*-B}m{qaHu9}BQe0IUA%;D z>2c%EZ)I#ifdBk~V8Y?tMVBKv*Wus*8K|M46eZ?%(2Z<)ewDdB!uXLi`?up(KQC~g zqo(sOY6B@S7%YNmZ3SOGjW@&=K;w^(tjGh;6(F~307~RU)yf$8FSEKWMvJr zY*Ov%9nAUG9-eE#GqvM-n}7fqcL=@uTl--SAVi#V%e7gnZ4_Kw0(dBR>{W({4#WpN z5|aBmszaHUj6;P6rsy^GGe4*GLH%EV(n_LDAp&2)Qxd}YxWK7u zrs~m~zU~#Z?(TkAp`EY6>Bmwm(YOql+3=2go!7qtum6O#Hj8{49Fc4z9Ffz{zT7XW z^ZVK7Kr;gB6a03zw9I0gi9^nKx06wP!H*ejuOOty#Hf8Zq^G-|{MMViy0uPjHmu=} zgy1)Zn%d#2IAr7T*yJ&- z@|-sV%MpRk88O3Y1?sqihw;r7MiZ9KpbF*Tw#BgEvus+RR@@3tRBkx@jW|c=07v#d z=+U8)cYg?Mx1IVGn@i_w0_MR96-nJ$$O{GvBsqN=BzCyzj1cb zPfK#q(C|z(+>R=SIWuVX`k$SZh-xp~`#ACRF@pwoz?zFTMPU9E4gxPuGX|M>NACYQ zMThim?!**_@=LwZcp`c|Pa_`0={VH}G>kAz$-fsf=RT+nh^D zQL=U$lgeq2_3UYr^%b&voYe-jYEYccbR8hrxR>lZ8S979@G+(P4ybf$mHNvQ$9U#u zYXwilwqQFu;-K(8U1N$@WqFDHXFsIFXem4C82~Q*-;jq`J?Bj7Tr6eYG`^gh*ZmG# zm4@Z?dEZVrigy4JiSN<%4UZF(zdE%~e~Pl%Fesfd4Gw~~3CgNR1?+nR-y3*M5!{|m zE?G`^`y{fuvEC!rF2pnQwzV|bB%$bMc(Ly%=~AsGP!7!R(ZtDZbG~LaAE3eHbm-4S zb)-a%oQKInj% zRKh2B1mMSw`=W9Ek6$Jt8-xFF6Hct5RG)eg5K)jLm1N#FHpXRg!CQ&4hi| zqh{GAGWIJl{|!L5Qugz&0aq}*%UlL$JhD8`TJ30cIqdn*G8{a#4Kgh~DnBq0c0BGBf-fEc-5o9E<3 zzQ6rZn$~qksQXaW3V;~C@V^jn2{kC3?(NQ{`!(HQF*lwHiTFSKVTBtE5_`G*W3B4< zy={&AZMM};pi~#o$9{r=Va z*#dkjSt4HF&y;s_=@RI^mZV=Wv<;aY(KOXdX}kxxeEZjx$g!+}%}L$<}~A zhMzQzgNvK}H@=6jyS*l7flC5##!#k{gDcP%2t%@|<+Gv9_E^A0Xz0P3-CoSSX}WUY z#Mhh#g&5*z+^>^lb)tb^kjO2&C7lZckUKm)ZOtW=T>I&&ZeX^SZhD;PGQwS*rJQFJI__`n(GKaHX}w z#5NZ^#z?Uff1J`ru<%Smu-nA)5b!q8GE`#9mh|Fi1ry}bt{J&mEYUenK=J^)1{$~f zF6od#^{jMLYA0wh5aV`Qc%JTU{Q%TDHHy`Q(&}H-&kqj#u$g>VaCLFm<5j8zEEXul zyiR)perCI!yWVW3kH*XxVW8~FBkufOqW-sGpvjZblf9oCr$juxlfdwh9IXB-QvrvA z6sk%%F#d0gy8oYl$Z-915Q6uyU?RKD?ZFs5_s@28OhSD-i;6J_i-KJ$pNiY1ku=okc zP$pIJpW-w8C)7kg*&%U@DoXk;JY3Q55kn$#_K@&6_qcMRz%gCfgGEiKeZ&;q09T&V zovZyXELx*Ry1+v<&+jTTkwpC0QyM>O<)`^nB$zn_7d znl4e5C#HqU2z5-R2U%#xwdvak=2+sTS~ZK$VPvSeF z^Eq>A@8}4du1pxIFf|4j6g4!&VmI>^b95xAQ!7qpdH+tKp@9w?8@nq@qOiNC zCr>Wr-At8LaEV9&A87Jmaa!YkMVSL17`|d70VFP9iAxK z?COrWN#xwtPWSa>qoqv}I2jk?;jmK6$naa}Ph?f*aX)zV7;zzFZ-85vf}K49Pw)Iw ze*OTGVhruxgY@)xFudhx=bfokM(tBuV)`$zA<`nHl3YG&-7keNY!> znZTTPdktzFsE+uJcT z6jFF`b@e$dt*iJGcu^3RsI;Ee3dCH^u94;#kT>PV_;|C8y`!IDaCiCSYLa@YS*Ydq z1o^a7q0G+*e6_q!{7WA^&!W#6A#)=~+`$0?h}&7fNE!}WF;DjdqjD{FrW_KPbhQ+# zZRLPv`&_wF@bgDQ@sw+DUH1e;6PSv>Ygbd%aoLDobO{G5LexueuyJrcOj`p}>Ti{E z+=3yKY;vUjxgtC;-$_oJ`)ECw*QuyB`;)lctdjLTsJ~sQQ&Lc*R9U%I*v>OKCott4 zUaT*&yY9RMmc{FJ`e-bVc}Y!LwxJ(5A-JJDYS&9MeZUX}cuM0yqM}Htn!Oo6O)`mdnP6ETO+_T3qgmR z_`2@SE0sZB#r5Phs4!`=bc4>R(XVF4Agm>{zRqLnco~>q^A9NWuj~5%v2ylrf^2&$ znnt~=5NyzX(I*=dwl7IWSJWFn|AOyWD3B%mZyGcVA(HqCQ%^6RrVyPt8AKPaz_OR^ zHm-kuN#C;*4NRDuxOgBo#1${ujGC1-K0kkVYL|f1QYFW35eYyWJmy>jHd^PVrUilH zcjijUmGF}ja$eqm{(haU-^KpX(Rhn~&0#AmsI9G{2ggP2!Dv?=tMQpT^Iji+z4im` zz^jw`ZjMW$D1Sq#pfX)k!^3Jm7~P+I@|c8VZ@f?)d;XXIS((`Y)9pzqE|IV=x}P5@ z5%49AYFmt>^*dPu0~cV+G6^Nd%bhpCDI=_CP01-KV>_UeWrl4+d@d+bDnvm0j)K9S zpY97wAFZ8y*P3|dcUPcNuo4VhC?|(&FrztQuRjTDV!poo>GNl4krrGKMW24lHOuM) zp-hQVy<{DHz47_;C>Wrq3K0rf3B}7_2}^BWp!n^0yrDUmD&1}ZTa=~hm;g45o}3H6 z0tf;i@jMFyHZnTUqdBRVEvX7yj^*(_N5#kI9WN&Qz{JjuSE&i?{6Ss)UO{AD9=~W@ z2;efy=Wr*E_n6&KX7pFgrJsKMU?BR!y!d9Z2@6OKN=kfS*2%^H)=AR)Rm%O9LxO@+ zRh88`#UAR}Ja@R2YRQfyINAH=L@SUc8hBz6Kaqr{z+FF5NzOk>o1)?BIv)IF(h94U z<}cwmi105<5e+?5`bQ(_zp$-m+%NYIY3afZ?hpXpVfx1zQY|JwI}J~74OLW zbgC+_rCQin=?!q>JuEE z$Mc%7hpJs$nk&K*o%xr!aAH0g=M|)cx$)JHvJSW6(qG$G1V*3XEN@yH?Y~iI;?_&JtNc0!_cZEwC1tte$3ZpbhN;nMlaZx6%_X-pxsdkq zH0RS-s}>xEC5cl#k(a6iuhyoLey*bsF3Wh-$I^?X)Uw!^V-5Xqq&y2x!~2cvz|+w= z^jL7Xar}3r7Kxc2l&RJ5w#CSoSGzTxy5TP}K{z}<)Gu{b=Rm8!oIwaZaN;iC5OH%R zb#}^@A*NnfRP0yCD~_;S>J2p;rDJz zqT-^=BTlVxabis?SD=YM(HcL5fg7uy#hP11uR4#zXr*!BpnP6Ko4c?bHT`7J44d(K zfXBOH9fkHOfzK8{mfHNsz1J0J^S3KXOe<3$I{E@Fxu^YZn8O+LRzQ>PaxAWyV{;ui z>(l%yjYZ8X19IV{Kpq;1LQWe%Pwu>9gA3>G(2#Hazzu159(k|@ai-+D9ROJT8(HAF z*Son7a_hxF<0>Sc-%ws`g)QtgOm;**uMe}qd%HeP`x*fXy~kh!((*apGTg@xRxGtxz0}1 z*+}X7>VbRlkrLL@G^XBC>X+kgMlG2}5MtUTGkS)gJT*Kcu9i8yJ!jM(2c2u8u*%)* zm=*6x%C2w=#rgi>+(Fpi{^sBu?ixP<21rDx6ncw3;%)8Qmoe$Pxs6rN#Lg`2tq46 zg6QJC?b-N&vXhwFKdAD31(Yx8*T-NrSdiX)fWL3d-IThRUkH~H)YR92v)sWYr3d8K zyk|t-zJ&s9;`Za`Q6qWk4tqXYg!_KB|3M83bg0)T&g-w-ez~b3mUQupxzave-v6ti zr2J)Ijm_5i(<9lT1o%;bh^H+bd)C6T(%Mt!bXz@d7H=m1i7iJq_=_>$)WDEtv^Sl@w2;T5_c%!rRTqH)5 zU!*YG*Jb0m#Vpdgfv(94=RR-HMMYwpE->#zg!YKQxGLdrwNrCPu#wnQ2{gpNA|`NY zn}D^>hzVC(U*W}tfO^Ah!|)ZF@7xWV!gV2OGs$^+J>g=2SV*w$#qqk^3%CU-2M4*f zV8mW}8RGlNuk6f>-l>X}B)Q2_E+moe&r{|*N6vjf)AV!Qv9FN(hx_KrQKU z>8!0#lr3TJ<*b`>x-wh*tcSK=PvNJ#ulTT7-ZWK#f(4afuk(tEpIErCA_ECJ2>?o zy?vJo^Z+rRc@T1&yK#}|4jk_4XJx|GH9>gi>Pf=8*y!R`q>?a6k#gT_nRNFUd2{TxpBYmL0Vw=a2vctJFpkF0mNU(GcWn1kBR z`DZcsRgIdWG}g1pA(N%cB#wfKh-w76W${)KM-->fnd*=EKJ*7ihwB6Wq*xiA!Hs#< zN9v%|TOFjClyuY2{Y14eM7J_lJuU!96iEYJzYpKHziF6jv@fqYJa06fa)facfz}AI zX4e4T&-M@IM~)CTX4e$a(`VZ7;j?IcSASHi#gRhSr`^pXAo(Cd*M0?^`!82Tu5WiEekX~DoSEjEHr?)UJ z*tapHmXvGhG6XJgA*P02J5*n6)1%v3Y6OpM`9rahLq>=Nn={fGub#)c2MSl74l4lR zo%JS6f=4rz$MyE5@cs?@e8sLlOYWq5u!~1>(KYH?=~ZAumJS!GwoL5I5`(8NZqOa6 z*ZD*J`(>bR`N%Hinq`HfN&bOky2$yGCSj5%)KC$c1wjRIek zOSDIYVrsIz;_cy`ymJuT>$>nc93Yk0w6NP>AB&R^KPDk(CSH-IS+(RC!4wTOw|;*| zf|E-gh{=`pf8)hyML)~ssMGskfiR9nFQT1ay6D1&-pY5jHT(SxO=3#5t<%z|>TS4d zamr|g1%(g+cTlr-{ckyo=^t36ktwZXDw*E+*WNx;Zhw&K;MoXRZWR8wg}xG@<^$c$ zufd7-LwmuFMZ4mvc=*DPkcLMIZbc5tG>PtpXfB`k*+c{C`Gz1&R411-e6bVL)9Asq+h zv6@xRLWPrdRDu+-sxs@Ww@1@DwXVeTJKvWtY>r&FewVcL6^pvpsv^k|K4rIT<-n0J zG}vcqxO{8n=;)|f?T#F17 za0p(L+ZrSPMi%~PqEL-|*OqTtI77l(N1%?2F`1>z4NpfmRkM6cedZ^M;dmf(Emp>B z5sHvwGC}rO$rk3Nef2mnHq*d-@E=VkWLtP2a|^=(%!@uqM#ZfEiI7!OK_8M)7LHIC|*>x&2U| zLMRoTVbpiZ;#}(XUd%pCt6|y()8pd8TgaA2E$1x)o8d7gLgabevxJh-Y#H-h1p={X z|7==@8e2vLUk?97sQQcHZ6Acwz1GB`T;*I9X-SOjzF$}wj+<{8Zaf=g3_b{BC^gG7 zUizIQ4-Q5Z>HWMfLVz+;z<1q30&Gb%vqgUj9F>3hhiXki3p&xzypzIt(Bs9;_mh%Y zI3sHRxMidzofK~X_AAqP2bKQ=*uBA7sJeEvc|==eT>pi`N-5*a%kPbRR2VI`(r1vz9<`@d*Q)6x z5^vOyuBM} zh;iN+f;!@>H5jgQrs2|)c!sDF)k;b0nV+#>qF7P}yqk^C5CZ%WryH3+dQDHiqRe}a zm+{)YUD3HqKDA}BlqNju-Md1w$f+MT)U0@=23#o@z8wUp2#?jyDk?8d&US$;V|$Y; zoXB@%HO$ch*xO2JQ(Qh`oI{ICyTcSwagqUy@uf3L6@{cOoR6C43q}qiO@=drZhdeFL#) zuqX3n<@Cq+ESmg+w<7=PTU`2BS`4|dvI3oQqBg|SRv;1)a@#Gftb`U6Tql7Gw|6YI zZ%KMT0{h=yL?QqcUQ=UrecjlcgS>tDok#fKB2Qmnd|PO^ptQ;kGF1^#CwTJPaQexT z<((Dr9F$d>?#Zqcx_G@m?cZsfFD}e>c6ZAhT^(0MMn^~CXD2PaV3yUuPB>s;;t2L&8dRaJ}8uT1fDU)t!XsY4u1KbBQjOI3R< zid-JMKTbK zd|X$_2-=0xArwO&vS>a8Ps~PoJ|d{(l3kIuo-Yr%_GS;bbj}fn8-ZwMv>R+wbnC*d zw#ca6aV(vUJb#Yu7r3`{-a}L5tJ$1nfi0z2rS$X?mAJ3KrCE8rD+P_d8Ax#<9l|A9 zjyZ=LC7lZfy%9mO7bZ^@7SdA%@!B>NnpYfB`TfG4E;5?mMudS~wiVtJrE^U_A$hBF z7c%$$d}J=$KHU?uTIjn9lx%_S<67vs$+&?e>gGU74yRezx9iYjK!Ix z0?JI82cnQ(deOBZiDY)naG8D6&8>N$yH=bdXYzXiQwsgGC-3`bSg_{1)u|u;HQ0=N zlduPCz8AEm&5If4rbo%78=UT_)!4D_5U$b-f4zV%dwy1AvfHrW(|r*`x}jfN2RTLP z`oZzJ7Vin}UN9zenT*yf)$s5e+L?E0m;R&%nMpwgZsg05BpeBfdd@al96(^*dzEv* zfy2Dk_4#59>EKLkGFJi{bl}P>p@?3myzoGPeKw+fqloi*IfEQ_;P6qupeK1%e|f`T zs65s@iEEuO{mXga?2e#)Vo)WuV*7mL)HXcg_E-k!Y6@cBtv075JpHZHu{5wf$(<8- zNxw@<2o!9Mk^9@YDVU$A?RBPE6(MQ21CI*C97@Q%@p_lo%kI3i#v7%_ z_Xf)xBb>(zlcX^PiaY|8oxyJQpPSv5sXJ-iy#pp!VUgfid`xr8)&VJ&XlSbn=qZ%^ zpT9&1WXj~7lHCgSPQ3q!dB;HqtDPvcGJgv~Ddx|=BdK=%1F*^Mc>9(sy4T>HCXewW z5{WrXQHjvzm}zUn!+yIr^(p?Cl7TfC{QML?!tPKIAv2%NJAKmiq8M(xGeZRJyrRJR z%?EXV6_~#9(W6l(@bcon4)?6IvU}p+af5tt*72T$&FS_G^aZe4>|i29U8k-^ZY!3^ zVuB>*4soDnx`jX!N<{>ORRU|GANc7r>4mK>OIIcxbO`9}52GM@c@aFFq+u?V_}#h)5nCof^nNpK{@^QSR>gFU*QK&~Vz1#3e$nXte8I8D3!ku)`3Zh54=@ z4|G7Y{y;(oK?M~d{!_3ECQKS13ns*6J>Bz5Zy#=X1!?c3wBL4pUUwkYMWbBsq>?&% z$yO4Eoe=_hTk)|EXkeYOU~0+xn$kOWXpIvEO;IcJA~MBh%>+OJJ9+qI zSN@t1wC+R*kFk9dvTM9#Q<%--#a2|sg*Oj%3yZ%A;L~X$kn)tIQa4^ss(;U`P~aHk z_|4qR@xqeGtTL#Fy_sy+7D0iI?vlI`!n9h7GVAvO80j4X@eJ4UnQ$v6Oq1;pj|0Uw zMt!!b-MZW!^h%*@&6T5_=oLhLZ2exjeqk{{<=nzN@m0AVvH|wbY5cI}ENvbQ@5Swd zUrdoPXqJDOs!2KCy@g;m>vRwM>Po+rr~I{uXUB}udP-_0HTBRrP0v=o?avI@b_}f6 zbb6>+snC;>&&@JmaCR&}b-SA7Zx&Ak?GObGyGXv@5$dqbvovdM*^_fwf9R`!{;YiO zA+vn}Wj7(ySgs#J@MlWtK~8;geg)fI6`8ti+d<<0)!iaEUB&znJ1=_#jdUvQ1}nzB zrhaL4vn3gA_67I@e0#wF`RTNGO(~T2^FwGhHEq8E3yal~(tWhZJ7pZ^<>iHKZ2UU!(TBsoO}l&6 zC|4P{`xDa@%FsPTACs zXnH?WjXT@?f^eq)7OO_^RpR^9OWAD(-M}`8cg^y0YrgfVL*@)CyOVm7>dm%tZ=d3t z_qR_+TstES4$53UziXr$Q)j;!>K3oI+9v~f)W1Z&*s?*Tk}kFrU1Z(B?!Hg8 zE`LJ?b?1fzu%e}@?=++AY!~c#*+#>rX7dzRdguU^g2$`yx-(1$p`^JK1N8aS)Yl%c zR28U_1G@yNbv|ky@%?NCYyGL@SAJsL04DZrlIb%?urEtOJx9sIOaVV&0)(+;oqkj2 z|9oX4VO)vHYJR}=R&ul^z|WPSf{@WVD5)oo7!|SYq z*X{N>hH{}6rBFHtq3-WSYjhG`>5~H7S~upD<-Ea|)a2kj5Sz+onywyj7OH*oST-n7 zqZ^7$sm2__qww=Dpwpl&_!vKJZMJqz%Hv8G-n!;f{Sm}irQ*Y90N1^6YPyZFnq2mH zQ8^tE9~_)T17NC&ea6{znJFIFwE!^ZLkY!mb^?MsjL5s_Y3t6nVq%Io&VZ~YRoT;? zfn6Krqr>lTg1jq}u=9o#>&;XNeiwEmqxsnjC+8aGSP)p0NT3TSZ50FlPa@P4zAjwGKRNySQ=gtGdUBAuB|foAJk z!g4bI<9&WullxfCM~o|W^vOU)RpP9rzEr8)8y`@Kjj>(1lCF7^wl8U5YjM!1i2sJ8 zoX5Qd)6&IU|!YqRF@QxE$csJoe{2b;O2C$NLxPDL?ntfF0lVFLf*buj0NlDylWg z((s6qK?Ed-6tat zt9$jD)jfY|!L6!$Z++jr=i6uRea?lWCXQ*hz3~uAO~wi}po$Xj4QZ5MUYl>Nc%W~7 z(|Liu_53`6$C)0rn2gnJ6Zm~p&e`or<3%rMj>$DFH@}VV3ah02gz=cy40E)_g{Ra& z&3Lk~sPLQj@7pKj*-8x9&vZ}eqGp%ZoAtG&7{SDK<9)F+vP!*B*Gb`3%v(^49cO3|5t-jTECO zO0nJ)7R!$6_q~fxoJYwcLzcr)@C27O0MYgjzW?_@#((j<{UOP5V#R0;!3476Au+iv zlVkou6mAS7Z+m}PHt>GiiYwF< zF=?q=W)^AqV;b^wd!vpjPfN}Abdq$RJn+_HcNJ2dZB|PsGRnkKNU znDgoM*XUfUvkn==3ldr&Lt6F)th- zYZBOov%Z7n*Sf*g<_hIECeO=nIr?yuDJo(F;jeW{HMaF;0*TRc83t!zP~|}|8eMF(Dds%*=A3rNvIoEx7e5kwqL21H(%h9lgYUlW})< zuM_6D=Xv-ijd~nC+Q>DYJav5l=7d1vX{E*%pGrsnWA7>?;lKU9*!s~5 zwOhfvKFi6VXSL^TY$VQ}^CPO`#|~r4Zi`)iAqO%{YGH3IpX}HTzNDNJDh_;pC>lNK zMF-36&2C>4#nsqXM&UIwD8kXiczP~U?OADgxn=v-dC#de`Nvvu>|F>1Yc#ihEPcf< z!5%M*JxKL}=-saVV5#Jw;WxTs<}OR@UJl-KdaO((afetD$w=#VFSIzjf> zB7W4%L+p6TdzYQ@APsjha88$O3kefItuiD0DfHvaR^ za<{wQa|9N3j~ACdG5w1}%wu-&lPQny5Crm8){F!$M>nePCz0dKdH)+#*j{luv;F9; zuf+7c^D*MN3(PvujOJZEff2WP2y}IwUEDs5*A(+vEy+G~+h{7P!yuc;y{-54iQaro z4%_O!gDJ3J)_zE=hVy#=rQ+K5rtNoo!(Y@(D$3>b4_ns)wx-m>@r*~mF5jWSTahsr z^VrO`n>Q;=68hBNS8^SqO;3_&TI3^HyQx_py~4A2L+!TNm6exubrR(tqbz$JAKCu8 zkPvjl&whU2C!mrhz9BCp{MIeUG|R4}0WiZ_IDTnwoFbeY2%n{l!cw$xbo$=d7sJG} zRL&(r<(`J5!zeRf^n`#d85M=M^7{*>`Du|4>S_Str^Eu6Z<({Lu`x~I&-%{r$OE3m zMZ(*+`nShD2EJ%Y6be3@3>|yQ2)v%QO+TSdnQ(RJ;8L0IydZE=+j}P8w#5i;3y{_CsO?Pd@E~*QI6x2dUe@Ely|VU3#F+VWhBF z$?uy3cF3K(VZ}1H6;uS{DcjuiN%J#NsAue0nFP*c+K${rWuo*S)|;Pn>v%o4K9pE_ zkyZN#M>}6M-Gou3^V5`F!zDfENp5*>f3wL*S zOJSM*`egF0!**>HBsBm|cYCdMhF|IeFMZ0gHm_=QrdsIG!ZfqB6gFUcM{2q!yZU{q zKY$-raOGt;zRb_Z*_aGmqQ$+hrpCa+a8q8lq^ncFd~tqL9#|c0WfkQ%RtdTrY}zKk zXT55Iq<@xB(e7sn=PfQ%Iy$;=I3nuC2p7mNai7#G1n>fABdXEt&aB>DG=_=lNPI86A0de9urh?*YVI=Q zp4nV&oZQEO;}v_7O9PD(-33J40L{UCT^fzc-mhn%(JX(w++l_^5fg{G4N|$4YvH$0 zRlPtQqAe!(l0_;M0-ad4J6Bl>+2Y>B+&6k&)jU$$5gII_epfNx)I^=4t_&LCq_OP2%YDUDFI z-0WMzz>gAJ>ogq`6YA`sKiP1*0~3eB4l}-!!k!m*t2b*7k;>ysI{6xS{*BVpTie`E zrSU;l8uOfCZbsk#4s7JzEr*^I-gmd2=**gv=(axnW@cm40^4CygTl*+qb=5>B=$Fv zoN3fskT~)_mG=A=7^G=nZA~g7QuPJX?&W}OhT?l6P2E7nOlIu-e3)$I3QyBz88v9* z0^CfoyRCnbxzjV>BWqDGdkNzyrhV`p)B0jJ5C8suPFr~>@Z{dHOAf)DZ-lrN8|^``z%-0_i+Xk$;$j4EgpdwFZBZ z^EhD3ow(Z!{g7N(ND@XI3CG#=D0=>RNU3eZxv=Nl%<^z39ByzvWO&s|eZn4YcF=*< zdUXz1*>>9d>YzlfxB0Qvq~)(OhMsgJ`N4rl;bciXbTOoEg=Qi<$lb?__v1#=7)SrMb|I)I1bBP4JyoUgE}?i3rq5` z?SxL75C0oro?U@3%4YHog=!jSXi3n7FTX20rbR!Ye9xw z4M1Grpx7lCwg;Y^8A^+Kh?-NaA5-OGqcmTMX#+}lci$%Ns&0tap@AWY?{98N0US_{ zJL_wU|L?Lcq@%{7U-6OKngB5aj&d~#`#*Qi2h#>{d;%6VG7l4Zt5&Cg^}M>FH=FfQ z^Ovyo0FE9qnm#|mcZ>RdFmob8s65z%jjp@qj_wK1+-c;KK)j*J)?;&ddrpE*u|a#2zc13;`@}? zpyYyor8n&;Kub9~y(#|>C7XF(ty!aB#c5jCSDXyVBpREpW~mlQ;A>#kWk}>Rxn1kY z*;KEiA;mS}2X?(;C815#ClCa7h4;}UTXgH(*GHh z+y6Q3WY1;6Js<;}zOcdLSVH*U`$E?rQt?PLNyjeG!BmI2liU+r46kuaE@X(Gcj4r} za>OWF(z6$j!t2|&lSrA4E>w`4S3;>4HkQFW9xYd@YtLC_7{X9mILODNzmGKinW=PM zYujWW*9bUjrPoVLS&pP#C;3(6u8qFG)^uQeig?%-O&O4@NNB-nA`{ZSxb;CJAtpC3 zLI2KFk>rsV35M$YN~@DwZ^73byRU-u9~s`H#N2}=o4OrxgmU+GYRTl2n+FF65oAA; zXZ>IBy8jv({S9dX6sscDQ6~aOfN(Cor$c++VcukjW%^Ari*pO3$dpU?v? z_m6NiMT@4y)6>V)@&5i24A-w4eDxrPpYV9H+j^hN%r_TiyS~*Q(^S#=~y?at=5AYOpbsIy4JT`Oe=AtE@q;f86x0c*;->n%^7a`dzdK{_kTXlp`$RB1URh;eCDP7b$c}I|w+PJvL5vh>-ZFFp` z)bh}G$)_9#*7-nzCzL7;)Z3av?cr=Aa|z8*Z9oI*kBEdm7+u<@H%hHD|h z-VkhvWyMv|xh^w8#Tf{ByzCx+6_^h0vvfb=ZR$Id8BLVJ6x+iPigVOpDWORJOwl(F zVz^F`LE20;my5&}<0m`M&Rzk+bf??Ks~OVuM^8yCC6oJ70;M!qncs|veqnU;6ed={ zrv(N=PQe#i;N!b(SF%=N^J;k(ux2Cre|PyPM%ElKO54Gw0A33ky+ZS)iB@6Z(ZM`ls``@` zERdvH%+DSBjB_Rl<_8ad{4B+5f-i0Enar@f%^Ua|?u~9O_qUC%t8~ zVG&}A`ESSm5%K@^XkR@p#!q-UYYNao{Ig820S1QLGQ=3G9($TUCuDb^k(yg^lkFVr z&p;9mU6e*@aPBZMy;oe^Sm;>6KSi4K<5xXj7YxfPaUJwSkSKMs{Dn?3Z@+=-_Np~J zCM`zZw?6Up>-C{5hfRvw>2*2C|MGM)DT&+hz(|8i1$J`pn9?}LY7ZhPGtDtpCRVKg z;*V7=UjfQmr!Or$b7Ur(r4YXT7s*~u#`l*NphVz-2?2E_|KPu29Zrsm8*Ys*FE_|f zt^ePS>Yb&+vNH0qK>%>`I&`OASXgzftg7017%aF#fj+Vn?D!E#v*>xJ1tmZb^2wyC zn7B6-J$Ls-YHP(2InP|(a{R9up7i2Cy|3_dMhY4lF3607Q1+m=T>99wX^eF0iLDk_ zb@aAP+av}hNJyeaH`_Yw0$V? z#k=DbXwv1$jFu`!XeQdcgf{(F1e;9Y{|&{%sm-a&PB zR8NGSq8x3CrRIBh-nHU}$Iapq3Upvw@;*K9Cn0rS5Oy~$>^r&i?ylRQ{Pk>9AyJE= zGQIZ98@z<^&NR4>M_+DDRHwE8__ioDK!CiNxFZ)+tMQEltAc1Cad-E~c7vo1hvxf^ zOx=AYrTNpF1<=kwR_I03d(_GS3pOD&VJA(d7vY?p)_(-n|9X*6V!gAApVXo4cJiA? zM<<2Kh=0EQ~_&>f80sUB}XB6@S z|52g;FK@zshVxZOd->5W#P62sAVMcBYlj`2Tu0M6%-GT09qG`k0_hFCA(}|!XPw~< zL_G-k=I|+Q1!~zr{+3JZ$Y5;*nd%@!Cq?iS`KigBacTU^1`M8ECf``9UbQB<=l;KekvFj7bgdaKY3!}%hzgyV7{F71^9ev3vG1g<~KvLv+2<>`)#E6-XG+WvI1jR{>~j%{46_LTM!TKq&Y%gFUZ zcO5bT9ct-8{t{cV2=tb{UNioesNixD@zKs~fSU|AWI4If_DXorM?3M5E;(CFq$x$T znq0``PlRwhs7!XK4XC-urET%EvlUNJzMAxrwj~>8lTCGyU``VzQHJ#}2R-Wv+IUtal1NgD6k1|ZeJF==wXTWcIDG#~vhp7hw&#BQs z-!>m6GAY!I4~A;+h2UbFojd*~zS3Y6L{JuS_PHUd7|_s0KFI?oe|8fPjrD%;d1*Xv zjE-97!*=9vXHEMuW2@mzZxhX+wh1Nah56(USy=gyaN?bsbElxMvk=WO3 zBR(-dbCh=+_5S%-AP3no29jgVzov$5n*BJ`b81D})<9U#p@(v3_>p#;Rx z_KV7V<>UYn4ZIl$?Wte^1kx?CW_7jp*xrkha^u)xRT43Ywo>!@@V-bLG>{oQ>pu@ zgXp&>fsiu-xmDt_sX#GY+?-KQli^jWp!))kEL46FVZ!gA5!^Yryw{qW?c`L#Bs3BL zjCpn4DLQ~;XcvV{A8ydpfJjI_RB^lY`k9Ew91f#4HU93wZ7SQzS{}I6D8-(?3k-ih zzqQl^bD44KJn+=I$M_x$J(e8_Y9e5boC`NJCD!46BS}*$2a`6T+HW@I_sePj$j%K| zHcPW~hqrjFjh|b@dQLr9^ov_F0lU+l*9k1YF{1=>mL;8u}k5`k>{G_BVI6>f9T>5;^V_V|; zsbgK&E4dh8h%#q7OMF#Q4sJ?a*x(8j z=+A<(Jz7l{(NpL}qLYQ-QScj0w<~X$JzZ?js73Jn{)S_I*+_@8dU(}3JP{dZO+@V+ z(0E?$3cMnb1A|tLoNqsT~0u;)LvvEegcqXg&rQnrjwNLalz zqzzmZ4(Qk_NrLZ7&Ixf=M^cG%vxI>|2Z+L3zQyA)=f;0QUE#=-#mpL(bp=~gUXxj6 z@JcT{JBLR7QR}TPEo7j}*}*9dy#(3=ZIWa#i(@A?)WDoEO|zWTD24TEa|GL&0On#xtiyqDAfs2 z#|DYRg?dK4-Gk%UYLl_vr0klI5IEp*%n$4R-(uoq zSk~zEJS<6we;{79`>u8Ls$qz;^FoX8j`$DxfQ=&w{V8IH088R_vQ3~~u&GAs?urTnK zqC}}eoeP;He(WXb7EOLty15Q#3ZcC4KA>7fx%8HU0XhgJSAk%4ibvg;4`Z=0T9^K0 z#L;Yk|JTJgGf{UT$#pjr-fng4*^YsTR8o2&t zOU_ySAq+1uF;r2)A)7C)lvFw-*I2mY>UE-DOMa7+Mzvbd3jErJZUzxk*~3$ zg~K>uU$nUXrS=mUoNOac>7g^}#s;PD^tT%YtT$n>{&3bDQs493SRe;)$D%K%aaCKT z5?^I<+AE-w5)BQ(RJEH|58V&HZDK-uB0_(eR>;@D6r8W~;#E%}`@u%EwC9`~30<3N zBiI3Jycyns)G(mA8c;u1D6TQn^1F znK`|Q8l!$lS_6VMb4^ko8f@a+XtyYRll0;}WXD7XwPfF{7ihBFJ`te`PO1i_vNLd# zD-~}dzfh@iZCn62Vno%W?;6#5-z1^7#gxFA?(KaoI^Qtvq;@Kk1DxUx6Dr>=zGdXIrlnD%EF2kd&lkdy zRl>WtXwFYo($y=V`3C4nMg);M}12q(Od~A^e z=Kv)L>iyrxI-qSZ(pCCdxt`tGY8?}0qp2JC{q4#jqMrplKxvo zP#mQ%(~QY@*Z7FG=|Qhx^Gk^46cC8C@U16V*j!YC@_@7;aIJ&7wJPSu4mqOVjd;H+ z@|w028DJoh*ZR@e923A_2+hmpR*-}HYbSzG>;R{CXV4rEhL%G?_YO|TpP)B)4-y>rzx#V0&Tf$hYJbys;U^p zCnmvPc2AeY4f3tm$|UW5eIfaI;F$#F%ELp!Wc>#zT$d=15}?{F)o{iY`;q*%h)zqH zK?DVeW5IOlKMK|#jgFDSfz5%I()qN1&5x03-fR?XYvA}`MP`@4i5_%;m;yrOAH z?X4vz2IJkGlQa2?o&n3;j*gDGc1N4}m6`9CqCg`84)X_{RPzUfZMfNS{HC*>juW;e z=Iya3oucvOMlDCDdX;u{cQ1#JnGVh_zB@ZR7e|3NiP_-FbW@>c&E13k>F;{1j%KDP zC44CghQ8E1y|BD>!^TG~(msCRyk6k#LxFtW4%VLSj zRp&MFdEv9u>Hg-?v{&S0_pxX*HSZ$of>XJuolg6t3>WU20jY4t^yEIPLvX?BKP& zc2AW^8!D;Anf}*Hn@Sd~tZhh8f;#^_Oxyv2av<+~?GxfEb^$2z9vE&Wob zIQULeljyC9&5xDl4B>P>DOm^-zDtLZEPqKK<>G0*>IMx;e}+FTEZ!^b_Z;2E$Gr5T zcQ%R@?wZ8ven2#>ztnIi)X+saWm;14*^@n-a~T;-?O4p&k{&G9APg18n=Isy9wqM| zoVxwyP)ik+H+)A)>8;2kekXvyK9P9Hp@J;3{b`}+sKNHmUiw)`HhSwDF?eiNK4_2y z9In>{`%{*>*xrvBY?pK-4&FAZcZ-2bl|a_uSsRb>StFY7$%(GuFR>mzYN@%Bz2yol z+JGJ&x8Ki9>UMdLXRe6b@!8*p=Zi;QWZaZ7)q=90m$;T%5*W145!>*&qzgDMV_ zYPXokKX|kOcLJPo{4MTpt~(x7dR{)Tw-TA1ANL&F#?Sjhy`}B7zrs^&RNr!2$}83M zVh-2D>kpLr4=)P0@$6%uO%^xI4)$+po+5`wIz(o-CaO-c(tSOm;mT+Gd+feyi|jQ8 oK2vinB$hRx!V`|V9(yD-oHZ?mYj(VedV;2syxP4YS+m#w12hx_R{#J2 literal 0 HcmV?d00001 diff --git a/designer/client/cypress/e2e/__image_snapshots__/electron/Linux/Activities should display activities #2.png b/designer/client/cypress/e2e/__image_snapshots__/electron/Linux/Activities should display activities #2.png new file mode 100644 index 0000000000000000000000000000000000000000..9b230c7dc9747b60bd0a8779b8b079173e77db5f GIT binary patch literal 37338 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�w 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

xQWZz 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 GcmE5ywJHAq delta 19520 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 M07*qoM6N<$f^x~tYXATM diff --git a/designer/client/cypress/e2e/__image_snapshots__/electron/Linux/UndoRedo should work with 'last deployed' tag #1.png b/designer/client/cypress/e2e/__image_snapshots__/electron/Linux/UndoRedo should work with 'last deployed' tag #1.png deleted file mode 100644 index a662f8a738bfdbc6294e6ecbef52546a796bc44c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3070 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 M07*qoM6N<$f^x~tYXATM diff --git a/designer/client/cypress/e2e/activities.cy.ts b/designer/client/cypress/e2e/activities.cy.ts new file mode 100644 index 00000000000..3887b05c27e --- /dev/null +++ b/designer/client/cypress/e2e/activities.cy.ts @@ -0,0 +1,106 @@ +const addCommentActivity = (comment: string) => { + cy.intercept("/api/processes/*/*/activity/comment").as("comment"); + cy.contains(/add comment/i).click(); + cy.get("[data-testid=window]").should("be.visible").find("textarea").eq(0).click().type(comment); + cy.get("[data-testid=window]").find("button").contains(/^Add/i).click(); + cy.wait("@comment"); +}; + +const addAttachmentActivity = (path: string) => { + cy.intercept("/api/processes/*/*/activity/attachments").as("attachment"); + cy.contains(/add attachment/i).click(); + cy.get("[data-testid=window]").should("be.visible").find("input").selectFile(path, { force: true }); + cy.get("[data-testid=window]").find("button").contains(/^Add/i).click(); + cy.wait("@attachment"); +}; + +const findActivity = (query: string) => { + cy.contains("Activities").should("exist").scrollIntoView(); + cy.get('input[placeholder="type here to find past event"]').clear().type(query); +}; + +const makeScreenshot = () => { + cy.get('[data-testid="activities-panel"]').matchImage({ + maxDiffThreshold: 0.01, + screenshotConfig: { + blackout: ["[data-testid='activity-date']"], + }, + }); +}; + +describe("Activities", () => { + const seed = "activities"; + + before(() => { + cy.viewport("macbook-16"); + cy.deleteAllTestProcesses({ filter: seed, force: true }); + }); + + after(() => { + cy.deleteAllTestProcesses({ filter: seed }); + }); + + beforeEach(() => { + cy.visitNewProcess(seed, "testProcess"); + }); + + it("should display activities", () => { + cy.getTestProcessName(seed, "001").then((name) => { + cy.archiveProcess(name); + cy.unarchiveProcess(name); + cy.migrateProcess(name, 2); + }); + + addCommentActivity("comment 1"); + addCommentActivity("comment 2"); + addCommentActivity("comment 3"); + addCommentActivity("comment 4"); + addCommentActivity("comment 5"); + addCommentActivity("comment 6"); + + // Compare action + cy.contains("Activities").should("exist").scrollIntoView(); + cy.contains(/^properties/i) + .should("be.enabled") + .click(); + cy.get("[data-testid=window]").should("be.visible").find("input").eq(1).click().type("100"); + cy.contains(/^apply/i) + .should("be.enabled") + .click(); + + cy.contains(/^save/i).should("be.enabled").click(); + cy.get("[data-testid=window]").should("be.visible").find("textarea").click().type("test comment"); + cy.contains(/^ok/i).should("be.enabled").click(); + + cy.get("[data-testid=compare-2]").eq(0).click(); + + cy.contains("Version to compare").siblings().as("versionToCompare"); + cy.get("@versionToCompare").contains(/2 - created by admin/); + cy.get("@versionToCompare").find("input").should("be.disabled"); + cy.contains("Difference to pick").get("#differentVersion input").select(1); + cy.contains(/^ok/i).should("be.enabled").click(); + + // Rename scenario activity + cy.contains(/^properties/i) + .should("be.enabled") + .click(); + cy.get("[data-testid=window]").should("be.visible").find("input").eq(0).click().type("-rename"); + cy.contains(/^apply/i) + .should("be.enabled") + .click(); + cy.contains(/^save/i).should("be.enabled").click(); + cy.contains(/^ok/i).should("be.enabled").click(); + + findActivity("comment 6"); + makeScreenshot(); + + findActivity("comment 1"); + makeScreenshot(); + cy.contains(/^show less/i).click(); + makeScreenshot(); + + addAttachmentActivity("cypress/fixtures/testProcess.json"); + findActivity("Attachment"); + makeScreenshot(); + }); +}); diff --git a/designer/client/cypress/e2e/fragment.cy.ts b/designer/client/cypress/e2e/fragment.cy.ts index e93a3b8cab9..bf2ae1db728 100644 --- a/designer/client/cypress/e2e/fragment.cy.ts +++ b/designer/client/cypress/e2e/fragment.cy.ts @@ -189,6 +189,7 @@ describe("Fragment", () => { cy.wait("@fragmentInputValidation"); cy.get("[data-testid=window]").find("section").scrollTo("top"); + cy.viewport(1600, 1200); cy.get("[data-testid=window]").find('[data-testid="content-size"]').matchImage(); cy.get("[data-testid=window]").find("section").scrollTo("bottom"); @@ -302,6 +303,7 @@ describe("Fragment", () => { .should("be.visible"); cy.get("[data-testid=window]").find("section").scrollTo("top"); + cy.viewport(1600, 1200); cy.get("[data-testid=window]").find('[data-testid="content-size"]').matchImage(); }); @@ -429,7 +431,7 @@ describe("Fragment", () => { cy.layoutScenario(); cy.get('[joint-selector="layers"]').matchImage({ - maxmaxDiffThreshold: 0.015, + maxDiffThreshold: 0.015, screenshotConfig: { padding: 16 }, }); }); diff --git a/designer/client/cypress/e2e/process.cy.ts b/designer/client/cypress/e2e/process.cy.ts index 32b90e6488e..afcd1748916 100644 --- a/designer/client/cypress/e2e/process.cy.ts +++ b/designer/client/cypress/e2e/process.cy.ts @@ -33,7 +33,9 @@ describe("Process", () => { cy.contains(/^ok$/i).should("be.enabled").click(); cy.wait("@save").its("response.statusCode").should("eq", 200); cy.contains(/^ok$/i).should("not.exist"); - cy.contains(/scenario name changed/i).should("be.visible"); + cy.get('[role="alert"]') + .contains(/scenario name changed/i) + .should("be.visible"); cy.location("href").should("contain", "-renamed"); }); @@ -55,7 +57,9 @@ describe("Process", () => { cy.wait("@save").its("response.statusCode").should("eq", 200); cy.contains(/^ok$/i).should("not.exist"); - cy.contains(/scenario name changed/i).should("be.visible"); + cy.get('[role="alert"]') + .contains(/scenario name changed/i) + .should("be.visible"); cy.location("href").should("contain", "-renamed"); cy.contains(/^properties/i) .should("be.enabled") diff --git a/designer/client/cypress/e2e/undo.cy.ts b/designer/client/cypress/e2e/undo.cy.ts index 9d48189a469..96b59b3f74b 100644 --- a/designer/client/cypress/e2e/undo.cy.ts +++ b/designer/client/cypress/e2e/undo.cy.ts @@ -100,18 +100,6 @@ describe("Undo/Redo", () => { cy.get("@redo").should("be.enabled").click().should("be.disabled"); }); - it("should work with 'last deployed' tag", () => { - const screenshotConfig = { - clip: { x: 0, y: 0, height: 25, width: 2000 }, - }; - cy.dragNode("enricher", { x: 560, y: 500 }); - cy.get("@undo").should("be.enabled").click().should("be.disabled"); - cy.deployScenario("undo"); - cy.contains("v2 | admin").scrollIntoView().parent().matchImage({ screenshotConfig }); - cy.get("@redo").should("be.enabled").click().should("be.disabled"); - cy.contains("v2 | admin").scrollIntoView().parent().matchImage({ screenshotConfig }); - }); - it("should work with counts", () => { cy.intercept("GET", "/api/processCounts/*", { boundedSource: { all: 10, errors: 0, fragmentCounts: {} }, diff --git a/designer/client/cypress/support/process.ts b/designer/client/cypress/support/process.ts index 170e8a0ce16..4aa5fdfdc71 100644 --- a/designer/client/cypress/support/process.ts +++ b/designer/client/cypress/support/process.ts @@ -26,16 +26,24 @@ declare global { removeKafkaTopic: typeof removeKafkaTopic; createSchema: typeof createSchema; removeSchema: typeof removeSchema; + getTestProcessName: typeof getTestProcessName; + archiveProcess: typeof archiveProcess; + unarchiveProcess: typeof unarchiveProcess; + migrateProcess: typeof migrateProcess; } } } const processIndexes = {}; +function getTestProcessName(name: string, index: string) { + return cy.wrap(`${Cypress.env("processNamePrefix")}-${index}-${name}-test-process`); +} + function createTestProcessName(name?: string) { processIndexes[name] = ++processIndexes[name] || 1; const index = padStart(processIndexes[name].toString(), 3, "0"); - return cy.wrap(`${Cypress.env("processNamePrefix")}-${index}-${name}-test-process`); + return getTestProcessName(name, index); } function createProcess( @@ -114,19 +122,35 @@ function addLabelsToNewProcess(name?: string, labels?: string[]) { }); } +function archiveProcess(processName: string) { + return cy.request({ + method: "POST", + url: `/api/archive/${processName}`, + failOnStatusCode: false, + }); +} + +function unarchiveProcess(processName: string) { + return cy.request({ + method: "POST", + url: `/api/unarchive/${processName}`, + failOnStatusCode: false, + }); +} + +function migrateProcess(processName: string, processVersionId: number) { + return cy.request({ + method: "POST", + url: `/api/remoteEnvironment/${processName}/${processVersionId}/migrate`, + failOnStatusCode: false, + }); +} + function deleteTestProcess(processName: string, force?: boolean) { const url = `/api/processes/${processName}`; - function archiveProcess() { - return cy.request({ - method: "POST", - url: `/api/archive/${processName}`, - failOnStatusCode: false, - }); - } - function archiveThenDeleteProcess() { - return archiveProcess().then(() => + return cy.archiveProcess(processName).then(() => cy.request({ method: "DELETE", url, @@ -300,7 +324,7 @@ function deployScenario(comment = "issues/123", withScreenshot?: boolean) { } function cancelScenario(comment = "issues/123") { - cy.contains(/^cancel$/i).click(); + cy.contains("button", /^cancel$/i).click(); cy.get("[data-testid=window] textarea").click().type(comment); cy.contains(/^ok$/i).should("be.enabled").click(); } @@ -326,4 +350,8 @@ Cypress.Commands.add("createKafkaTopic", createKafkaTopic); Cypress.Commands.add("removeKafkaTopic", removeKafkaTopic); Cypress.Commands.add("createSchema", createSchema); Cypress.Commands.add("removeSchema", removeSchema); +Cypress.Commands.add("getTestProcessName", getTestProcessName); +Cypress.Commands.add("archiveProcess", archiveProcess); +Cypress.Commands.add("unarchiveProcess", unarchiveProcess); +Cypress.Commands.add("migrateProcess", migrateProcess); export default {}; diff --git a/designer/client/package-lock.json b/designer/client/package-lock.json index 992a97e28b1..2b5d3c931a8 100644 --- a/designer/client/package-lock.json +++ b/designer/client/package-lock.json @@ -75,6 +75,8 @@ "react-transition-group": "4.4.5", "react-treeview": "0.4.7", "react-truncate-list": "1.0.1", + "react-virtualized-auto-sizer": "1.0.24", + "react-window": "1.8.10", "reduce-reducers": "1.0.4", "redux": "4.2.1", "redux-devtools-extension": "2.13.9", @@ -146,6 +148,7 @@ "@types/react-redux": "7.1.25", "@types/react-select": "3.1.2", "@types/react-transition-group": "4.4.4", + "@types/react-window": "1.8.8", "@types/redux-state-sync": "3.1.2", "@types/testing-library__jest-dom": "5.14.9", "@types/uuid": "8.3.4", @@ -6780,6 +6783,15 @@ "@types/react": "*" } }, + "node_modules/@types/react-window": { + "version": "1.8.8", + "resolved": "https://registry.npmjs.org/@types/react-window/-/react-window-1.8.8.tgz", + "integrity": "sha512-8Ls660bHR1AUA2kuRvVG9D/4XpRC6wjAaPT9dil7Ckc76eP9TKWZwwmgfq8Q1LANX3QNDnoU4Zp48A3w+zK69Q==", + "dev": true, + "dependencies": { + "@types/react": "*" + } + }, "node_modules/@types/redux-state-sync": { "version": "3.1.2", "integrity": "sha512-l5erpFpkZ3/0YCA5Kx8exbaB8PJE1cePaXx/Qhq52hvqTOy1+xQCz/90Ipfu0sypQ480PS54nuVAV7lv+F6CWg==", @@ -22608,6 +22620,31 @@ "react": ">=16.8.x" } }, + "node_modules/react-virtualized-auto-sizer": { + "version": "1.0.24", + "resolved": "https://registry.npmjs.org/react-virtualized-auto-sizer/-/react-virtualized-auto-sizer-1.0.24.tgz", + "integrity": "sha512-3kCn7N9NEb3FlvJrSHWGQ4iVl+ydQObq2fHMn12i5wbtm74zHOPhz/i64OL3c1S1vi9i2GXtZqNqUJTQ+BnNfg==", + "peerDependencies": { + "react": "^15.3.0 || ^16.0.0-alpha || ^17.0.0 || ^18.0.0", + "react-dom": "^15.3.0 || ^16.0.0-alpha || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/react-window": { + "version": "1.8.10", + "resolved": "https://registry.npmjs.org/react-window/-/react-window-1.8.10.tgz", + "integrity": "sha512-Y0Cx+dnU6NLa5/EvoHukUD0BklJ8qITCtVEPY1C/nL8wwoZ0b5aEw8Ff1dOVHw7fCzMt55XfJDd8S8W8LCaUCg==", + "dependencies": { + "@babel/runtime": "^7.0.0", + "memoize-one": ">=3.1.1 <6" + }, + "engines": { + "node": ">8.0.0" + }, + "peerDependencies": { + "react": "^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0" + } + }, "node_modules/read-cache": { "version": "1.0.0", "integrity": "sha1-5mTvMRYRZsl1HNvo28+GtftY93Q=", @@ -32747,6 +32784,15 @@ "@types/react": "*" } }, + "@types/react-window": { + "version": "1.8.8", + "resolved": "https://registry.npmjs.org/@types/react-window/-/react-window-1.8.8.tgz", + "integrity": "sha512-8Ls660bHR1AUA2kuRvVG9D/4XpRC6wjAaPT9dil7Ckc76eP9TKWZwwmgfq8Q1LANX3QNDnoU4Zp48A3w+zK69Q==", + "dev": true, + "requires": { + "@types/react": "*" + } + }, "@types/redux-state-sync": { "version": "3.1.2", "integrity": "sha512-l5erpFpkZ3/0YCA5Kx8exbaB8PJE1cePaXx/Qhq52hvqTOy1+xQCz/90Ipfu0sypQ480PS54nuVAV7lv+F6CWg==", @@ -44523,6 +44569,20 @@ "resolved": "https://registry.npmjs.org/react-truncate-list/-/react-truncate-list-1.0.1.tgz", "integrity": "sha512-zywxMZadwUJ9HkRtlk09OBK0UqX+P/69gKov2tNtIpejE+1OFgX/13G3PPXAbt46gIolPLBtqD1eRZro+wZESg==" }, + "react-virtualized-auto-sizer": { + "version": "1.0.24", + "resolved": "https://registry.npmjs.org/react-virtualized-auto-sizer/-/react-virtualized-auto-sizer-1.0.24.tgz", + "integrity": "sha512-3kCn7N9NEb3FlvJrSHWGQ4iVl+ydQObq2fHMn12i5wbtm74zHOPhz/i64OL3c1S1vi9i2GXtZqNqUJTQ+BnNfg==" + }, + "react-window": { + "version": "1.8.10", + "resolved": "https://registry.npmjs.org/react-window/-/react-window-1.8.10.tgz", + "integrity": "sha512-Y0Cx+dnU6NLa5/EvoHukUD0BklJ8qITCtVEPY1C/nL8wwoZ0b5aEw8Ff1dOVHw7fCzMt55XfJDd8S8W8LCaUCg==", + "requires": { + "@babel/runtime": "^7.0.0", + "memoize-one": ">=3.1.1 <6" + } + }, "read-cache": { "version": "1.0.0", "integrity": "sha1-5mTvMRYRZsl1HNvo28+GtftY93Q=", diff --git a/designer/client/package.json b/designer/client/package.json index edd48d78cdb..dd6728cfb77 100644 --- a/designer/client/package.json +++ b/designer/client/package.json @@ -68,6 +68,8 @@ "react-transition-group": "4.4.5", "react-treeview": "0.4.7", "react-truncate-list": "1.0.1", + "react-virtualized-auto-sizer": "1.0.24", + "react-window": "1.8.10", "reduce-reducers": "1.0.4", "redux": "4.2.1", "redux-devtools-extension": "2.13.9", @@ -139,6 +141,7 @@ "@types/react-redux": "7.1.25", "@types/react-select": "3.1.2", "@types/react-transition-group": "4.4.4", + "@types/react-window": "1.8.8", "@types/redux-state-sync": "3.1.2", "@types/testing-library__jest-dom": "5.14.9", "@types/uuid": "8.3.4", @@ -253,7 +256,7 @@ "start-prod": "BACKEND_DOMAIN=http://localhost:${BE_PORT:-8080} node server.js", "start:backend-docker": "npm run clean-translations && (! wait-on -t 250 tcp:localhost:${BE_PORT:-8080} 2> /dev/null || (echo \"Port: ${BE_PORT:-8080} already in use!\" && exit 1)) && start-server-and-test backend:docker http-get://localhost:${BE_PORT:-8080}/static/main.html start", "start:backend-remote": "npm run clean-translations && webpack serve", - "start:backend-staging": "BACKEND_DOMAIN=https://staging.nussknacker.io npm run start:backend-remote", + "start:backend-staging": "BACKEND_DOMAIN=https://activities-demo.sandbox.nussknacker.io npm run start:backend-remote", "start:backend-demo": "BACKEND_DOMAIN=https://demo.nussknacker.io npm run start:backend-remote", "backend:docker": "docker-compose kill && docker-compose rm -f -v && docker-compose up --no-recreate", "pretest": "npm run check", diff --git a/designer/client/src/actions/actionTypes.ts b/designer/client/src/actions/actionTypes.ts index 577b0fd71db..a6cae1509af 100644 --- a/designer/client/src/actions/actionTypes.ts +++ b/designer/client/src/actions/actionTypes.ts @@ -27,6 +27,8 @@ export type ActionTypes = | "UPDATE_TEST_FORM_PARAMETERS" | "DISPLAY_PROCESS" | "DISPLAY_PROCESS_ACTIVITY" + | "GET_SCENARIO_ACTIVITIES" + | "UPDATE_SCENARIO_ACTIVITIES" | "PROCESS_FETCH" | "PROCESS_LOADING" | "LOADING_FAILED" diff --git a/designer/client/src/actions/nk/scenarioActivities.ts b/designer/client/src/actions/nk/scenarioActivities.ts new file mode 100644 index 00000000000..b305a70e668 --- /dev/null +++ b/designer/client/src/actions/nk/scenarioActivities.ts @@ -0,0 +1,43 @@ +import { ThunkAction } from "../reduxTypes"; +import httpService from "../../http/HttpService"; +import { mergeActivityDataWithMetadata } from "../../components/toolbars/activities/helpers/mergeActivityDataWithMetadata"; +import { extendActivitiesWithUIData } from "../../components/toolbars/activities/helpers/extendActivitiesWithUIData"; +import { UIActivity } from "../../components/toolbars/activities"; + +export type GetScenarioActivitiesAction = { + type: "GET_SCENARIO_ACTIVITIES"; + activities: UIActivity[]; +}; + +export type UpdateScenarioActivitiesAction = { + type: "UPDATE_SCENARIO_ACTIVITIES"; + activities: UIActivity[]; +}; + +export function getScenarioActivities(scenarioName: string): ThunkAction { + return (dispatch) => { + return Promise.all([httpService.fetchActivitiesMetadata(scenarioName), httpService.fetchActivities(scenarioName)]).then( + ([ + { data: activitiesMetadata }, + { + data: { activities }, + }, + ]) => { + const mergedActivitiesDataWithMetadata = mergeActivityDataWithMetadata(activities, activitiesMetadata); + return dispatch({ + type: "GET_SCENARIO_ACTIVITIES", + activities: extendActivitiesWithUIData(mergedActivitiesDataWithMetadata), + }); + }, + ); + }; +} + +export function updateScenarioActivities(activities: (activities: UIActivity[]) => UIActivity[]): ThunkAction { + return (dispatch, getState) => { + return dispatch({ + type: "UPDATE_SCENARIO_ACTIVITIES", + activities: activities(getState().processActivity.activities), + }); + }; +} diff --git a/designer/client/src/actions/reduxTypes.ts b/designer/client/src/actions/reduxTypes.ts index f05a034a528..dd5108f511a 100644 --- a/designer/client/src/actions/reduxTypes.ts +++ b/designer/client/src/actions/reduxTypes.ts @@ -12,11 +12,14 @@ import { NodeDetailsActions } from "./nk/nodeDetails"; import { NotificationActions } from "./nk/notifications"; import { DisplayTestResultsDetailsAction } from "./nk/displayTestResults"; import { LoadProcessVersionsAction } from "./nk/loadProcessVersions"; +import { GetScenarioActivitiesAction, UpdateScenarioActivitiesAction } from "./nk/scenarioActivities"; type TypedAction = | UiActions | SettingsActions | DisplayProcessActivityAction + | GetScenarioActivitiesAction + | UpdateScenarioActivitiesAction | NodeActions | ToolbarActions | NodeDetailsActions diff --git a/designer/client/src/components/comment/CommentContent.tsx b/designer/client/src/components/comment/CommentContent.tsx index faba0aaa73f..d48ead4efe7 100644 --- a/designer/client/src/components/comment/CommentContent.tsx +++ b/designer/client/src/components/comment/CommentContent.tsx @@ -2,8 +2,10 @@ import React, { useMemo } from "react"; import { isEmpty } from "lodash"; import xss from "xss"; import { PanelComment } from "./StyledComment"; -import { Link, ThemeProvider, Typography, useTheme } from "@mui/material"; -import ReactDOMServer from "react-dom/server"; +import { Link, Theme, ThemeProvider, Typography, useTheme } from "@mui/material"; +import ReactDOMServer, { renderToString } from "react-dom/server"; +import Highlighter from "react-highlight-words"; +import { Variant } from "@mui/material/styles/createTypography"; interface Props { content: string; @@ -11,9 +13,60 @@ interface Props { substitutionPattern?: string; substitutionLink?: string; }; + searchWords?: string[]; + variant?: Variant; } -function CommentContent({ commentSettings, content }: Props): JSX.Element { +const withHighlightText = (text: string, searchWords: string[], theme: Theme) => { + const handleReplaceText = (textToReplace: string, valueToReplace: string) => { + return textToReplace.replace( + valueToReplace, + renderToString( + , + ), + ); + }; + + let replacedText = text; + const beforeHtmlTagTextRegexp = /^(.*?)(?=<[a-zA-Z][^\s>]*\b[^>]*>)/; + const beforeHtmlTagText = text.match(beforeHtmlTagTextRegexp)?.[0]; + + if (beforeHtmlTagText) { + replacedText = handleReplaceText(replacedText, beforeHtmlTagText); + } + + const htmlTagInsideTextRegexp = /<([a-zA-Z][^\s>]*)(?:\s[^>]*)?>(.*?)<\/\1>/; + const htmlTagInsideText = text.match(htmlTagInsideTextRegexp)?.[2]; + + if (htmlTagInsideText) { + replacedText = handleReplaceText(replacedText, htmlTagInsideText); + } + + const afterHtmlTagTextRegexp = /<([a-zA-Z][^\s>]*)(?:\s[^>]*)?>(.*?)<\/\1>(\s.*)?/; + const afterHtmlTagText = text.match(afterHtmlTagTextRegexp)?.[3]; + + if (afterHtmlTagText) { + replacedText = handleReplaceText(replacedText, afterHtmlTagText); + } + + if (!beforeHtmlTagText && !htmlTagInsideText && !afterHtmlTagText) { + replacedText = handleReplaceText(replacedText, text); + } + + return replacedText; +}; + +function CommentContent({ commentSettings, content, searchWords, variant = "caption" }: Props): JSX.Element { const theme = useTheme(); const newContent = useMemo(() => { if (isEmpty(commentSettings)) { @@ -34,7 +87,7 @@ function CommentContent({ commentSettings, content }: Props): JSX.Element { } }, [commentSettings, content, theme]); - const __html = useMemo( + const sanitizedContent = useMemo( () => xss(newContent, { whiteList: { @@ -45,9 +98,14 @@ function CommentContent({ commentSettings, content }: Props): JSX.Element { [newContent], ); + const __html = useMemo( + () => (searchWords ? withHighlightText(sanitizedContent, searchWords, theme) : sanitizedContent), + [sanitizedContent, searchWords, theme], + ); + return ( - + ); } diff --git a/designer/client/src/components/modals/AddAttachmentDialog.tsx b/designer/client/src/components/modals/AddAttachmentDialog.tsx new file mode 100644 index 00000000000..0fa60aae090 --- /dev/null +++ b/designer/client/src/components/modals/AddAttachmentDialog.tsx @@ -0,0 +1,66 @@ +import React, { useCallback, useMemo, useState } from "react"; +import { WindowButtonProps, WindowContentProps } from "@touk/window-manager"; +import { PromptContent } from "../../windowManager"; +import { css, cx } from "@emotion/css"; +import { LoadingButtonTypes } from "../../windowManager/LoadingButton"; +import { useTranslation } from "react-i18next"; +import { Typography } from "@mui/material"; +import httpService from "../../http/HttpService"; +import { useDispatch, useSelector } from "react-redux"; +import { getProcessName, getProcessVersionId } from "../../reducers/selectors/graph"; +import { AddAttachment, Attachment } from "../processAttach/AddAttachment"; +import { AttachmentEl } from "../processAttach/AttachmentEl"; +import { getScenarioActivities } from "../../actions/nk/scenarioActivities"; + +const AddAttachmentDialog = (props: WindowContentProps) => { + const [attachments, setAttachment] = useState([]); + const { t } = useTranslation(); + const processName = useSelector(getProcessName); + const processVersionId = useSelector(getProcessVersionId); + const dispatch = useDispatch(); + + const confirmAction = useCallback(async () => { + const attachmentPromises = attachments.map((attachment) => + httpService.addAttachment(processName, processVersionId, attachment.file), + ); + const results = await Promise.all(attachmentPromises); + + if (results.every((result) => result === "success")) { + props.close(); + } + + if (results.some((result) => result === "success")) { + await dispatch(await getScenarioActivities(processName)); + } + }, [attachments, processName, processVersionId, props]); + + const buttons: WindowButtonProps[] = useMemo( + () => [ + { title: t("dialog.button.cancel", "Cancel"), action: () => props.close(), classname: LoadingButtonTypes.secondaryButton }, + { title: t("dialog.button.add", "Add"), action: () => confirmAction() }, + ], + [confirmAction, props, t], + ); + + const handleSetAttachment = useCallback((attachment: Attachment) => { + setAttachment((prevState) => [...prevState, attachment]); + }, []); + + const handleDeleteAttachment = useCallback((attachmentIndex: number) => { + setAttachment((prevState) => prevState.filter((_, index) => index !== attachmentIndex)); + }, []); + + return ( + +

+ {props.data.title} + + {attachments.map((attachment, index) => ( + + ))} +
+ + ); +}; + +export default AddAttachmentDialog; diff --git a/designer/client/src/components/modals/AddCommentDialog.tsx b/designer/client/src/components/modals/AddCommentDialog.tsx new file mode 100644 index 00000000000..d8d40ce5c31 --- /dev/null +++ b/designer/client/src/components/modals/AddCommentDialog.tsx @@ -0,0 +1,55 @@ +import React, { useCallback, useMemo, useState } from "react"; +import { WindowButtonProps, WindowContentProps } from "@touk/window-manager"; +import { PromptContent } from "../../windowManager"; +import { css, cx } from "@emotion/css"; +import { LoadingButtonTypes } from "../../windowManager/LoadingButton"; +import { useTranslation } from "react-i18next"; +import CommentInput from "../comment/CommentInput"; +import { Typography } from "@mui/material"; +import httpService from "../../http/HttpService"; +import { useDispatch, useSelector } from "react-redux"; +import { getProcessName, getProcessVersionId } from "../../reducers/selectors/graph"; +import { getScenarioActivities } from "../../actions/nk/scenarioActivities"; + +const AddCommentDialog = (props: WindowContentProps) => { + const [comment, setState] = useState(""); + const { t } = useTranslation(); + const processName = useSelector(getProcessName); + const processVersionId = useSelector(getProcessVersionId); + const dispatch = useDispatch(); + + const confirmAction = useCallback(async () => { + const status = await httpService.addComment(processName, processVersionId, comment); + if (status === "success") { + await dispatch(await getScenarioActivities(processName)); + props.close(); + } + }, [comment, dispatch, processName, processVersionId, props]); + + const buttons: WindowButtonProps[] = useMemo( + () => [ + { title: t("dialog.button.cancel", "Cancel"), action: () => props.close(), classname: LoadingButtonTypes.secondaryButton }, + { title: t("dialog.button.add", "Add"), action: () => confirmAction() }, + ], + [confirmAction, props, t], + ); + + return ( + +
+ {props.data.title} + setState(e.target.value)} + value={comment} + className={css({ + minWidth: 600, + minHeight: 80, + })} + autoFocus + /> +
+
+ ); +}; + +export default AddCommentDialog; diff --git a/designer/client/src/components/modals/CompareVersionsDialog.tsx b/designer/client/src/components/modals/CompareVersionsDialog.tsx index b8afd7c42a9..7f95f6c71a5 100644 --- a/designer/client/src/components/modals/CompareVersionsDialog.tsx +++ b/designer/client/src/components/modals/CompareVersionsDialog.tsx @@ -1,10 +1,10 @@ /* eslint-disable i18next/no-literal-string */ import { css, cx } from "@emotion/css"; -import { WindowButtonProps, WindowContentProps } from "@touk/window-manager"; +import { WindowButtonProps, WindowContentProps, WindowType } from "@touk/window-manager"; import { keys } from "lodash"; import React, { useCallback, useEffect, useMemo, useState } from "react"; import { useSelector } from "react-redux"; -import { WindowContent } from "../../windowManager"; +import { WindowContent, WindowKind } from "../../windowManager"; import { formatAbsolutely } from "../../common/DateUtils"; import { flattenObj, objectDiff } from "../../common/JsonUtils"; import HttpService from "../../http/HttpService"; @@ -21,6 +21,14 @@ import { useTranslation } from "react-i18next"; import { Option, TypeSelect } from "../graph/node-modal/fragment-input-definition/TypeSelect"; import { WindowHeaderIconStyled } from "../graph/node-modal/nodeDetails/NodeDetailsStyled"; import Icon from "../../assets/img/toolbarButtons/compare.svg"; +import i18next from "i18next"; + +const initState: State = { + otherVersion: null, + currentDiffId: null, + difference: null, + remoteVersions: [], +}; interface State { currentDiffId: string; @@ -29,14 +37,11 @@ interface State { difference: unknown; } -const VersionsForm = () => { +interface Props { + predefinedOtherVersion?: string; +} +const VersionsForm = ({ predefinedOtherVersion }: Props) => { const remotePrefix = "remote-"; - const initState: State = { - otherVersion: null, - currentDiffId: null, - difference: null, - remoteVersions: [], - }; const [state, setState] = useState(initState); const processName = useSelector(getProcessName); @@ -62,15 +67,24 @@ const VersionsForm = () => { [state.difference], ); - const loadVersion = (versionId: string) => { - if (versionId) { - HttpService.compareProcesses(processName, version, versionToPass(versionId), isRemote(versionId)).then((response) => - setState((prevState) => ({ ...prevState, difference: response.data, otherVersion: versionId, currentDiffId: null })), - ); - } else { - setState(initState); + const loadVersion = useCallback( + (versionId: string) => { + if (versionId) { + HttpService.compareProcesses(processName, version, versionToPass(versionId), isRemote(versionId)).then((response) => + setState((prevState) => ({ ...prevState, difference: response.data, otherVersion: versionId, currentDiffId: null })), + ); + } else { + setState(initState); + } + }, + [processName, version], + ); + + useEffect(() => { + if (predefinedOtherVersion) { + loadVersion(predefinedOtherVersion); } - }; + }, [loadVersion, predefinedOtherVersion]); const isRemote = (versionId: string) => { return versionId.startsWith(remotePrefix); @@ -202,6 +216,7 @@ const VersionsForm = () => { Version to compare loadVersion(value)} @@ -229,14 +244,25 @@ const VersionsForm = () => { ); }; -const CompareVersionsDialog = (props: WindowContentProps) => { +export const handleOpenCompareVersionDialog = ( + scenarioVersionId?: string, +): Partial> => ({ + title: i18next.t("dialog.title.compareVersions", "compare versions"), + isResizable: true, + minWidth: 980, + minHeight: 200, + kind: WindowKind.compareVersions, + meta: { scenarioVersionId }, +}); + +const CompareVersionsDialog = (props: WindowContentProps) => { const { t } = useTranslation(); const buttons: WindowButtonProps[] = useMemo(() => [{ title: t("dialog.button.ok", "OK"), action: props.close }], [props.close, t]); return ( } {...props}> - + ); diff --git a/designer/client/src/components/modals/GenerateTestDataDialog.tsx b/designer/client/src/components/modals/GenerateTestDataDialog.tsx index 03d80ea3905..08189d2120e 100644 --- a/designer/client/src/components/modals/GenerateTestDataDialog.tsx +++ b/designer/client/src/components/modals/GenerateTestDataDialog.tsx @@ -18,7 +18,7 @@ import { import { NodeInput } from "../FormElements"; import ValidationLabels from "./ValidationLabels"; import { isEmpty } from "lodash"; -import { FormControl, Typography } from "@mui/material"; +import { Typography } from "@mui/material"; import { LoadingButtonTypes } from "../../windowManager/LoadingButton"; import { nodeInput, nodeValue } from "../graph/node-modal/NodeDetailsContent/NodeTableStyled"; import { NodeTable } from "../graph/node-modal/NodeDetailsContent/NodeTable"; diff --git a/designer/client/src/components/modals/SaveProcessDialog.tsx b/designer/client/src/components/modals/SaveProcessDialog.tsx index d77f2222c57..e281d3a2b76 100644 --- a/designer/client/src/components/modals/SaveProcessDialog.tsx +++ b/designer/client/src/components/modals/SaveProcessDialog.tsx @@ -20,6 +20,7 @@ import { visualizationUrl } from "../../common/VisualizationUrl"; import { useLocation, useNavigate } from "react-router-dom"; import { Typography } from "@mui/material"; import { LoadingButtonTypes } from "../../windowManager/LoadingButton"; +import { getScenarioActivities } from "../../actions/nk/scenarioActivities"; export function SaveProcessDialog(props: WindowContentProps): JSX.Element { const location = useLocation(); @@ -42,6 +43,7 @@ export function SaveProcessDialog(props: WindowContentProps): JSX.Element { await dispatch(UndoActionCreators.clearHistory()); await dispatch(displayCurrentProcessVersion(processName)); await dispatch(displayProcessActivity(processName)); + await dispatch(await getScenarioActivities(processName)); if (isRenamed) { await dispatch(loadProcessToolbarsConfiguration(unsavedNewName)); diff --git a/designer/client/src/components/processAttach/AddAttachment.tsx b/designer/client/src/components/processAttach/AddAttachment.tsx index 1c3dda5abe2..91701d69034 100644 --- a/designer/client/src/components/processAttach/AddAttachment.tsx +++ b/designer/client/src/components/processAttach/AddAttachment.tsx @@ -1,23 +1,31 @@ import React, { useCallback } from "react"; import Dropzone from "react-dropzone"; import { useTranslation } from "react-i18next"; -import { addAttachment } from "../../actions/nk"; -import { useDispatch, useSelector } from "react-redux"; +import { useSelector } from "react-redux"; import { getProcessName, getProcessVersionId } from "../../reducers/selectors/graph"; import ButtonUpload from "../../assets/img/icons/buttonUpload.svg"; import { NodeInput } from "../FormElements"; import { AddAttachmentsWrapper, AttachmentButton, AttachmentDropZone, AttachmentsContainer } from "./StyledAttach"; import { Typography } from "@mui/material"; -export function AddAttachment() { +export interface Attachment { + processName: string; + processVersionId: number; + file: File; +} + +interface Props { + handleSetAttachment: ({ processName, processVersionId, file }: Attachment) => void; +} + +export function AddAttachment({ handleSetAttachment }: Props) { const { t } = useTranslation(); - const dispatch = useDispatch(); const processName = useSelector(getProcessName); const processVersionId = useSelector(getProcessVersionId); const addFiles = useCallback( - (files) => files.forEach((file) => dispatch(addAttachment(processName, processVersionId, file))), - [dispatch, processName, processVersionId], + (files: File[]) => files.forEach((file) => handleSetAttachment({ processName, processVersionId, file })), + [handleSetAttachment, processName, processVersionId], ); return ( diff --git a/designer/client/src/components/processAttach/AddAttachment_deprecated.tsx b/designer/client/src/components/processAttach/AddAttachment_deprecated.tsx new file mode 100644 index 00000000000..1c3dda5abe2 --- /dev/null +++ b/designer/client/src/components/processAttach/AddAttachment_deprecated.tsx @@ -0,0 +1,40 @@ +import React, { useCallback } from "react"; +import Dropzone from "react-dropzone"; +import { useTranslation } from "react-i18next"; +import { addAttachment } from "../../actions/nk"; +import { useDispatch, useSelector } from "react-redux"; +import { getProcessName, getProcessVersionId } from "../../reducers/selectors/graph"; +import ButtonUpload from "../../assets/img/icons/buttonUpload.svg"; +import { NodeInput } from "../FormElements"; +import { AddAttachmentsWrapper, AttachmentButton, AttachmentDropZone, AttachmentsContainer } from "./StyledAttach"; +import { Typography } from "@mui/material"; + +export function AddAttachment() { + const { t } = useTranslation(); + const dispatch = useDispatch(); + const processName = useSelector(getProcessName); + const processVersionId = useSelector(getProcessVersionId); + + const addFiles = useCallback( + (files) => files.forEach((file) => dispatch(addAttachment(processName, processVersionId, file))), + [dispatch, processName, processVersionId], + ); + + return ( + + + {({ getRootProps, getInputProps }) => ( + + + + + + {t("attachments.buttonText", "drop or choose a file")} + + + + )} + + + ); +} diff --git a/designer/client/src/components/processAttach/AttachmentEl.tsx b/designer/client/src/components/processAttach/AttachmentEl.tsx index 5196c23b272..ef4eb7fa2c0 100644 --- a/designer/client/src/components/processAttach/AttachmentEl.tsx +++ b/designer/client/src/components/processAttach/AttachmentEl.tsx @@ -1,26 +1,28 @@ import React from "react"; -import DownloadIcon from "@mui/icons-material/Download"; -import { Attachment } from "../../reducers/processActivity"; -import HttpService from "../../http/HttpService"; -import Date from "../common/Date"; -import { AttachmentDetails, DownloadAttachment, DownloadButton, AttachHeader } from "./StyledAttach"; -import { ProcessName } from "../Process/types"; +import { AttachmentDetails, AttachHeader } from "./StyledAttach"; import { Typography } from "@mui/material"; +import { Attachment } from "./AddAttachment"; +import { DeleteOutline } from "@mui/icons-material"; +import { StyledIconButton } from "../toolbars/activities/ActivitiesSearch"; -export function AttachmentEl({ data, processName }: { data: Attachment; processName: ProcessName }) { +export function AttachmentEl({ + attachment, + handleDeleteAttachment, + index, +}: { + attachment: Attachment; + handleDeleteAttachment: (index: number) => void; + index: number; +}) { return (
  • - - HttpService.downloadAttachment(processName, data.id, data.fileName)}> - - - - - {` | v${data.processVersionId} | ${data.user}`} + {` ${attachment.file.name} | v${attachment.processVersionId}`} + handleDeleteAttachment(index)}> + + - {data.fileName}
  • ); diff --git a/designer/client/src/components/processAttach/AttachmentEl_deprecated.tsx b/designer/client/src/components/processAttach/AttachmentEl_deprecated.tsx new file mode 100644 index 00000000000..5196c23b272 --- /dev/null +++ b/designer/client/src/components/processAttach/AttachmentEl_deprecated.tsx @@ -0,0 +1,27 @@ +import React from "react"; +import DownloadIcon from "@mui/icons-material/Download"; +import { Attachment } from "../../reducers/processActivity"; +import HttpService from "../../http/HttpService"; +import Date from "../common/Date"; +import { AttachmentDetails, DownloadAttachment, DownloadButton, AttachHeader } from "./StyledAttach"; +import { ProcessName } from "../Process/types"; +import { Typography } from "@mui/material"; + +export function AttachmentEl({ data, processName }: { data: Attachment; processName: ProcessName }) { + return ( +
  • + + HttpService.downloadAttachment(processName, data.id, data.fileName)}> + + + + + + + {` | v${data.processVersionId} | ${data.user}`} + + {data.fileName} + +
  • + ); +} diff --git a/designer/client/src/components/processAttach/ProcessAttachments.tsx b/designer/client/src/components/processAttach/ProcessAttachments.tsx index f126a3c5289..fff13feadbc 100644 --- a/designer/client/src/components/processAttach/ProcessAttachments.tsx +++ b/designer/client/src/components/processAttach/ProcessAttachments.tsx @@ -2,8 +2,8 @@ import React from "react"; import { useSelector } from "react-redux"; import { RootState } from "../../reducers"; import { getCapabilities } from "../../reducers/selectors/other"; -import { AttachmentEl } from "./AttachmentEl"; -import { AddAttachment } from "./AddAttachment"; +import { AttachmentEl } from "./AttachmentEl_deprecated"; +import { AddAttachment } from "./AddAttachment_deprecated"; import { ProcessAttachmentsList, ProcessAttachmentsStyled } from "./StyledAttach"; import { getProcessName } from "../../reducers/selectors/graph"; diff --git a/designer/client/src/components/themed/InputWithIcon.tsx b/designer/client/src/components/themed/InputWithIcon.tsx index aa331329c59..6450b08bf58 100644 --- a/designer/client/src/components/themed/InputWithIcon.tsx +++ b/designer/client/src/components/themed/InputWithIcon.tsx @@ -1,5 +1,5 @@ import { css, cx } from "@emotion/css"; -import { Box, styled, useTheme } from "@mui/material"; +import { useTheme } from "@mui/material"; import React, { forwardRef, PropsWithChildren, ReactElement, ReactNode, useCallback, useImperativeHandle, useRef } from "react"; import { ClearIcon } from "../table/SearchFilter"; import { InputProps, ThemedInput } from "./ThemedInput"; @@ -8,6 +8,7 @@ type Props = PropsWithChildren & { onClear?: () => void; onAddonClick?: () => void; endAdornment?: ReactNode; + onKeyDown?: (e: KeyboardEvent) => void; }; export type Focusable = { diff --git a/designer/client/src/components/toolbarSettings/TOOLBAR_COMPONENTS_MAP.ts b/designer/client/src/components/toolbarSettings/TOOLBAR_COMPONENTS_MAP.ts index 2c5fe39982b..b6b326342db 100644 --- a/designer/client/src/components/toolbarSettings/TOOLBAR_COMPONENTS_MAP.ts +++ b/designer/client/src/components/toolbarSettings/TOOLBAR_COMPONENTS_MAP.ts @@ -10,6 +10,7 @@ import { UserSettingsPanel } from "../toolbars/UserSettingsPanel"; import { VersionsPanel } from "../toolbars/VersionsPanel"; import ProcessActions from "../toolbars/scenarioActions/ProcessActions"; import { SearchPanel } from "../toolbars/search/SearchPanel"; +import { ActivitiesPanel } from "../toolbars/activities"; export const TOOLBAR_COMPONENTS_MAP: Record> = { DefaultPanel: DefaultToolbarPanel, @@ -26,4 +27,5 @@ export const TOOLBAR_COMPONENTS_MAP: Record import("../toolbars/Survey")), + "activities-panel": ActivitiesPanel, }; diff --git a/designer/client/src/components/toolbars/activities/ActivitiesPanel.tsx b/designer/client/src/components/toolbars/activities/ActivitiesPanel.tsx new file mode 100644 index 00000000000..a64cf24a495 --- /dev/null +++ b/designer/client/src/components/toolbars/activities/ActivitiesPanel.tsx @@ -0,0 +1,181 @@ +import React, { useCallback, useEffect, useRef, useState } from "react"; +import { ToolbarPanelProps } from "../../toolbarComponents/DefaultToolbarPanel"; +import { ToolbarWrapper } from "../../toolbarComponents/toolbarWrapper/ToolbarWrapper"; +import { ActionMetadata, ActivitiesResponse, ActivityMetadata } from "./types"; +import { VariableSizeList } from "react-window"; +import AutoSizer from "react-virtualized-auto-sizer"; +import { ActivitiesPanelRow } from "./ActivitiesPanelRow"; +import { Box, CircularProgress, styled } from "@mui/material"; +import { useActivitiesSearch } from "./useActivitiesSearch"; +import { ActivitiesSearch } from "./ActivitiesSearch"; +import { blendDarken, blendLighten } from "../../../containers/theme/helpers"; +import { ActivitiesPanelFooter } from "./ActivitiesPanelFooter"; +import { useDispatch, useSelector } from "react-redux"; +import { getProcessName } from "../../../reducers/selectors/graph"; +import { getScenarioActivities, updateScenarioActivities } from "../../../actions/nk/scenarioActivities"; +import { getVisibleActivities } from "../../../reducers/selectors/activities"; +import { handleToggleActivities } from "./helpers/handleToggleActivities"; + +const StyledVariableSizeList = styled(VariableSizeList)(({ theme }) => ({ + "::-webkit-scrollbar": { + width: "5px", + height: "0", + }, + "::-webkit-scrollbar-track": { + background: blendDarken(theme.palette.common.white, 0.75), + }, + "::-webkit-scrollbar-thumb": { + background: blendLighten(theme.palette.background.paper, 0.5), + }, + "::-webkit-scrollbar-thumb:hover": { + background: blendLighten(theme.palette.background.paper, 0.5), + }, +})); + +export type Activity = ActivitiesResponse["activities"][number] & { + activities: ActivityMetadata; + actions: ActionMetadata[]; +}; + +export type ItemActivity = Activity & { + uiGeneratedId: string; + uiType: "item"; + isHidden: boolean; + isFound: boolean; + isActiveFound: boolean; +}; + +export type ButtonActivity = { + uiGeneratedId: string; + uiType: "toggleItemsButton"; + sameItemOccurrence: number; + isClicked: boolean; +}; + +export type DateActivity = { + uiGeneratedId: string; + uiType: "date"; + value: string | [string, string]; +}; + +export type UIActivity = ItemActivity | ButtonActivity | DateActivity; + +const estimatedItemSize = 150; +const panelHeight = "500px"; + +export const ActivitiesPanel = (props: ToolbarPanelProps) => { + const listRef = useRef(null); + + /* + * It's for a calculation of dynamic items size https://github.com/bvaughn/react-window/issues/582 + **/ + const rowHeights = useRef({}); + const [isLoading, setIsLoading] = useState(true); + const scenarioName = useSelector(getProcessName); + const uiActivities = useSelector(getVisibleActivities); + + const dispatch = useDispatch(); + + const setRowHeight = useCallback((index: number, height: number) => { + if (listRef.current) { + listRef.current.resetAfterIndex(0); + } + + rowHeights.current = { ...rowHeights.current, [index]: height }; + }, []); + + const getRowHeight = useCallback((index: number) => { + return rowHeights.current[index] || estimatedItemSize; + }, []); + + const handleUpdateScenarioActivities = useCallback( + (activities: (activities: UIActivity[]) => UIActivity[]) => dispatch(updateScenarioActivities(activities)), + [dispatch], + ); + const { handleSearch, foundResults, selectedResult, searchQuery, changeResult, handleClearResults } = useActivitiesSearch({ + activities: uiActivities, + handleScrollToItem: (index, align) => listRef.current.scrollToItem(index, align), + handleUpdateScenarioActivities, + }); + + const handleHideRows = (uiGeneratedId: string, sameItemOccurrence: number) => { + dispatch( + updateScenarioActivities((prevState) => { + const { uiActivities, buttonPosition } = handleToggleActivities(prevState, uiGeneratedId, sameItemOccurrence, "collapse"); + listRef.current.scrollToItem(buttonPosition - 2); + return uiActivities; + }), + ); + }; + + const handleShowRows = (uiGeneratedId: string, sameItemOccurrence: number) => { + dispatch( + updateScenarioActivities((prevState) => { + const { uiActivities } = handleToggleActivities(prevState, uiGeneratedId, sameItemOccurrence, "expand"); + return uiActivities; + }), + ); + }; + + const handleFetchActivities = useCallback(async () => { + setIsLoading(true); + try { + await dispatch(await getScenarioActivities(scenarioName)); + } finally { + setIsLoading(false); + } + }, [dispatch, scenarioName]); + + useEffect(() => { + handleFetchActivities(); + }, [handleFetchActivities]); + + return ( + + + + {isLoading ? ( + + + + ) : ( + + {({ width, height }) => ( + { + return uiActivities[index].uiGeneratedId; + }} + > + {({ index, style }) => ( + + )} + + )} + + )} + + + + ); +}; diff --git a/designer/client/src/components/toolbars/activities/ActivitiesPanelFooter.tsx b/designer/client/src/components/toolbars/activities/ActivitiesPanelFooter.tsx new file mode 100644 index 00000000000..c90c57d1cc0 --- /dev/null +++ b/designer/client/src/components/toolbars/activities/ActivitiesPanelFooter.tsx @@ -0,0 +1,50 @@ +import React, { useCallback } from "react"; +import { Box, Button, lighten, styled } from "@mui/material"; +import { useWindows, WindowKind } from "../../../windowManager"; +import { useTranslation } from "react-i18next"; + +const StyledFooterButton = styled(Button)(({ theme }) => ({ + textTransform: "none", + padding: `${theme.spacing(0.5)} ${theme.spacing(2)}`, + flex: 1, + backgroundColor: lighten(theme.palette.background.paper, 0.2), + ...theme.typography.caption, + color: theme.palette.getContrastText(lighten(theme.palette.background.paper, 0.2)), + "&:hover": { + backgroundColor: theme.palette.action.hover, + }, +})); + +export const ActivitiesPanelFooter = () => { + const { t } = useTranslation(); + const { open } = useWindows(); + + const handleOpenAddComment = useCallback(() => { + open({ + title: "Add comment", + isModal: true, + shouldCloseOnEsc: true, + kind: WindowKind.addComment, + }); + }, [open]); + + const handleOpenAddAttachment = useCallback(() => { + open({ + title: "Add attachment", + isModal: true, + shouldCloseOnEsc: true, + kind: WindowKind.addAttachment, + }); + }, [open]); + + return ( + + + {t("activities.footer.addComment", "Add comment")} + + + {t("activities.footer.addAttachment", "Add attachment")} + + + ); +}; diff --git a/designer/client/src/components/toolbars/activities/ActivitiesPanelRow.tsx b/designer/client/src/components/toolbars/activities/ActivitiesPanelRow.tsx new file mode 100644 index 00000000000..8ce5f07b96b --- /dev/null +++ b/designer/client/src/components/toolbars/activities/ActivitiesPanelRow.tsx @@ -0,0 +1,72 @@ +import React, { CSSProperties, memo, useEffect, useMemo, useRef } from "react"; +import { DateItem, ActivityItem, ToggleButtonItem } from "./ActivityPanelRowItem"; +import { UIActivity } from "./ActivitiesPanel"; +import { useTranslation } from "react-i18next"; +import { useSelector } from "react-redux"; +import { RootState } from "../../../reducers"; +import { getProcessState } from "../../../reducers/selectors/scenarioState"; + +interface Props { + index: number; + style?: CSSProperties | undefined; + setRowHeight: (index: number, height: number) => void; + handleShowRows(uiGeneratedId: string, sameItemOccurrence: number): void; + handleHideRows(uiGeneratedId: string, sameItemOccurrence: number): void; + activities: UIActivity[]; + searchQuery: string; +} + +export const ActivitiesPanelRow = memo(({ index, style, setRowHeight, handleShowRows, handleHideRows, activities, searchQuery }: Props) => { + const scenarioState = useSelector((state: RootState) => getProcessState(state)); + + const { t } = useTranslation(); + const rowRef = useRef(null); + const activity = useMemo(() => activities[index], [activities, index]); + const firstDeployedIndex = useMemo( + () => activities.findIndex((activeItem) => activeItem.uiType === "item" && activeItem.type === "SCENARIO_DEPLOYED"), + [activities], + ); + const isRunning = firstDeployedIndex === index && scenarioState.status.name === "RUNNING"; + const isFirstDateItem = activities.findIndex((activeItem) => activeItem.uiType === "date") === index; + + useEffect(() => { + if (rowRef.current) { + setRowHeight(index, rowRef.current.clientHeight); + } + }, [index, rowRef, setRowHeight]); + + const itemToRender = useMemo(() => { + switch (activity.uiType) { + case "item": { + return ; + } + case "date": { + return ; + } + case "toggleItemsButton": { + return ( +
    + {activity.isClicked ? ( + handleHideRows(activity.uiGeneratedId, activity.sameItemOccurrence)}> + {t("activitiesPanel.buttons.showLess", "Show less")} + + ) : ( + handleShowRows(activity.uiGeneratedId, activity.sameItemOccurrence)}> + {t("activitiesPanel.buttons.showMore", "Show {{sameItemOccurrence}} more", { + sameItemOccurrence: activity.sameItemOccurrence, + })} + + )} +
    + ); + } + default: { + return null; + } + } + }, [activity, handleHideRows, handleShowRows, isRunning, isFirstDateItem, searchQuery, t]); + + return
    {itemToRender}
    ; +}); + +ActivitiesPanelRow.displayName = "ActivitiesPanelRow"; diff --git a/designer/client/src/components/toolbars/activities/ActivitiesSearch.tsx b/designer/client/src/components/toolbars/activities/ActivitiesSearch.tsx new file mode 100644 index 00000000000..4e923840257 --- /dev/null +++ b/designer/client/src/components/toolbars/activities/ActivitiesSearch.tsx @@ -0,0 +1,66 @@ +import React from "react"; +import { SearchInputWithIcon } from "../../themed/SearchInput"; +import i18next from "i18next"; +import { Box, IconButton, styled, Typography } from "@mui/material"; +import { isEmpty } from "lodash"; +import { SearchIcon } from "../../table/SearchFilter"; +import { ExpandLess, ExpandMore } from "@mui/icons-material"; + +export const StyledIconButton = styled(IconButton)(() => ({ + padding: 0, + "&:focus-within": { + outline: 0, + }, +})); + +interface Props { + handleSearch: (value: string) => void; + searchQuery: string; + selectedResult: number; + foundResults: string[]; + changeResult: (index: number) => void; + handleClearResults: () => void; +} + +export const ActivitiesSearch = ({ handleSearch, searchQuery, selectedResult, foundResults, changeResult, handleClearResults }: Props) => { + const areResults = foundResults.length > 0; + + return ( + <> + { + if (e.key === "Enter") { + changeResult(selectedResult + 1); + } + }} + > + + + + {searchQuery && ( + <> + ({ + color: "secondary", + mr: 1, + })} + > + {areResults ? `Result ${selectedResult + 1}/${foundResults.length}` : "Result 0"} + + changeResult(selectedResult - 1)}> + + + changeResult(selectedResult + 1)}> + + + + )} + + + ); +}; diff --git a/designer/client/src/components/toolbars/activities/ActivityPanelRowItem/ActivityItem.tsx b/designer/client/src/components/toolbars/activities/ActivityPanelRowItem/ActivityItem.tsx new file mode 100644 index 00000000000..d62c8a77b01 --- /dev/null +++ b/designer/client/src/components/toolbars/activities/ActivityPanelRowItem/ActivityItem.tsx @@ -0,0 +1,106 @@ +import React, { ForwardedRef, forwardRef } from "react"; +import { useSelector } from "react-redux"; +import { Box, styled, Typography } from "@mui/material"; +import { formatDateTime } from "../../../../common/DateUtils"; +import CommentContent from "../../../comment/CommentContent"; +import { createSelector } from "reselect"; +import { getFeatureSettings } from "../../../../reducers/selectors/settings"; +import { ItemActivity } from "../ActivitiesPanel"; +import { SearchHighlighter } from "../../creator/SearchHighlighter"; +import ActivityItemHeader from "./ActivityItemHeader"; +import { ActivityTypes } from "../types"; +import { getItemColors } from "../helpers/activityItemColors"; + +const StyledActivityRoot = styled("div")(({ theme }) => ({ + padding: theme.spacing(0.5, 1.25), +})); + +const StyledActivityContent = styled("div")<{ isActiveFound: boolean; isFound: boolean }>(({ theme, isActiveFound, isFound }) => ({ + ...getItemColors(theme, isActiveFound, isFound), + borderRadius: theme.spacing(0.5), +})); + +const StyledActivityBody = styled("div")(({ theme }) => ({ + display: "flex", + flexDirection: "column", + padding: theme.spacing(1, 0.5), + gap: theme.spacing(0.5), +})); + +const getCommentSettings = createSelector(getFeatureSettings, (f) => f.commentSettings || {}); + +export const ActivityItem = forwardRef( + ( + { activity, isRunning, searchQuery }: { activity: ItemActivity; isRunning: boolean; searchQuery: string }, + ref: ForwardedRef, + ) => { + const commentSettings = useSelector(getCommentSettings); + + const actionsWithVersionInfo: ActivityTypes[] = [ + "PERFORMED_SINGLE_EXECUTION", + "PERFORMED_SCHEDULED_EXECUTION", + "SCENARIO_DEPLOYED", + "SCENARIO_PAUSED", + "SCENARIO_CANCELED", + ]; + + const version = + activity.scenarioVersionId && actionsWithVersionInfo.includes(activity.type) && `Version: ${activity.scenarioVersionId}`; + + return ( + + + + + + + + {formatDateTime(activity.date)} + + + | + + + {activity.user} + + + + {version && {version}} + + + {activity?.comment?.content?.value && ( + + )} + {activity.additionalFields.map((additionalField, index) => { + const additionalFieldText = `${additionalField.name}: ${additionalField.value}`; + + return ( + + {additionalFieldText} + + ); + })} + + + + ); + }, +); + +ActivityItem.displayName = "ActivityItem"; diff --git a/designer/client/src/components/toolbars/activities/ActivityPanelRowItem/ActivityItemHeader.tsx b/designer/client/src/components/toolbars/activities/ActivityPanelRowItem/ActivityItemHeader.tsx new file mode 100644 index 00000000000..f0c617a7153 --- /dev/null +++ b/designer/client/src/components/toolbars/activities/ActivityPanelRowItem/ActivityItemHeader.tsx @@ -0,0 +1,223 @@ +import React, { PropsWithChildren, useCallback, useMemo } from "react"; +import { Button, styled, Typography } from "@mui/material"; +import { SearchHighlighter } from "../../creator/SearchHighlighter"; +import HttpService from "../../../../http/HttpService"; +import { ActionMetadata, ActivityAttachment, ActivityTypes } from "../types"; +import UrlIcon from "../../../UrlIcon"; +import { unsavedProcessChanges } from "../../../../common/DialogMessages"; +import { useDispatch, useSelector } from "react-redux"; +import { getProcessName, getProcessVersionId, getScenario, isSaveDisabled } from "../../../../reducers/selectors/graph"; +import { useWindows } from "../../../../windowManager"; +import { displayScenarioVersion } from "../../../../actions/nk"; +import { ItemActivity } from "../ActivitiesPanel"; +import { handleOpenCompareVersionDialog } from "../../../modals/CompareVersionsDialog"; +import { getHeaderColors } from "../helpers/activityItemColors"; + +const StyledHeaderIcon = styled(UrlIcon)(({ theme }) => ({ + width: "16px", + height: "16px", + color: theme.palette.primary.main, +})); + +const StyledHeaderActionIcon = styled(UrlIcon)(({ theme }) => ({ + width: "1.25rem", + height: "1.25rem", + marginLeft: "auto", + cursor: "pointer", + color: theme.palette.text.secondary, +})); + +const StyledActivityItemHeader = styled("div")<{ isHighlighted: boolean; isRunning: boolean; isActiveFound: boolean }>( + ({ theme, isHighlighted, isRunning, isActiveFound }) => ({ + display: "flex", + alignItems: "center", + padding: theme.spacing(0.5, 0.75), + borderRadius: theme.spacing(0.5), + ...getHeaderColors(theme, isHighlighted, isRunning, isActiveFound), + }), +); + +const HeaderActivity = ({ + activityAction, + scenarioVersionId, + activityAttachment, +}: { + activityAction: ActionMetadata; + scenarioVersionId: number; + activityAttachment: ActivityAttachment; +}) => { + const { open } = useWindows(); + const processName = useSelector(getProcessName); + const currentScenarioVersionId = useSelector(getProcessVersionId); + + switch (activityAction.id) { + case "compare": { + const isCurrentVersionIsTheSameAsVersionFromActivity = currentScenarioVersionId === scenarioVersionId; + if (isCurrentVersionIsTheSameAsVersionFromActivity) { + return null; + } + + return ( + open(handleOpenCompareVersionDialog(scenarioVersionId.toString()))} + key={activityAction.id} + src={activityAction.icon} + /> + ); + } + case "download_attachment": { + const attachmentStatus = activityAttachment.file.status; + + if (attachmentStatus === "DELETED") { + return null; + } + + const attachmentId = attachmentStatus === "AVAILABLE" && activityAttachment.file.id; + const attachmentName = activityAttachment.filename; + + const handleDownloadAttachment = () => HttpService.downloadAttachment(processName, attachmentId, attachmentName); + return ( + + ); + } + default: { + return null; + } + } +}; + +interface Props { + activity: ItemActivity; + isRunning: boolean; + isActiveFound: boolean; + isFound: boolean; + searchQuery: string; +} + +const WithOpenVersion = ({ + scenarioVersion, + isFound, + children, + activityType, +}: PropsWithChildren<{ + scenarioVersion: number; + isFound: boolean; + activityType: ActivityTypes; +}>) => { + const nothingToSave = useSelector(isSaveDisabled); + const scenario = useSelector(getScenario); + const { name } = scenario || {}; + const dispatch = useDispatch(); + const { confirm } = useWindows(); + + const doChangeVersion = useCallback( + (scenarioId: number) => { + dispatch(displayScenarioVersion(name, scenarioId)); + }, + [dispatch, name], + ); + + const changeVersion = useCallback( + (scenarioId: number) => + nothingToSave + ? doChangeVersion(scenarioId) + : confirm({ + text: unsavedProcessChanges(), + onConfirmCallback: (confirmed) => confirmed && doChangeVersion(scenarioId), + confirmText: "DISCARD", + denyText: "CANCEL", + }), + [confirm, doChangeVersion, nothingToSave], + ); + + return ( + + ); +}; + +const ActivityItemHeader = ({ activity, isRunning, isFound, isActiveFound, searchQuery }: Props) => { + const scenario = useSelector(getScenario); + const { processVersionId } = scenario || {}; + + const isHighlighted = ["SCENARIO_DEPLOYED", "SCENARIO_CANCELED"].includes(activity.type); + const openVersionEnable = + ["SCENARIO_MODIFIED", "SCENARIO_DEPLOYED"].includes(activity.type) && activity.scenarioVersionId !== processVersionId; + + const getHeaderTitle = useMemo(() => { + const text = activity.overrideDisplayableName || activity.activities.displayableName; + + const headerTitle = ( + ({ + color: theme.palette.text.primary, + overflow: "hidden", + textOverflow: "ellipsis", + textWrap: "noWrap", + padding: !openVersionEnable && theme.spacing(0, 1), + })} + > + {text} + + ); + + if (openVersionEnable) { + return ( + + {headerTitle} + + ); + } + + return headerTitle; + }, [ + activity.activities.displayableName, + activity.overrideDisplayableName, + activity.scenarioVersionId, + isFound, + openVersionEnable, + searchQuery, + ]); + + return ( + + + {getHeaderTitle} + {activity.actions.map((activityAction) => ( + + ))} + + ); +}; + +export default ActivityItemHeader; diff --git a/designer/client/src/components/toolbars/activities/ActivityPanelRowItem/DateItem.tsx b/designer/client/src/components/toolbars/activities/ActivityPanelRowItem/DateItem.tsx new file mode 100644 index 00000000000..c2d2db1f1db --- /dev/null +++ b/designer/client/src/components/toolbars/activities/ActivityPanelRowItem/DateItem.tsx @@ -0,0 +1,24 @@ +import React, { ForwardedRef, forwardRef } from "react"; +import { Box, Divider, Typography } from "@mui/material"; +import { formatUiDate } from "../helpers/date"; +import { DateActivity } from "../ActivitiesPanel"; + +interface Props { + activity: DateActivity; + isFirstDateItem: boolean; +} +export const DateItem = forwardRef(({ activity, isFirstDateItem }: Props, ref: ForwardedRef) => { + return ( + + ({ flex: 1, backgroundColor: theme.palette.common.white, mr: 1 })} /> + + {Array.isArray(activity.value) + ? `${formatUiDate(activity.value[0])} - ${formatUiDate(activity.value[1])}` + : formatUiDate(activity.value)} + + ({ flex: 1, backgroundColor: theme.palette.common.white, ml: 1 })} /> + + ); +}); + +DateItem.displayName = "DateItem"; diff --git a/designer/client/src/components/toolbars/activities/ActivityPanelRowItem/ToggleButtonItem.tsx b/designer/client/src/components/toolbars/activities/ActivityPanelRowItem/ToggleButtonItem.tsx new file mode 100644 index 00000000000..89326e0aca7 --- /dev/null +++ b/designer/client/src/components/toolbars/activities/ActivityPanelRowItem/ToggleButtonItem.tsx @@ -0,0 +1,33 @@ +import React, { PropsWithChildren } from "react"; +import { Button, Divider, styled } from "@mui/material"; + +export const ToggleItemsRoot = styled("div")(({ theme }) => ({ + paddingLeft: theme.spacing(1), + display: "flex", + alignItems: "center", + justifyContent: "flex-end", +})); + +export const ToggleItemsButton = styled(Button)(({ theme }) => ({ + textTransform: "lowercase", + fontSize: theme.typography.caption.fontSize, + fontWeight: theme.typography.caption.fontWeight, + paddingTop: 0, + paddingBottom: 0, +})); + +interface Props { + handleHideRow(): void; +} + +export const ToggleButtonItem = ({ handleHideRow, children }: PropsWithChildren) => { + return ( + + ({ flex: 1, backgroundColor: theme.palette.primary.main, borderBottomWidth: 0.5 })} + /> + {children} + + ); +}; diff --git a/designer/client/src/components/toolbars/activities/ActivityPanelRowItem/index.ts b/designer/client/src/components/toolbars/activities/ActivityPanelRowItem/index.ts new file mode 100644 index 00000000000..3293bda3a4a --- /dev/null +++ b/designer/client/src/components/toolbars/activities/ActivityPanelRowItem/index.ts @@ -0,0 +1,3 @@ +export * from "./ActivityItem"; +export * from "./DateItem"; +export * from "./ToggleButtonItem"; diff --git a/designer/client/src/components/toolbars/activities/helpers/activityItemColors.ts b/designer/client/src/components/toolbars/activities/helpers/activityItemColors.ts new file mode 100644 index 00000000000..d0192a16818 --- /dev/null +++ b/designer/client/src/components/toolbars/activities/helpers/activityItemColors.ts @@ -0,0 +1,69 @@ +import { Theme } from "@mui/material"; +import { blend } from "@mui/system"; +import { getBorderColor } from "../../../../containers/theme/helpers"; + +const defaultBorder = (theme: Theme) => `0.5px solid ${getBorderColor(theme)}`; +const activeBorder = (theme: Theme) => `0.5px solid ${blend(theme.palette.background.paper, theme.palette.primary.main, 0.4)}`; + +const runningActiveFoundHeaderBackground = (theme: Theme) => blend(theme.palette.background.paper, theme.palette.primary.main, 0.3); +const highlightedHeaderBackground = (theme: Theme) => blend(theme.palette.background.paper, theme.palette.primary.main, 0.05); +const highlightedActiveFoundHeaderBackground = (theme: Theme) => blend(theme.palette.background.paper, theme.palette.primary.main, 0.2); +const runningHeaderBackground = (theme: Theme) => blend(theme.palette.background.paper, theme.palette.primary.main, 0.2); +const activeFoundItemBackground = (theme: Theme) => blend(theme.palette.background.paper, theme.palette.primary.main, 0.2); +const foundItemBackground = (theme: Theme) => blend(theme.palette.background.paper, theme.palette.primary.main, 0.08); + +export const getHeaderColors = (theme: Theme, isHighlighted: boolean, isRunning: boolean, isActiveFound: boolean) => { + if (isRunning && isActiveFound) { + return { + backgroundColor: runningActiveFoundHeaderBackground(theme), + border: activeBorder(theme), + }; + } + + if (isHighlighted && isActiveFound) { + return { + backgroundColor: highlightedActiveFoundHeaderBackground(theme), + border: activeBorder(theme), + }; + } + + if (isRunning) { + return { + backgroundColor: runningHeaderBackground(theme), + border: defaultBorder(theme), + }; + } + + if (isHighlighted) { + return { + backgroundColor: highlightedHeaderBackground(theme), + border: defaultBorder(theme), + }; + } + + return { + backgroundColor: undefined, + border: "none", + }; +}; + +export const getItemColors = (theme: Theme, isActiveFound: boolean, isFound: boolean) => { + if (isActiveFound) { + return { + backgroundColor: activeFoundItemBackground(theme), + border: activeBorder(theme), + }; + } + + if (isFound) { + return { + backgroundColor: foundItemBackground(theme), + border: activeBorder(theme), + }; + } + + return { + backgroundColor: undefined, + border: "none", + }; +}; diff --git a/designer/client/src/components/toolbars/activities/helpers/date.ts b/designer/client/src/components/toolbars/activities/helpers/date.ts new file mode 100644 index 00000000000..18dc0d565d0 --- /dev/null +++ b/designer/client/src/components/toolbars/activities/helpers/date.ts @@ -0,0 +1,19 @@ +import moment from "moment/moment"; +import { Moment } from "moment"; + +export function formatUiDate(date: string) { + const now = moment(); // Current date and time + const inputDate = moment(date); // Date to be formatted + + if (inputDate.isSame(now, "day")) { + return "Today"; + } else if (inputDate.isSame(moment().subtract(1, "days"), "day")) { + return "Yesterday"; + } else { + return date; + } +} + +export function formatDate(date: string | Moment) { + return moment(date).format("YYYY-MM-DD"); +} diff --git a/designer/client/src/components/toolbars/activities/helpers/extendActivitiesWithUIData.test.ts b/designer/client/src/components/toolbars/activities/helpers/extendActivitiesWithUIData.test.ts new file mode 100644 index 00000000000..4b3fd1c865e --- /dev/null +++ b/designer/client/src/components/toolbars/activities/helpers/extendActivitiesWithUIData.test.ts @@ -0,0 +1,1040 @@ +import { extendActivitiesWithUIData } from "./extendActivitiesWithUIData"; +import { mergeActivityDataWithMetadata } from "./mergeActivityDataWithMetadata"; +import { sampleMetadataResponse } from "../../../../../__mocks__/fixtures/sampleMetadataResponse"; +import { sampleActivitiesResponse, sampleActivitiesWithRepetiveResponse } from "../../../../../__mocks__/fixtures/sampleActivitiesResponse"; +import { ActivitiesResponse } from "../types"; + +describe("extendActivitiesWithUIData", () => { + it("should render activities with metadata when items are not grouped", () => { + const activitiesDataWithMetadata = mergeActivityDataWithMetadata(sampleActivitiesResponse, sampleMetadataResponse); + + const expected = [ + { + uiType: "date", + value: "2024-09-26", + }, + { + actions: [], + activities: { + displayableName: "Scenario unarchived", + icon: "/assets/process/success.svg", + supportedActions: [], + type: "SCENARIO_UNARCHIVED", + }, + additionalFields: [], + attachment: null, + comment: null, + date: "2024-09-26T10:26:42.999982Z", + id: "1ba9d6c6-c61a-42f5-8311-4d0f287867f7", + isActiveFound: false, + isFound: false, + isHidden: false, + overrideDisplayableName: null, + overrideIcon: null, + overrideSupportedActions: null, + scenarioVersionId: 6, + type: "SCENARIO_UNARCHIVED", + uiType: "item", + user: "admin", + }, + { + actions: [], + activities: { + displayableName: "Scenario archived", + icon: "/assets/process/archived.svg", + supportedActions: [], + type: "SCENARIO_ARCHIVED", + }, + additionalFields: [], + attachment: null, + comment: null, + date: "2024-09-26T10:26:28.293423Z", + id: "ec245dc5-f84c-4880-8727-b8c999c35a3f", + isActiveFound: false, + isFound: false, + isHidden: false, + overrideDisplayableName: null, + overrideIcon: null, + overrideSupportedActions: null, + scenarioVersionId: 6, + type: "SCENARIO_ARCHIVED", + uiType: "item", + user: "admin", + }, + { + uiType: "date", + value: "2024-09-25", + }, + { + actions: [ + { + displayableName: "Delete", + icon: "/assets/states/error.svg", + id: "delete_comment", + }, + { + displayableName: "Edit", + icon: "/assets/states/error.svg", + id: "edit_comment", + }, + { + displayableName: "Compare", + icon: "/assets/states/error.svg", + id: "compare", + }, + ], + activities: { + displayableName: "New version saved", + icon: "/assets/states/success.svg", + supportedActions: ["delete_comment", "edit_comment", "compare"], + type: "SCENARIO_MODIFIED", + }, + additionalFields: [], + attachment: null, + comment: { + content: { + status: "AVAILABLE", + value: "122", + }, + lastModifiedAt: "2024-09-25T09:55:04.309Z", + lastModifiedBy: "admin", + }, + date: "2024-09-25T09:55:04.309Z", + id: "15c0e8a9-d1c5-47dd-bf28-8a08217fff5b", + isActiveFound: false, + isFound: false, + isHidden: false, + overrideDisplayableName: "Version 4 saved", + overrideIcon: null, + overrideSupportedActions: null, + scenarioVersionId: 4, + type: "SCENARIO_MODIFIED", + uiType: "item", + user: "admin", + }, + { + uiType: "date", + value: "2024-09-22", + }, + { + actions: [ + { + displayableName: "Delete", + icon: "/assets/states/error.svg", + id: "delete_comment", + }, + { + displayableName: "Edit", + icon: "/assets/states/error.svg", + id: "edit_comment", + }, + { + displayableName: "Compare", + icon: "/assets/states/error.svg", + id: "compare", + }, + ], + activities: { + displayableName: "New version saved", + icon: "/assets/states/success.svg", + supportedActions: ["delete_comment", "edit_comment", "compare"], + type: "SCENARIO_MODIFIED", + }, + additionalFields: [], + attachment: null, + comment: { + content: { + status: "AVAILABLE", + value: "tests save", + }, + lastModifiedAt: "2024-09-22T09:53:40.875721Z", + lastModifiedBy: "admin", + }, + date: "2024-09-22T09:53:40.875721Z", + id: "a2576467-9bf9-4a92-b71f-be95b84d59f6", + isActiveFound: false, + isFound: false, + isHidden: false, + overrideDisplayableName: "Version 3 saved", + overrideIcon: null, + overrideSupportedActions: null, + scenarioVersionId: 3, + type: "SCENARIO_MODIFIED", + uiType: "item", + user: "admin", + }, + { + uiType: "date", + value: "2022-01-22", + }, + { + actions: [ + { + displayableName: "Download", + icon: "/assets/states/error.svg", + id: "download_attachment", + }, + { + displayableName: "Delete", + icon: "/assets/states/error.svg", + id: "delete_attachment", + }, + ], + activities: { + displayableName: "Attachment", + icon: "/assets/states/success.svg", + supportedActions: ["download_attachment", "delete_attachment"], + type: "ATTACHMENT_ADDED", + }, + additionalFields: [], + attachment: { + file: { + id: 1, + status: "AVAILABLE", + }, + filename: "324.log", + lastModifiedAt: "2022-01-22T06:09:44.313094Z", + lastModifiedBy: "admin", + }, + comment: null, + date: "2022-01-22T06:09:44.313094Z", + id: "48f383f9-ccdd-46b6-9b33-5f6693165755", + isActiveFound: false, + isFound: false, + isHidden: false, + overrideDisplayableName: null, + overrideIcon: null, + overrideSupportedActions: null, + scenarioVersionId: 1, + type: "ATTACHMENT_ADDED", + uiType: "item", + user: "admin", + }, + ]; + + expect(extendActivitiesWithUIData(activitiesDataWithMetadata)).toMatchObject(expected); + }); + it("should render activities with metadata and date range when items are grouped in the date range", () => { + const activitiesDataWithMetadata = mergeActivityDataWithMetadata(sampleActivitiesWithRepetiveResponse, sampleMetadataResponse); + + const expected = [ + { + uiType: "date", + value: ["2024-09-25", "2024-09-26"], + }, + { + actions: [], + activities: { + displayableName: "Scenario unarchived", + icon: "/assets/process/success.svg", + supportedActions: [], + type: "SCENARIO_UNARCHIVED", + }, + additionalFields: [], + attachment: null, + comment: null, + date: "2024-09-26T10:26:42.999982Z", + id: "1ba9d6c6-c61a-42f5-8311-4d0f287867f7", + isActiveFound: false, + isFound: false, + isHidden: false, + overrideDisplayableName: null, + overrideIcon: null, + overrideSupportedActions: null, + scenarioVersionId: 6, + type: "SCENARIO_UNARCHIVED", + uiType: "item", + user: "admin", + }, + { + actions: [], + activities: { + displayableName: "Scenario archived", + icon: "/assets/process/archived.svg", + supportedActions: [], + type: "SCENARIO_ARCHIVED", + }, + additionalFields: [], + attachment: null, + comment: null, + date: "2024-09-26T10:26:28.293423Z", + id: "ec245dc5-f84c-4880-8727-b8c999c35a3f", + isActiveFound: false, + isFound: false, + isHidden: false, + overrideDisplayableName: null, + overrideIcon: null, + overrideSupportedActions: null, + scenarioVersionId: 6, + type: "SCENARIO_ARCHIVED", + uiType: "item", + user: "admin", + }, + { + actions: [ + { + displayableName: "Delete", + icon: "/assets/states/error.svg", + id: "delete_comment", + }, + { + displayableName: "Edit", + icon: "/assets/states/error.svg", + id: "edit_comment", + }, + { + displayableName: "Compare", + icon: "/assets/states/error.svg", + id: "compare", + }, + ], + activities: { + displayableName: "New version saved", + icon: "/assets/states/success.svg", + supportedActions: ["delete_comment", "edit_comment", "compare"], + type: "SCENARIO_MODIFIED", + }, + additionalFields: [], + attachment: null, + comment: { + content: { + status: "AVAILABLE", + value: "Scenario migrated from local by admin", + }, + lastModifiedAt: "2024-09-26T10:22:45.494475Z", + lastModifiedBy: "admin", + }, + date: "2024-09-26T10:22:45.494475Z", + id: "4e8c9f40-7c2c-434e-8528-68dccf66d5d1", + isActiveFound: false, + isFound: false, + isHidden: false, + overrideDisplayableName: "Version 6 saved", + overrideIcon: null, + overrideSupportedActions: null, + scenarioVersionId: 6, + type: "SCENARIO_MODIFIED", + uiType: "item", + user: "admin", + }, + { + actions: [ + { + displayableName: "Delete", + icon: "/assets/states/error.svg", + id: "delete_comment", + }, + { + displayableName: "Edit", + icon: "/assets/states/error.svg", + id: "edit_comment", + }, + { + displayableName: "Compare", + icon: "/assets/states/error.svg", + id: "compare", + }, + ], + activities: { + displayableName: "New version saved", + icon: "/assets/states/success.svg", + supportedActions: ["delete_comment", "edit_comment", "compare"], + type: "SCENARIO_MODIFIED", + }, + additionalFields: [], + attachment: null, + comment: { + content: { + status: "AVAILABLE", + value: "Scenario migrated from local by admin", + }, + lastModifiedAt: "2024-09-26T10:13:11.571064Z", + lastModifiedBy: "admin", + }, + date: "2024-09-26T10:13:11.571064Z", + id: "35fa34fe-7f07-4b50-98fb-58d19d5b7704", + isActiveFound: false, + isFound: false, + isHidden: true, + overrideDisplayableName: "Version 6 saved", + overrideIcon: null, + overrideSupportedActions: null, + scenarioVersionId: 6, + type: "SCENARIO_MODIFIED", + uiType: "item", + user: "admin", + }, + { + actions: [ + { + displayableName: "Delete", + icon: "/assets/states/error.svg", + id: "delete_comment", + }, + { + displayableName: "Edit", + icon: "/assets/states/error.svg", + id: "edit_comment", + }, + { + displayableName: "Compare", + icon: "/assets/states/error.svg", + id: "compare", + }, + ], + activities: { + displayableName: "New version saved", + icon: "/assets/states/success.svg", + supportedActions: ["delete_comment", "edit_comment", "compare"], + type: "SCENARIO_MODIFIED", + }, + additionalFields: [], + attachment: null, + comment: { + content: { + status: "AVAILABLE", + value: "Scenario migrated from local by admin", + }, + lastModifiedAt: "2024-09-26T10:11:29.657265Z", + lastModifiedBy: "admin", + }, + date: "2024-09-26T10:11:29.657265Z", + id: "c0745a74-937c-4682-b9c8-cb94f619eb14", + isActiveFound: false, + isFound: false, + isHidden: true, + overrideDisplayableName: "Version 6 saved", + overrideIcon: null, + overrideSupportedActions: null, + scenarioVersionId: 6, + type: "SCENARIO_MODIFIED", + uiType: "item", + user: "admin", + }, + { + actions: [ + { + displayableName: "Delete", + icon: "/assets/states/error.svg", + id: "delete_comment", + }, + { + displayableName: "Edit", + icon: "/assets/states/error.svg", + id: "edit_comment", + }, + { + displayableName: "Compare", + icon: "/assets/states/error.svg", + id: "compare", + }, + ], + activities: { + displayableName: "New version saved", + icon: "/assets/states/success.svg", + supportedActions: ["delete_comment", "edit_comment", "compare"], + type: "SCENARIO_MODIFIED", + }, + additionalFields: [], + attachment: null, + comment: { + content: { + status: "AVAILABLE", + value: "test12345", + }, + lastModifiedAt: "2024-09-26T10:08:00.895385Z", + lastModifiedBy: "admin", + }, + date: "2024-09-26T10:08:00.895385Z", + id: "2cf1f252-3be1-413f-ad62-ccb311683744", + isActiveFound: false, + isFound: false, + isHidden: true, + overrideDisplayableName: "Version 6 saved", + overrideIcon: null, + overrideSupportedActions: null, + scenarioVersionId: 6, + type: "SCENARIO_MODIFIED", + uiType: "item", + user: "admin", + }, + { + actions: [ + { + displayableName: "Delete", + icon: "/assets/states/error.svg", + id: "delete_comment", + }, + { + displayableName: "Edit", + icon: "/assets/states/error.svg", + id: "edit_comment", + }, + { + displayableName: "Compare", + icon: "/assets/states/error.svg", + id: "compare", + }, + ], + activities: { + displayableName: "New version saved", + icon: "/assets/states/success.svg", + supportedActions: ["delete_comment", "edit_comment", "compare"], + type: "SCENARIO_MODIFIED", + }, + additionalFields: [], + attachment: null, + comment: { + content: { + status: "AVAILABLE", + value: "test", + }, + lastModifiedAt: "2024-09-26T07:17:19.892192Z", + lastModifiedBy: "admin", + }, + date: "2024-09-26T07:17:19.892192Z", + id: "0182764d-c568-403e-88ea-24942a091af5", + isActiveFound: false, + isFound: false, + isHidden: true, + overrideDisplayableName: "Version 5 saved", + overrideIcon: null, + overrideSupportedActions: null, + scenarioVersionId: 5, + type: "SCENARIO_MODIFIED", + uiType: "item", + user: "admin", + }, + { + actions: [ + { + displayableName: "Delete", + icon: "/assets/states/error.svg", + id: "delete_comment", + }, + { + displayableName: "Edit", + icon: "/assets/states/error.svg", + id: "edit_comment", + }, + { + displayableName: "Compare", + icon: "/assets/states/error.svg", + id: "compare", + }, + ], + activities: { + displayableName: "New version saved", + icon: "/assets/states/success.svg", + supportedActions: ["delete_comment", "edit_comment", "compare"], + type: "SCENARIO_MODIFIED", + }, + additionalFields: [], + attachment: null, + comment: { + content: { + status: "AVAILABLE", + value: "122", + }, + lastModifiedAt: "2024-09-25T09:55:04.309Z", + lastModifiedBy: "admin", + }, + date: "2024-09-25T09:55:04.309Z", + id: "15c0e8a9-d1c5-47dd-bf28-8a08217fff5b", + isActiveFound: false, + isFound: false, + isHidden: true, + overrideDisplayableName: "Version 4 saved", + overrideIcon: null, + overrideSupportedActions: null, + scenarioVersionId: 4, + type: "SCENARIO_MODIFIED", + uiType: "item", + user: "admin", + }, + { + actions: [ + { + displayableName: "Delete", + icon: "/assets/states/error.svg", + id: "delete_comment", + }, + { + displayableName: "Edit", + icon: "/assets/states/error.svg", + id: "edit_comment", + }, + { + displayableName: "Compare", + icon: "/assets/states/error.svg", + id: "compare", + }, + ], + activities: { + displayableName: "New version saved", + icon: "/assets/states/success.svg", + supportedActions: ["delete_comment", "edit_comment", "compare"], + type: "SCENARIO_MODIFIED", + }, + additionalFields: [], + attachment: null, + comment: { + content: { + status: "AVAILABLE", + value: "tests save", + }, + lastModifiedAt: "2024-09-25T09:53:40.875721Z", + lastModifiedBy: "admin", + }, + date: "2024-09-25T09:53:40.875721Z", + id: "a2576467-9bf9-4a92-b71f-be95b84d59f6", + isActiveFound: false, + isFound: false, + isHidden: true, + overrideDisplayableName: "Version 3 saved", + overrideIcon: null, + overrideSupportedActions: null, + scenarioVersionId: 3, + type: "SCENARIO_MODIFIED", + uiType: "item", + user: "admin", + }, + { + isClicked: false, + sameItemOccurrence: 6, + uiType: "toggleItemsButton", + }, + { + actions: [ + { + displayableName: "Download", + icon: "/assets/states/error.svg", + id: "download_attachment", + }, + { + displayableName: "Delete", + icon: "/assets/states/error.svg", + id: "delete_attachment", + }, + ], + activities: { + displayableName: "Attachment", + icon: "/assets/states/success.svg", + supportedActions: ["download_attachment", "delete_attachment"], + type: "ATTACHMENT_ADDED", + }, + additionalFields: [], + attachment: { + file: { + id: 1, + status: "AVAILABLE", + }, + filename: "324.log", + lastModifiedAt: "2024-09-25T06:09:44.313094Z", + lastModifiedBy: "admin", + }, + comment: null, + date: "2024-09-25T06:09:44.313094Z", + id: "48f383f9-ccdd-46b6-9b33-5f6693165755", + isActiveFound: false, + isFound: false, + isHidden: false, + overrideDisplayableName: null, + overrideIcon: null, + overrideSupportedActions: null, + scenarioVersionId: 1, + type: "ATTACHMENT_ADDED", + uiType: "item", + user: "admin", + }, + { + actions: [ + { + displayableName: "Delete", + icon: "/assets/states/error.svg", + id: "delete_comment", + }, + { + displayableName: "Edit", + icon: "/assets/states/error.svg", + id: "edit_comment", + }, + ], + activities: { + displayableName: "Comment", + icon: "/assets/states/success.svg", + supportedActions: ["delete_comment", "edit_comment"], + type: "COMMENT_ADDED", + }, + additionalFields: [], + attachment: null, + comment: { + content: { + status: "AVAILABLE", + value: "test", + }, + lastModifiedAt: "2024-09-25T06:09:03.470213Z", + lastModifiedBy: "admin", + }, + date: "2024-09-25T06:09:03.470213Z", + id: "56a7dd49-778b-468b-8e33-99bd176218aa", + isActiveFound: false, + isFound: false, + isHidden: false, + overrideDisplayableName: null, + overrideIcon: null, + overrideSupportedActions: null, + scenarioVersionId: 1, + type: "COMMENT_ADDED", + uiType: "item", + user: "admin", + }, + ]; + + expect(extendActivitiesWithUIData(activitiesDataWithMetadata)).toMatchObject(expected); + }); + + it("should render activities with metadata without date range when items are grouped in the same day", () => { + const activities: ActivitiesResponse["activities"] = [ + { + id: "0a309251-9b98-4712-9d71-d5f31f0dd218", + user: "admin", + date: "2024-10-02T07:52:57.294963Z", + scenarioVersionId: 13, + comment: { + content: { + value: "test", + status: "AVAILABLE", + }, + lastModifiedBy: "admin", + lastModifiedAt: "2024-10-02T07:52:57.294963Z", + }, + additionalFields: [], + type: "COMMENT_ADDED", + }, + { + id: "72ec0519-2b1e-4fa9-a589-ca41372f4b1d", + user: "admin", + date: "2024-10-02T08:41:02.637105Z", + scenarioVersionId: 13, + comment: { + content: { + value: "34", + status: "AVAILABLE", + }, + lastModifiedBy: "admin", + lastModifiedAt: "2024-10-02T08:41:02.637105Z", + }, + additionalFields: [], + type: "COMMENT_ADDED", + }, + { + id: "c18a2e19-7a70-4150-bb12-eff61d21a2ed", + user: "admin", + date: "2024-10-02T08:48:39.822978Z", + scenarioVersionId: 13, + comment: { + content: { + value: "1", + status: "AVAILABLE", + }, + lastModifiedBy: "admin", + lastModifiedAt: "2024-10-02T08:48:39.822978Z", + }, + additionalFields: [], + type: "COMMENT_ADDED", + }, + { + id: "c03cae2e-95e6-44ac-8d5d-6d253a8f669a", + user: "admin", + date: "2024-10-02T08:48:42.599853Z", + scenarioVersionId: 13, + comment: { + content: { + value: "2", + status: "AVAILABLE", + }, + lastModifiedBy: "admin", + lastModifiedAt: "2024-10-02T08:48:42.599853Z", + }, + additionalFields: [], + type: "COMMENT_ADDED", + }, + { + id: "61f1c933-1c80-427f-b43d-39f5ae7a2b84", + user: "admin", + date: "2024-10-02T08:48:44.563111Z", + scenarioVersionId: 13, + comment: { + content: { + value: "3", + status: "AVAILABLE", + }, + lastModifiedBy: "admin", + lastModifiedAt: "2024-10-02T08:48:44.563111Z", + }, + additionalFields: [], + type: "COMMENT_ADDED", + }, + { + id: "0bb4b537-6d17-4f65-999f-1ab3255621b6", + user: "admin", + date: "2024-10-02T08:48:46.816130Z", + scenarioVersionId: 13, + comment: { + content: { + value: "4", + status: "AVAILABLE", + }, + lastModifiedBy: "admin", + lastModifiedAt: "2024-10-02T08:48:46.816130Z", + }, + additionalFields: [], + type: "COMMENT_ADDED", + }, + ]; + const activitiesDataWithMetadata = mergeActivityDataWithMetadata(activities, sampleMetadataResponse); + + const expected = [ + { + uiType: "date", + value: "2024-10-02", + }, + { + actions: [ + { + displayableName: "Delete", + icon: "/assets/states/error.svg", + id: "delete_comment", + }, + { + displayableName: "Edit", + icon: "/assets/states/error.svg", + id: "edit_comment", + }, + ], + activities: { + displayableName: "Comment", + icon: "/assets/states/success.svg", + supportedActions: ["delete_comment", "edit_comment"], + type: "COMMENT_ADDED", + }, + additionalFields: [], + comment: { + content: { + status: "AVAILABLE", + value: "4", + }, + lastModifiedAt: "2024-10-02T08:48:46.816130Z", + lastModifiedBy: "admin", + }, + date: "2024-10-02T08:48:46.816130Z", + id: "0bb4b537-6d17-4f65-999f-1ab3255621b6", + isActiveFound: false, + isFound: false, + isHidden: false, + scenarioVersionId: 13, + type: "COMMENT_ADDED", + uiType: "item", + user: "admin", + }, + { + actions: [ + { + displayableName: "Delete", + icon: "/assets/states/error.svg", + id: "delete_comment", + }, + { + displayableName: "Edit", + icon: "/assets/states/error.svg", + id: "edit_comment", + }, + ], + activities: { + displayableName: "Comment", + icon: "/assets/states/success.svg", + supportedActions: ["delete_comment", "edit_comment"], + type: "COMMENT_ADDED", + }, + additionalFields: [], + comment: { + content: { + status: "AVAILABLE", + value: "3", + }, + lastModifiedAt: "2024-10-02T08:48:44.563111Z", + lastModifiedBy: "admin", + }, + date: "2024-10-02T08:48:44.563111Z", + id: "61f1c933-1c80-427f-b43d-39f5ae7a2b84", + isActiveFound: false, + isFound: false, + isHidden: true, + scenarioVersionId: 13, + type: "COMMENT_ADDED", + uiType: "item", + user: "admin", + }, + { + actions: [ + { + displayableName: "Delete", + icon: "/assets/states/error.svg", + id: "delete_comment", + }, + { + displayableName: "Edit", + icon: "/assets/states/error.svg", + id: "edit_comment", + }, + ], + activities: { + displayableName: "Comment", + icon: "/assets/states/success.svg", + supportedActions: ["delete_comment", "edit_comment"], + type: "COMMENT_ADDED", + }, + additionalFields: [], + comment: { + content: { + status: "AVAILABLE", + value: "2", + }, + lastModifiedAt: "2024-10-02T08:48:42.599853Z", + lastModifiedBy: "admin", + }, + date: "2024-10-02T08:48:42.599853Z", + id: "c03cae2e-95e6-44ac-8d5d-6d253a8f669a", + isActiveFound: false, + isFound: false, + isHidden: true, + scenarioVersionId: 13, + type: "COMMENT_ADDED", + uiType: "item", + user: "admin", + }, + { + actions: [ + { + displayableName: "Delete", + icon: "/assets/states/error.svg", + id: "delete_comment", + }, + { + displayableName: "Edit", + icon: "/assets/states/error.svg", + id: "edit_comment", + }, + ], + activities: { + displayableName: "Comment", + icon: "/assets/states/success.svg", + supportedActions: ["delete_comment", "edit_comment"], + type: "COMMENT_ADDED", + }, + additionalFields: [], + comment: { + content: { + status: "AVAILABLE", + value: "1", + }, + lastModifiedAt: "2024-10-02T08:48:39.822978Z", + lastModifiedBy: "admin", + }, + date: "2024-10-02T08:48:39.822978Z", + id: "c18a2e19-7a70-4150-bb12-eff61d21a2ed", + isActiveFound: false, + isFound: false, + isHidden: true, + scenarioVersionId: 13, + type: "COMMENT_ADDED", + uiType: "item", + user: "admin", + }, + { + actions: [ + { + displayableName: "Delete", + icon: "/assets/states/error.svg", + id: "delete_comment", + }, + { + displayableName: "Edit", + icon: "/assets/states/error.svg", + id: "edit_comment", + }, + ], + activities: { + displayableName: "Comment", + icon: "/assets/states/success.svg", + supportedActions: ["delete_comment", "edit_comment"], + type: "COMMENT_ADDED", + }, + additionalFields: [], + comment: { + content: { + status: "AVAILABLE", + value: "34", + }, + lastModifiedAt: "2024-10-02T08:41:02.637105Z", + lastModifiedBy: "admin", + }, + date: "2024-10-02T08:41:02.637105Z", + id: "72ec0519-2b1e-4fa9-a589-ca41372f4b1d", + isActiveFound: false, + isFound: false, + isHidden: true, + scenarioVersionId: 13, + type: "COMMENT_ADDED", + uiType: "item", + user: "admin", + }, + { + actions: [ + { + displayableName: "Delete", + icon: "/assets/states/error.svg", + id: "delete_comment", + }, + { + displayableName: "Edit", + icon: "/assets/states/error.svg", + id: "edit_comment", + }, + ], + activities: { + displayableName: "Comment", + icon: "/assets/states/success.svg", + supportedActions: ["delete_comment", "edit_comment"], + type: "COMMENT_ADDED", + }, + additionalFields: [], + comment: { + content: { + status: "AVAILABLE", + value: "test", + }, + lastModifiedAt: "2024-10-02T07:52:57.294963Z", + lastModifiedBy: "admin", + }, + date: "2024-10-02T07:52:57.294963Z", + id: "0a309251-9b98-4712-9d71-d5f31f0dd218", + isActiveFound: false, + isFound: false, + isHidden: true, + scenarioVersionId: 13, + type: "COMMENT_ADDED", + uiType: "item", + user: "admin", + }, + { + isClicked: false, + sameItemOccurrence: 5, + uiType: "toggleItemsButton", + }, + ]; + + expect(extendActivitiesWithUIData(activitiesDataWithMetadata)).toMatchObject(expected); + }); +}); diff --git a/designer/client/src/components/toolbars/activities/helpers/extendActivitiesWithUIData.ts b/designer/client/src/components/toolbars/activities/helpers/extendActivitiesWithUIData.ts new file mode 100644 index 00000000000..f9d231d7b8e --- /dev/null +++ b/designer/client/src/components/toolbars/activities/helpers/extendActivitiesWithUIData.ts @@ -0,0 +1,141 @@ +import moment from "moment/moment"; +import { v4 as uuid4 } from "uuid"; +import { Activity, ButtonActivity, DateActivity, UIActivity } from "../ActivitiesPanel"; +import { formatDate } from "./date"; + +const getLatestDateItem = (uiActivities: UIActivity[]) => { + let previousDateItem: DateActivity | undefined; + + for (let prev = uiActivities.length; prev >= 0; prev--) { + const item = uiActivities[prev]; + if (item?.uiType === "date") { + previousDateItem = item; + break; + } + } + + return previousDateItem; +}; + +export const extendActivitiesWithUIData = (activitiesDataWithMetadata: Activity[]) => { + const uiActivities: UIActivity[] = []; + const maxAllowedTypesDuplicatesToItemsHide = 2; + + const recursiveDateLabelDesignation = ( + currentActivity: Activity, + index: number, + occurrences: string[] = [], + iteration = 0, + ): DateActivity | undefined => { + const nextActivity = activitiesDataWithMetadata[index + 1 + iteration]; + const latestDateItem = getLatestDateItem(uiActivities); + const currentAndNextActivityTypeAreTheSame = currentActivity.type === nextActivity?.type; + + if (latestDateItem?.value?.includes?.(formatDate(currentActivity.date))) { + return undefined; + } + + const isDateRangeInOccurrences = occurrences.every((occurrence) => occurrence === occurrences[0]); + const shouldAddDateRangeElement = + occurrences.length > maxAllowedTypesDuplicatesToItemsHide && !currentAndNextActivityTypeAreTheSame && !isDateRangeInOccurrences; + + if (shouldAddDateRangeElement) { + const dates = occurrences.map((occurrence) => moment(occurrence)); + return { + uiGeneratedId: uuid4(), + uiType: "date", + value: [formatDate(moment.min(dates)), formatDate(moment.max(dates))], + }; + } + + const currentAndNextActivityDateAreTheSame = formatDate(currentActivity.date) === (nextActivity && formatDate(nextActivity.date)); + + if (currentAndNextActivityTypeAreTheSame || currentAndNextActivityDateAreTheSame) { + iteration++; + + if (currentAndNextActivityTypeAreTheSame) { + occurrences.push(formatDate(currentActivity.date)); + const isNextActivityLastOfType = activitiesDataWithMetadata[index + 1 + iteration]?.type !== nextActivity.type; + if (isNextActivityLastOfType) { + occurrences.push(formatDate(nextActivity.date)); + } + } else { + occurrences = []; + } + + return recursiveDateLabelDesignation(nextActivity, index, occurrences, iteration); + } + + const initialActivity = activitiesDataWithMetadata[index]; + + const isDateElementPreviouslyAdded = latestDateItem?.value?.includes?.(formatDate(initialActivity.date)); + if (!isDateElementPreviouslyAdded) { + return { + uiGeneratedId: uuid4(), + uiType: "date", + value: formatDate(initialActivity.date), + }; + } + + return undefined; + }; + + const recursiveToggleItemsButtonDesignation = (activity: Activity, index: number, occurrences = 0): ButtonActivity | undefined => { + const previousActivityIndex = index - 1 - occurrences; + const previousActivity = activitiesDataWithMetadata[previousActivityIndex]; + const nextActivity = activitiesDataWithMetadata[index + 1]; + + if ( + occurrences >= maxAllowedTypesDuplicatesToItemsHide && + activity.type !== previousActivity?.type && + activity.type !== nextActivity?.type + ) { + return { + uiGeneratedId: uuid4(), + uiType: "toggleItemsButton", + sameItemOccurrence: occurrences, + isClicked: false, + }; + } + + if (activity.type === previousActivity?.type) { + occurrences++; + return recursiveToggleItemsButtonDesignation(activity, index, occurrences); + } + + return undefined; + }; + + const initiallyHideItems = (sameItemOccurrence: number) => { + const itemOnly = uiActivities.filter((uiActivity) => uiActivity.uiType === "item"); + for (let i = itemOnly.length - sameItemOccurrence; i < itemOnly.length; i++) { + const item = itemOnly[i]; + + if (item.uiType === "item") { + item.isHidden = true; + } + } + }; + + activitiesDataWithMetadata + .sort((a, b) => moment(b.date).diff(a.date)) + .forEach((activity, index) => { + const dateLabel = recursiveDateLabelDesignation(activity, index); + const toggleItemsButton = recursiveToggleItemsButtonDesignation(activity, index); + dateLabel && uiActivities.push(dateLabel); + uiActivities.push({ + ...activity, + isActiveFound: false, + isFound: false, + uiGeneratedId: uuid4(), + uiType: "item", + isHidden: false, + }); + if (toggleItemsButton) { + initiallyHideItems(toggleItemsButton.sameItemOccurrence); + uiActivities.push(toggleItemsButton); + } + }); + + return uiActivities; +}; diff --git a/designer/client/src/components/toolbars/activities/helpers/handleToggleActivities.ts b/designer/client/src/components/toolbars/activities/helpers/handleToggleActivities.ts new file mode 100644 index 00000000000..6289d44ade0 --- /dev/null +++ b/designer/client/src/components/toolbars/activities/helpers/handleToggleActivities.ts @@ -0,0 +1,43 @@ +import { UIActivity } from "../ActivitiesPanel"; + +export const handleToggleActivities = ( + activities: UIActivity[], + uiGeneratedId: string, + sameItemOccurrence: number, + type: "expand" | "collapse", +) => { + const newState = [...activities]; + + const isHidden = type === "collapse"; + const isClicked = type !== "collapse"; + + const buttonIndex = newState.findIndex((uiActivity) => uiActivity.uiGeneratedId === uiGeneratedId); + + if (buttonIndex === -1) return { activities, buttonPosition: -1 }; + + let itemsToSetState = sameItemOccurrence; + let iteration = 0; + + while (itemsToSetState > 0) { + iteration++; + + const targetIndex = buttonIndex - iteration + 1; + + if (targetIndex < 0 || targetIndex >= newState.length) break; + + const itemToHide = newState[targetIndex]; + + if (itemToHide.uiType === "item") { + newState[targetIndex] = { ...itemToHide, isHidden }; + itemsToSetState--; + } + } + + const clickedItem = newState[buttonIndex]; + + if (clickedItem.uiType === "toggleItemsButton") { + newState[buttonIndex] = { ...clickedItem, isClicked }; + } + + return { uiActivities: newState, buttonPosition: buttonIndex - iteration }; +}; diff --git a/designer/client/src/components/toolbars/activities/helpers/mergeActivityDataWithMetadata.ts b/designer/client/src/components/toolbars/activities/helpers/mergeActivityDataWithMetadata.ts new file mode 100644 index 00000000000..92d7ad48a31 --- /dev/null +++ b/designer/client/src/components/toolbars/activities/helpers/mergeActivityDataWithMetadata.ts @@ -0,0 +1,16 @@ +import { ActivitiesResponse, ActivityMetadataResponse } from "../types"; +import { Activity } from "../ActivitiesPanel"; + +export const mergeActivityDataWithMetadata = ( + activities: ActivitiesResponse["activities"], + activitiesMetadata: ActivityMetadataResponse, +): Activity[] => { + return activities.map((activity): Activity => { + const activities = activitiesMetadata.activities.find((activityMetadata) => activityMetadata.type === activity.type); + const actions = activities.supportedActions.map((supportedAction) => { + return activitiesMetadata.actions.find((action) => action.id === supportedAction); + }); + + return { ...activity, activities, actions }; + }); +}; diff --git a/designer/client/src/components/toolbars/activities/index.ts b/designer/client/src/components/toolbars/activities/index.ts new file mode 100644 index 00000000000..8d7b099699b --- /dev/null +++ b/designer/client/src/components/toolbars/activities/index.ts @@ -0,0 +1 @@ +export * from "./ActivitiesPanel"; diff --git a/designer/client/src/components/toolbars/activities/types.ts b/designer/client/src/components/toolbars/activities/types.ts new file mode 100644 index 00000000000..add4fd689c6 --- /dev/null +++ b/designer/client/src/components/toolbars/activities/types.ts @@ -0,0 +1,79 @@ +export type ActivityTypes = + | "SCENARIO_CREATED" + | "SCENARIO_ARCHIVED" + | "SCENARIO_UNARCHIVED" + | "SCENARIO_DEPLOYED" + | "SCENARIO_CANCELED" + | "SCENARIO_MODIFIED" + | "SCENARIO_PAUSED" + | "SCENARIO_NAME_CHANGED" + | "COMMENT_ADDED" + | "ATTACHMENT_ADDED" + | "CHANGED_PROCESSING_MODE" + | "INCOMING_MIGRATION" + | "OUTGOING_MIGRATION" + | "PERFORMED_SINGLE_EXECUTION" + | "PERFORMED_SCHEDULED_EXECUTION" + | "AUTOMATIC_UPDATE" + | "CUSTOM_ACTION"; + +export interface ActivityMetadata { + type: ActivityTypes; + displayableName: string; + icon: string; + supportedActions: string[]; +} + +export interface ActionMetadata { + id: "compare" | "delete_comment" | "edit_comment" | "download_attachment" | "delete_attachment"; + displayableName: string; + icon: string; +} + +export type ActivityAdditionalFields = { name: string; value: string }; + +interface ActivityComment { + content: { + value: string; + status: "AVAILABLE" | "DELETED"; + }; + lastModifiedBy: string; + lastModifiedAt: string; +} + +interface ActivityAttachmentDeleteStatus { + status: "DELETED"; +} + +interface ActivityAttachmentAvailableStatus { + id: number; + status: "AVAILABLE"; +} + +export interface ActivityAttachment { + file: ActivityAttachmentDeleteStatus | ActivityAttachmentAvailableStatus; + filename: string; + lastModifiedBy: string; + lastModifiedAt: string; +} + +export interface ActivitiesResponse { + activities: { + id: string; + type: ActivityTypes; + user: string; + date: string; + scenarioVersionId: number; + comment?: ActivityComment; + attachment?: ActivityAttachment; + overrideDisplayableName?: string; + overrideSupportedActions?: string[]; + overrideIcon?: string; + additionalFields: ActivityAdditionalFields[]; + }[]; +} + +export interface ActivityMetadataResponse { + activities: ActivityMetadata[]; + actions: ActionMetadata[]; +} diff --git a/designer/client/src/components/toolbars/activities/useActivitiesSearch.test.ts b/designer/client/src/components/toolbars/activities/useActivitiesSearch.test.ts new file mode 100644 index 00000000000..6565ecab364 --- /dev/null +++ b/designer/client/src/components/toolbars/activities/useActivitiesSearch.test.ts @@ -0,0 +1,126 @@ +import { useActivitiesSearch } from "./useActivitiesSearch"; +import { act, renderHook } from "@testing-library/react"; +import { extendActivitiesWithUIData } from "./helpers/extendActivitiesWithUIData"; +import { ActivitiesResponse } from "./types"; +import { mergeActivityDataWithMetadata } from "./helpers/mergeActivityDataWithMetadata"; +import { sampleMetadataResponse } from "../../../../__mocks__/fixtures/sampleMetadataResponse"; + +const sampleActivitiesResponse: ActivitiesResponse["activities"] = [ + { + id: "56a7dd49-778b-468b-8e33-99bd176218aa", + user: "admin", + date: "2024-09-25T06:09:03.470213Z", + scenarioVersionId: 1, + comment: { + content: { + value: "test", + status: "AVAILABLE", + }, + lastModifiedBy: "admin", + lastModifiedAt: "2024-09-25T06:09:03.470213Z", + }, + additionalFields: [], + type: "COMMENT_ADDED", + }, + { + id: "48f383f9-ccdd-46b6-9b33-5f6693165755", + user: "admin", + date: "2024-09-25T06:09:44.313094Z", + scenarioVersionId: 1, + comment: null, + attachment: { + file: { + id: 1, + status: "AVAILABLE", + }, + filename: "324.log", + lastModifiedBy: "admin", + lastModifiedAt: "2024-09-25T06:09:44.313094Z", + }, + additionalFields: [], + overrideIcon: null, + overrideDisplayableName: null, + overrideSupportedActions: null, + type: "ATTACHMENT_ADDED", + }, + { + id: "a2576467-9bf9-4a92-b71f-be95b84d59f6", + user: "admin", + date: "2024-09-25T09:53:40.875721Z", + scenarioVersionId: 3, + comment: { + content: { + value: "tests save", + status: "AVAILABLE", + }, + lastModifiedBy: "admin", + lastModifiedAt: "2024-09-25T09:53:40.875721Z", + }, + additionalFields: [], + overrideDisplayableName: "Version 3 saved", + type: "SCENARIO_MODIFIED", + }, + { + id: "15c0e8a9-d1c5-47dd-bf28-8a08217fff5b", + user: "admin", + date: "2024-09-27T09:55:04.309Z", + scenarioVersionId: 4, + comment: { + content: { + value: "122", + status: "AVAILABLE", + }, + lastModifiedBy: "admin", + lastModifiedAt: "2024-09-27T09:55:04.309Z", + }, + additionalFields: [], + overrideDisplayableName: "Version 4 saved", + type: "SCENARIO_MODIFIED", + }, + { + id: "da3d1f78-7d73-4ed9-b0e5-95538e150d0d", + user: "some user", + date: "2022-12-17T14:21:17Z", + scenarioVersionId: 1, + additionalFields: [ + { + name: "oldName", + value: "marketing campaign", + }, + { + name: "newName", + value: "old marketing campaign", + }, + ], + type: "SCENARIO_NAME_CHANGED", + }, +]; + +const mockedActivities = extendActivitiesWithUIData(mergeActivityDataWithMetadata(sampleActivitiesResponse, sampleMetadataResponse)); + +describe(useActivitiesSearch.name, () => { + it.each<[string, string[]]>([ + ["atta", [mockedActivities[4].uiGeneratedId]], + ["3 saved", [mockedActivities[3].uiGeneratedId]], + ["2024-09-27", [mockedActivities[1].uiGeneratedId]], + ["tests save", [mockedActivities[3].uiGeneratedId]], + ["newName: old marketing campaign", [mockedActivities[7].uiGeneratedId]], + ])("should find elements when query is '%s'", (searchQuery, expected) => { + const handleScrollToItemMock = jest.fn(); + const handleUpdateScenarioActivitiesMock = jest.fn(); + + const { result } = renderHook(() => + useActivitiesSearch({ + activities: mockedActivities, + handleScrollToItem: handleScrollToItemMock, + handleUpdateScenarioActivities: handleUpdateScenarioActivitiesMock, + }), + ); + + act(() => { + result.current.handleSearch(searchQuery); + }); + + expect(result.current.foundResults).toMatchObject(expected); + }); +}); diff --git a/designer/client/src/components/toolbars/activities/useActivitiesSearch.ts b/designer/client/src/components/toolbars/activities/useActivitiesSearch.ts new file mode 100644 index 00000000000..4e78c07c907 --- /dev/null +++ b/designer/client/src/components/toolbars/activities/useActivitiesSearch.ts @@ -0,0 +1,177 @@ +import { useCallback, useState } from "react"; +import { Activity, UIActivity } from "./ActivitiesPanel"; +import { Align } from "react-window"; +import { NestedKeyOf } from "../../../reducers/graph/nestedKeyOf"; +import { get, uniq } from "lodash"; +import { ActivityAdditionalFields } from "./types"; +import { handleToggleActivities } from "./helpers/handleToggleActivities"; + +interface Props { + activities: UIActivity[]; + handleScrollToItem: (index: number, align: Align) => void; + handleUpdateScenarioActivities: (activities: (activities: UIActivity[]) => UIActivity[]) => void; +} +export const useActivitiesSearch = ({ activities, handleScrollToItem, handleUpdateScenarioActivities }: Props) => { + const [searchQuery, setSearchQuery] = useState(""); + const [foundResults, setFoundResults] = useState([]); + const [selectedResult, setSelectedResult] = useState(0); + + const handleSetFoundResults = useCallback((activities: UIActivity[]) => { + const uniqueFoundResults = uniq(activities).map((activity) => activity.uiGeneratedId); + setFoundResults(uniqueFoundResults); + + return uniqueFoundResults; + }, []); + + const handleUpdateSearchResults = useCallback( + (foundActivities: string[], selectedResult: number) => { + handleUpdateScenarioActivities((prevState) => { + return prevState.map((activity) => { + if (activity.uiType !== "item") { + return activity; + } + + activity.isFound = false; + activity.isActiveFound = false; + + if (foundActivities.some((foundResult) => foundResult === activity.uiGeneratedId)) { + activity.isFound = true; + } + + if (activity.uiGeneratedId === foundActivities[selectedResult]) { + activity.isActiveFound = true; + } + + return activity; + }); + }); + }, + [handleUpdateScenarioActivities], + ); + + const handleExpandAllResults = useCallback(() => { + handleUpdateScenarioActivities((prevState) => { + let newState = [...prevState]; + + for (const activity of newState) { + if (activity.uiType === "toggleItemsButton") { + newState = handleToggleActivities(newState, activity.uiGeneratedId, activity.sameItemOccurrence, "expand").uiActivities; + } + } + + return newState; + }); + }, [handleUpdateScenarioActivities]); + + const handleCollapseAllResults = useCallback(() => { + handleUpdateScenarioActivities((prevState) => { + let newState = [...prevState]; + + for (const activity of newState) { + if (activity.uiType === "toggleItemsButton") { + newState = handleToggleActivities( + newState, + activity.uiGeneratedId, + activity.sameItemOccurrence, + "collapse", + ).uiActivities; + } + } + + return newState; + }); + }, [handleUpdateScenarioActivities]); + + const handleClearResults = useCallback(() => { + setSearchQuery(""); + setSelectedResult(0); + setFoundResults([]); + handleUpdateSearchResults([], 0); + handleCollapseAllResults(); + }, [handleCollapseAllResults, handleUpdateSearchResults]); + + const handleSearch = useCallback( + (value: string) => { + handleExpandAllResults(); + setSearchQuery(value); + + if (value === "") { + handleClearResults(); + return; + } + + setSelectedResult(0); + + const foundActivities: UIActivity[] = []; + + const fullSearchAllowedFields: NestedKeyOf[] = [ + "date", + "user", + "comment.content.value", + "activities.displayableName", + "overrideDisplayableName", + "additionalFields", + ]; + + for (const activity of activities) { + if (activity.uiType !== "item") { + continue; + } + + for (const fullSearchAllowedField of fullSearchAllowedFields) { + const searchFieldValue: string | ActivityAdditionalFields[] = get(activity, fullSearchAllowedField, "") || ""; + + if (Array.isArray(searchFieldValue)) { + if ( + searchFieldValue.some((searchValue) => + `${searchValue.name.toLowerCase()}: ${searchValue.value.toLowerCase()}`.includes(value.toLowerCase()), + ) + ) { + foundActivities.push(activity); + } + + continue; + } + + if (value && searchFieldValue.toLowerCase().includes(value.toLowerCase())) { + foundActivities.push(activity); + } + } + } + + const uniqueFoundResults = handleSetFoundResults(foundActivities); + handleUpdateSearchResults(uniqueFoundResults, selectedResult); + const indexToScroll = activities.findIndex((item) => item.uiGeneratedId === foundActivities[0]?.uiGeneratedId); + handleScrollToItem(indexToScroll, "center"); + }, + [ + activities, + handleClearResults, + handleExpandAllResults, + handleScrollToItem, + handleSetFoundResults, + handleUpdateSearchResults, + selectedResult, + ], + ); + + const changeResult = (selectedResultNewValue: number) => { + if (selectedResultNewValue < 0) { + selectedResultNewValue = foundResults.length - 1; + } + + if (selectedResultNewValue >= foundResults.length) { + selectedResultNewValue = 0; + } + + const foundResult = foundResults[selectedResultNewValue]; + handleScrollToItem( + activities.findIndex((item) => item.uiGeneratedId === foundResult), + "center", + ); + setSelectedResult(selectedResultNewValue); + handleUpdateSearchResults(foundResults, selectedResultNewValue); + }; + + return { handleSearch, foundResults, selectedResult, searchQuery, changeResult, handleClearResults }; +}; diff --git a/designer/client/src/components/toolbars/creator/SearchHighlighter.tsx b/designer/client/src/components/toolbars/creator/SearchHighlighter.tsx index 21aa6a3ab11..ec634998a47 100644 --- a/designer/client/src/components/toolbars/creator/SearchHighlighter.tsx +++ b/designer/client/src/components/toolbars/creator/SearchHighlighter.tsx @@ -1,15 +1,20 @@ import { useTheme } from "@mui/material"; import Highlighter from "react-highlight-words"; -import React from "react"; +import React, { CSSProperties } from "react"; export function SearchHighlighter({ children, highlights = [], className, + typographyStyle = {}, + title, + ...props }: { children: string; highlights: string[]; className?: string; + typographyStyle?: CSSProperties; + title?: string; }) { const theme = useTheme(); return ( @@ -19,11 +24,15 @@ export function SearchHighlighter({ searchWords={highlights} autoEscape highlightTag={`span`} + unhighlightStyle={typographyStyle} highlightStyle={{ + ...typographyStyle, color: theme.palette.warning.main, background: theme.palette.background.paper, fontWeight: "bold", }} + title={title} + {...props} /> ); } diff --git a/designer/client/src/components/toolbars/process/buttons/CompareButton.tsx b/designer/client/src/components/toolbars/process/buttons/CompareButton.tsx index 7fe24351fe0..262ff5f06df 100644 --- a/designer/client/src/components/toolbars/process/buttons/CompareButton.tsx +++ b/designer/client/src/components/toolbars/process/buttons/CompareButton.tsx @@ -4,9 +4,9 @@ import { useSelector } from "react-redux"; import Icon from "../../../../assets/img/toolbarButtons/compare.svg"; import { hasOneVersion } from "../../../../reducers/selectors/graph"; import { useWindows } from "../../../../windowManager"; -import { WindowKind } from "../../../../windowManager/WindowKind"; import { ToolbarButton } from "../../../toolbarComponents/toolbarButtons"; import { ToolbarButtonProps } from "../../types"; +import { handleOpenCompareVersionDialog } from "../../../modals/CompareVersionsDialog"; type Props = ToolbarButtonProps; @@ -22,15 +22,7 @@ function CompareButton(props: Props): JSX.Element { name={t("panels.actions.process-compare.button", "compare")} icon={} disabled={!available} - onClick={() => - open({ - title: t("dialog.title.compareVersions", "compare versions"), - isResizable: true, - minWidth: 980, - minHeight: 200, - kind: WindowKind.compareVersions, - }) - } + onClick={() => open(handleOpenCompareVersionDialog())} type={type} /> ); diff --git a/designer/client/src/components/toolbars/process/buttons/MigrateButton.tsx b/designer/client/src/components/toolbars/process/buttons/MigrateButton.tsx index 4da1cb8faad..0d252a2cb41 100644 --- a/designer/client/src/components/toolbars/process/buttons/MigrateButton.tsx +++ b/designer/client/src/components/toolbars/process/buttons/MigrateButton.tsx @@ -1,7 +1,7 @@ import { isEmpty } from "lodash"; import React, { useCallback } from "react"; import { useTranslation } from "react-i18next"; -import { useSelector } from "react-redux"; +import { useDispatch, useSelector } from "react-redux"; import Icon from "../../../../assets/img/toolbarButtons/migrate.svg"; import * as DialogMessages from "../../../../common/DialogMessages"; import HttpService from "../../../../http/HttpService"; @@ -10,6 +10,7 @@ import { getFeatureSettings, getTargetEnvironmentId } from "../../../../reducers import { useWindows } from "../../../../windowManager"; import { CapabilitiesToolbarButton } from "../../../toolbarComponents/CapabilitiesToolbarButton"; import { ToolbarButtonProps } from "../../types"; +import { getScenarioActivities } from "../../../../actions/nk/scenarioActivities"; type Props = ToolbarButtonProps; @@ -20,6 +21,7 @@ function MigrateButton(props: Props) { const featuresSettings = useSelector(getFeatureSettings); const migrationPossible = useSelector(isMigrationPossible); const targetEnvironmentId = useSelector(getTargetEnvironmentId); + const dispatch = useDispatch(); const available = !disabled && migrationPossible; const { t } = useTranslation(); @@ -29,7 +31,11 @@ function MigrateButton(props: Props) { () => confirm({ text: DialogMessages.migrate(processName, targetEnvironmentId), - onConfirmCallback: (confirmed) => confirmed && HttpService.migrateProcess(processName, versionId), + onConfirmCallback: (confirmed) => + confirmed && + HttpService.migrateProcess(processName, versionId).then(async () => { + await dispatch(await getScenarioActivities(processName)); + }), confirmText: t("panels.actions.process-migrate.yes", "Yes"), denyText: t("panels.actions.process-migrate.no", "No"), }), diff --git a/designer/client/src/components/toolbars/process/buttons/UnArchiveButton.tsx b/designer/client/src/components/toolbars/process/buttons/UnArchiveButton.tsx index 9dbc55ac2d3..419c6e44353 100644 --- a/designer/client/src/components/toolbars/process/buttons/UnArchiveButton.tsx +++ b/designer/client/src/components/toolbars/process/buttons/UnArchiveButton.tsx @@ -9,6 +9,7 @@ import { useWindows } from "../../../../windowManager"; import { CapabilitiesToolbarButton } from "../../../toolbarComponents/CapabilitiesToolbarButton"; import { ToolbarButtonProps } from "../../types"; import { displayCurrentProcessVersion, loadProcessToolbarsConfiguration } from "../../../../actions/nk"; +import { getScenarioActivities } from "../../../../actions/nk/scenarioActivities"; function UnArchiveButton({ disabled, type }: ToolbarButtonProps) { const processName = useSelector(getProcessName); @@ -25,9 +26,10 @@ function UnArchiveButton({ disabled, type }: ToolbarButtonProps) { text: DialogMessages.unArchiveProcess(processName), onConfirmCallback: (confirmed) => confirmed && - HttpService.unArchiveProcess(processName).then(() => { + HttpService.unArchiveProcess(processName).then(async () => { dispatch(loadProcessToolbarsConfiguration(processName)); dispatch(displayCurrentProcessVersion(processName)); + await dispatch(await getScenarioActivities(processName)); }), confirmText: t("panels.actions.process-unarchive.yes", "Yes"), denyText: t("panels.actions.process-unarchive.no", "No"), diff --git a/designer/client/src/components/toolbars/process/buttons/useArchiveHelper.ts b/designer/client/src/components/toolbars/process/buttons/useArchiveHelper.ts index 029f8fcd69c..d4d63499e7f 100644 --- a/designer/client/src/components/toolbars/process/buttons/useArchiveHelper.ts +++ b/designer/client/src/components/toolbars/process/buttons/useArchiveHelper.ts @@ -8,6 +8,7 @@ import { unsavedProcessChanges } from "../../../../common/DialogMessages"; import { getFeatureSettings } from "../../../../reducers/selectors/settings"; import { displayCurrentProcessVersion, loadProcessToolbarsConfiguration } from "../../../../actions/nk"; import { useCallback } from "react"; +import { getScenarioActivities } from "../../../../actions/nk/scenarioActivities"; export const useArchiveHelper = (processName: string) => { const dispatch = useDispatch(); @@ -17,13 +18,14 @@ export const useArchiveHelper = (processName: string) => { const { redirectAfterArchive } = useSelector(getFeatureSettings); const archive = useCallback(async () => { - return HttpService.archiveProcess(processName).then(() => { + return HttpService.archiveProcess(processName).then(async () => { dispatch({ type: "ARCHIVED" }); if (redirectAfterArchive) { navigate(ArchivedPath); } else { dispatch(loadProcessToolbarsConfiguration(processName)); dispatch(displayCurrentProcessVersion(processName)); + await dispatch(await getScenarioActivities(processName)); } }); }, [dispatch, navigate, processName, redirectAfterArchive]); diff --git a/designer/client/src/components/toolbars/test/buttons/AdhocTestingButton.tsx b/designer/client/src/components/toolbars/test/buttons/AdhocTestingButton.tsx index 7a36f2903cf..3efcc9a6e05 100644 --- a/designer/client/src/components/toolbars/test/buttons/AdhocTestingButton.tsx +++ b/designer/client/src/components/toolbars/test/buttons/AdhocTestingButton.tsx @@ -30,7 +30,7 @@ function AdhocTestingButton({ disabled, name, title, docs, markdownContent, type const sourcesFound = testParameters.length; const multipleSourcesTest = useCallback(() => { - inform({ text: `Ad hoc testing is supported only for scenario with single source. Your scenario has ${sourcesFound} sources.` }); + inform({ text: `Ad hoc testing is supported only for scenario with single source. Your scenario has ${sourcesFound} sources.` }); }, [inform, sourcesFound]); const action = useAdhocTestingAction(); diff --git a/designer/client/src/containers/Notifications.tsx b/designer/client/src/containers/Notifications.tsx index 417760bcfd8..9e4719527de 100644 --- a/designer/client/src/containers/Notifications.tsx +++ b/designer/client/src/containers/Notifications.tsx @@ -16,6 +16,7 @@ import { loadProcessVersions } from "../actions/nk/loadProcessVersions"; import { useChangeConnectionError } from "./connectionErrorProvider"; import i18next from "i18next"; import { ThunkAction } from "../actions/reduxTypes"; +import { getScenarioActivities } from "../actions/nk/scenarioActivities"; const prepareNotification = ({ id, message, type }: BackendNotification): ThunkAction => @@ -54,6 +55,7 @@ const handleRefresh = case "versions": return dispatch(loadProcessVersions(scenarioName)); case "activity": + dispatch(getScenarioActivities(scenarioName)); return dispatch(displayProcessActivity(scenarioName)); case "state": return dispatch(loadProcessState(scenarioName)); diff --git a/designer/client/src/http/HttpService.ts b/designer/client/src/http/HttpService.ts index a970abeb7f0..372a6c2b901 100644 --- a/designer/client/src/http/HttpService.ts +++ b/designer/client/src/http/HttpService.ts @@ -18,7 +18,7 @@ import { } from "../components/Process/types"; import { ToolbarsConfig } from "../components/toolbarSettings/types"; import { AuthenticationSettings } from "../reducers/settings"; -import { Expression, NodeType, ProcessAdditionalFields, ProcessDefinitionData, ReturnedType, ScenarioGraph, VariableTypes } from "../types"; +import { Expression, NodeType, ProcessAdditionalFields, ProcessDefinitionData, ScenarioGraph, VariableTypes } from "../types"; import { Instant, WithId } from "../types/common"; import { BackendNotification } from "../containers/Notifications"; import { ProcessCounts } from "../reducers/graph"; @@ -27,8 +27,9 @@ import { AdditionalInfo } from "../components/graph/node-modal/NodeAdditionalInf import { withoutHackOfEmptyEdges } from "../components/graph/GraphPartialsInTS/EdgeUtils"; import { CaretPosition2d, ExpressionSuggestion } from "../components/graph/node-modal/editors/expression/ExpressionSuggester"; import { GenericValidationRequest, TestAdhocValidationRequest } from "../actions/nk/adhocTesting"; -import { EventTrackingSelectorType, EventTrackingType } from "../containers/event-tracking/use-register-tracking-events"; +import { EventTrackingSelectorType, EventTrackingType } from "../containers/event-tracking"; import { AvailableScenarioLabels, ScenarioLabelsValidationResponse } from "../components/Labels/types"; +import { ActivitiesResponse, ActivityMetadataResponse } from "../components/toolbars/activities/types"; type HealthCheckProcessDeploymentType = { status: string; @@ -253,7 +254,7 @@ class HttpService { return api.get("/processes", { params: data }); } - fetchProcessDetails(processName: ProcessName, versionId?: ProcessVersionId) { + fetchProcessDetails(processName: ProcessName, versionId?: ProcessVersionId): Promise> { const id = encodeURIComponent(processName); const url = versionId ? `/processes/${id}/${versionId}` : `/processes/${id}`; return api.get(url); @@ -372,11 +373,15 @@ class HttpService { return api.get(`/processes/${encodeURIComponent(processName)}/activity`); } - addComment(processName, versionId, data) { - return api - .post(`/processes/${encodeURIComponent(processName)}/${versionId}/activity/comments`, data) - .then(() => this.#addInfo(i18next.t("notification.info.commentAdded", "Comment added"))) - .catch((error) => this.#addError(i18next.t("notification.error.failedToAddComment", "Failed to add comment"), error)); + async addComment(processName: string, versionId: number, comment: string): Promise<"success" | "error"> { + try { + await api.post(`/processes/${encodeURIComponent(processName)}/${versionId}/activity/comment`, comment); + this.#addInfo(i18next.t("notification.info.commentAdded", "Comment added")); + return "success" as const; + } catch (error) { + await this.#addError(i18next.t("notification.error.failedToAddComment", "Failed to add comment"), error); + return "error" as const; + } } deleteComment(processName, commentId) { @@ -386,15 +391,17 @@ class HttpService { .catch((error) => this.#addError(i18next.t("notification.error.failedToDeleteComment", "Failed to delete comment"), error)); } - addAttachment(processName: ProcessName, versionId: ProcessVersionId, file: File) { - return api - .post(`/processes/${encodeURIComponent(processName)}/${versionId}/activity/attachments`, file, { + async addAttachment(processName: ProcessName, versionId: ProcessVersionId, file: File) { + try { + await api.post(`/processes/${encodeURIComponent(processName)}/${versionId}/activity/attachments`, file, { headers: { "Content-Disposition": `attachment; filename="${file.name}"` }, - }) - .then(() => this.#addInfo(i18next.t("notification.error.attachmentAdded", "Attachment added"))) - .catch((error) => - this.#addError(i18next.t("notification.error.failedToAddAttachment", "Failed to add attachment"), error, true), - ); + }); + this.#addInfo(i18next.t("notification.error.attachmentAdded", "Attachment added")); + return "success" as const; + } catch (error) { + await this.#addError(i18next.t("notification.error.failedToAddAttachment", "Failed to add attachment"), error, true); + return "error" as const; + } } downloadAttachment(processName: ProcessName, attachmentId, fileName: string) { @@ -805,6 +812,14 @@ class HttpService { return api.post(`/statistic`, { statistics }); } + fetchActivitiesMetadata(scenarioName: string) { + return api.get(`/processes/${scenarioName}/activity/activities/metadata`); + } + + fetchActivities(scenarioName: string) { + return api.get(`/processes/${scenarioName}/activity/activities`); + } + #addInfo(message: string) { if (this.#notificationActions) { this.#notificationActions.success(message); diff --git a/designer/client/src/reducers/processActivity.ts b/designer/client/src/reducers/processActivity.ts index 38fb20a0a21..5a48155210a 100644 --- a/designer/client/src/reducers/processActivity.ts +++ b/designer/client/src/reducers/processActivity.ts @@ -1,6 +1,7 @@ import { Action } from "../actions/reduxTypes"; import { Instant } from "../types/common"; import { ProcessVersionId } from "../components/Process/types"; +import { UIActivity } from "../components/toolbars/activities"; export type User = string; @@ -23,11 +24,13 @@ export type Comment = { export type ProcessActivityState = { comments: $TodoType[]; attachments: Attachment[]; + activities: UIActivity[]; }; const emptyProcessActivity: ProcessActivityState = { comments: [], attachments: [], + activities: [], }; export function reducer(state: ProcessActivityState = emptyProcessActivity, action: Action): ProcessActivityState { @@ -39,6 +42,18 @@ export function reducer(state: ProcessActivityState = emptyProcessActivity, acti attachments: action.attachments || [], }; } + case "GET_SCENARIO_ACTIVITIES": { + return { + ...state, + activities: action.activities, + }; + } + case "UPDATE_SCENARIO_ACTIVITIES": { + return { + ...state, + activities: action.activities, + }; + } default: return state; } diff --git a/designer/client/src/reducers/selectors/activities.ts b/designer/client/src/reducers/selectors/activities.ts new file mode 100644 index 00000000000..a40b379af63 --- /dev/null +++ b/designer/client/src/reducers/selectors/activities.ts @@ -0,0 +1,13 @@ +import { RootState } from "../index"; +import { createSelector } from "reselect"; + +export const getActivity = (state: RootState) => state.processActivity; + +/* + * To correctly display items in a react-window list, only the visible elements should be passed. + **/ +export const getVisibleActivities = createSelector( + getActivity, + (state) => + state.activities.filter((activity) => (activity.uiType === "item" && !activity.isHidden) || activity.uiType !== "item") || [], +); diff --git a/designer/client/src/stylesheets/SelectStyled.ts b/designer/client/src/stylesheets/SelectStyled.ts index e2ae01d41cc..273bc04b099 100644 --- a/designer/client/src/stylesheets/SelectStyled.ts +++ b/designer/client/src/stylesheets/SelectStyled.ts @@ -77,15 +77,15 @@ export const selectStyled = (theme: Theme) => { } ::-webkit-scrollbar-track { - background: ${blendLighten(theme.palette.background.paper, 0.5)}; + background: ${blendDarken(theme.palette.common.white, 0.75)}; } ::-webkit-scrollbar-thumb { - background: ${alpha(theme.palette.background.paper, 0.85)}; + background: ${blendLighten(theme.palette.background.paper, 0.5)}; } ::-webkit-scrollbar-thumb:hover { - background: ${theme.palette.action.hover}; + background: ${blendLighten(theme.palette.background.paper, 0.5)}; } `; diff --git a/designer/client/src/windowManager/ContentGetter.tsx b/designer/client/src/windowManager/ContentGetter.tsx index c5468541265..c74056c5e28 100644 --- a/designer/client/src/windowManager/ContentGetter.tsx +++ b/designer/client/src/windowManager/ContentGetter.tsx @@ -7,6 +7,7 @@ import { Debug } from "../containers/Debug"; import { NuThemeProvider } from "../containers/theme/nuThemeProvider"; import { WindowContent } from "./WindowContent"; import { WindowKind } from "./WindowKind"; +import AddAttachmentDialog from "../components/modals/AddAttachmentDialog"; const AddProcessDialog = loadable(() => import("../components/AddProcessDialog"), { fallback: }); const NodeDetails = loadable(() => import("../components/graph/node-modal/node/NodeDetails"), { @@ -48,6 +49,10 @@ const ScenarioDetailsDialog = loadable(() => import("../components/modals/MoreSc fallback: , }); +const AddCommentDialog = loadable(() => import("../components/modals/AddCommentDialog"), { + fallback: , +}); + const contentGetter: React.FC> = (props) => { switch (props.data.kind) { case WindowKind.addFragment: @@ -86,6 +91,10 @@ const contentGetter: React.FC> = (props) => { return ; case WindowKind.scenarioDetails: return ; + case WindowKind.addComment: + return ; + case WindowKind.addAttachment: + return ; default: return ( diff --git a/designer/client/src/windowManager/WindowKind.tsx b/designer/client/src/windowManager/WindowKind.tsx index 9bf01758e57..f362613115b 100644 --- a/designer/client/src/windowManager/WindowKind.tsx +++ b/designer/client/src/windowManager/WindowKind.tsx @@ -18,4 +18,6 @@ export enum WindowKind { scenarioDetails, viewDescription, editDescription, + addComment, + addAttachment, } diff --git a/designer/client/test/CompareVersionDialog-test.tsx b/designer/client/test/CompareVersionDialog-test.tsx index cf343347ced..4c8c8eb4cd0 100644 --- a/designer/client/test/CompareVersionDialog-test.tsx +++ b/designer/client/test/CompareVersionDialog-test.tsx @@ -88,6 +88,7 @@ describe(CompareVersionsDialog.name, () => { title: "compare versions", kind: 12, id: "8b0a9e43-9d18-4837-950c-858d35b7c60c", + meta: { scenarioVersionId: undefined }, }} /> @@ -132,6 +133,7 @@ describe(CompareVersionsDialog.name, () => { title: "compare versions", kind: 12, id: "8b0a9e43-9d18-4837-950c-858d35b7c60c", + meta: { scenarioVersionId: undefined }, }} /> diff --git a/designer/server/src/main/resources/defaultDesignerConfig.conf b/designer/server/src/main/resources/defaultDesignerConfig.conf index 48b40bdff68..548967a5392 100644 --- a/designer/server/src/main/resources/defaultDesignerConfig.conf +++ b/designer/server/src/main/resources/defaultDesignerConfig.conf @@ -122,9 +122,7 @@ processToolbarConfig { { type: "search-panel" } { type: "tips-panel" } { type: "creator-panel", hidden: { archived: true } } - { type: "versions-panel" } - { type: "comments-panel" } - { type: "attachments-panel" } + { type: "activities-panel" } ] topRight: [ { type: "process-info-panel" } diff --git a/designer/server/src/main/resources/web/static/assets/activities/canceled.svg b/designer/server/src/main/resources/web/static/assets/activities/cancel.svg similarity index 100% rename from designer/server/src/main/resources/web/static/assets/activities/canceled.svg rename to designer/server/src/main/resources/web/static/assets/activities/cancel.svg diff --git a/designer/server/src/main/scala/pl/touk/nussknacker/ui/config/scenariotoolbar/ToolbarPanelConfig.scala b/designer/server/src/main/scala/pl/touk/nussknacker/ui/config/scenariotoolbar/ToolbarPanelConfig.scala index d25bb75ece7..b3ee3960b9c 100644 --- a/designer/server/src/main/scala/pl/touk/nussknacker/ui/config/scenariotoolbar/ToolbarPanelConfig.scala +++ b/designer/server/src/main/scala/pl/touk/nussknacker/ui/config/scenariotoolbar/ToolbarPanelConfig.scala @@ -38,6 +38,7 @@ object ToolbarPanelTypeConfig extends Enumeration { val ProcessInfoPanel: Value = Value("process-info-panel") val ProcessActionsPanel: Value = Value("process-actions-panel") val ButtonsPanel: Value = Value("buttons-panel") + val ActivitiesPanel: Value = Value("activities-panel") // Some of panels require buttons not empty list param, this method verify that.. def requiresButtonsParam(`type`: ToolbarPanelType): Boolean = diff --git a/designer/server/src/main/scala/pl/touk/nussknacker/ui/process/test/ScenarioTestService.scala b/designer/server/src/main/scala/pl/touk/nussknacker/ui/process/test/ScenarioTestService.scala index ce5cdb8670d..56f3a05611d 100644 --- a/designer/server/src/main/scala/pl/touk/nussknacker/ui/process/test/ScenarioTestService.scala +++ b/designer/server/src/main/scala/pl/touk/nussknacker/ui/process/test/ScenarioTestService.scala @@ -48,17 +48,17 @@ class ScenarioTestService( scenarioGraph: ScenarioGraph, processVersion: ProcessVersion, isFragment: Boolean - )(implicit user: LoggedUser): Map[String, List[Parameter]] = { + )(implicit user: LoggedUser): Map[String, List[Parameter]] = { val canonical = toCanonicalProcess(scenarioGraph, processVersion, isFragment) testInfoProvider .getTestParameters(processVersion, canonical) } def testUISourceParametersDefinition( - scenarioGraph: ScenarioGraph, - processVersion: ProcessVersion, - isFragment: Boolean - )(implicit user: LoggedUser): List[UISourceParameters] = + scenarioGraph: ScenarioGraph, + processVersion: ProcessVersion, + isFragment: Boolean + )(implicit user: LoggedUser): List[UISourceParameters] = testParametersDefinition(scenarioGraph, processVersion, isFragment) .map { case (id, params) => UISourceParameters(id, params.map(DefinitionsService.createUIParameter)) } .map { assignUserFriendlyEditor } diff --git a/docs/Changelog.md b/docs/Changelog.md index 6d866ce0c72..b4fea16d22d 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -76,6 +76,7 @@ * [#6958](https://github.com/TouK/nussknacker/pull/6958) Add message size limit in the "Kafka" exceptionHandler * [#6988](https://github.com/TouK/nussknacker/pull/6988) Remove unused API classes: `MultiMap`, `TimestampedEvictableStateFunction` * [#7000](https://github.com/TouK/nussknacker/pull/7000) Show all possible options for dictionary editor on open. +* [#6979](https://github.com/TouK/nussknacker/pull/6979) Introduces an activities panel that provides information about all system activities. ## 1.17 diff --git a/docs/MigrationGuide.md b/docs/MigrationGuide.md index fc570388b7b..4d9e185ad5a 100644 --- a/docs/MigrationGuide.md +++ b/docs/MigrationGuide.md @@ -51,6 +51,9 @@ To see the biggest differences please consult the [changelog](Changelog.md). * [#6952](https://github.com/TouK/nussknacker/pull/6952) Improvement: TypeInformation support for scala.Option: If you used CaseClassTypeInfoFactory with case classes that contain the Option type, the state won't be restored after the upgrade. +### Configuration changes +* [#6979](https://github.com/TouK/nussknacker/pull/6979) Add `type: "activities-panel"` to The `processToolbarConfig` which replace deprecated `{ type: "versions-panel" }` `{ type: "comments-panel" }` and `{ type: "attachments-panel" }` + ## In version 1.17.0 ### Code API changes diff --git a/engine/flink/executor/src/main/scala/pl/touk/nussknacker/engine/process/typeinformation/TypingResultAwareTypeInformationDetection.scala b/engine/flink/executor/src/main/scala/pl/touk/nussknacker/engine/process/typeinformation/TypingResultAwareTypeInformationDetection.scala index 8e17e1f802e..baadb2baa59 100644 --- a/engine/flink/executor/src/main/scala/pl/touk/nussknacker/engine/process/typeinformation/TypingResultAwareTypeInformationDetection.scala +++ b/engine/flink/executor/src/main/scala/pl/touk/nussknacker/engine/process/typeinformation/TypingResultAwareTypeInformationDetection.scala @@ -11,10 +11,12 @@ import pl.touk.nussknacker.engine.flink.api.TypedMultiset import pl.touk.nussknacker.engine.flink.api.typeinformation.TypeInformationDetection import pl.touk.nussknacker.engine.flink.typeinformation.ConcreteCaseClassTypeInfo import pl.touk.nussknacker.engine.process.typeinformation.internal.ContextTypeHelpers -import pl.touk.nussknacker.engine.process.typeinformation.internal.typedobject.{TypedJavaMapTypeInformation, TypedScalaMapTypeInformation} +import pl.touk.nussknacker.engine.process.typeinformation.internal.typedobject.{ + TypedJavaMapTypeInformation, + TypedScalaMapTypeInformation +} import pl.touk.nussknacker.engine.util.Implicits._ - // TODO: handle avro types - see FlinkConfluentUtils /* This class generates TypeInformation based on ValidationContext and TypingResult. diff --git a/engine/flink/extensions-api/src/main/scala/pl/touk/nussknacker/engine/flink/api/serialization/SerializerWithSpecifiedClass.scala b/engine/flink/extensions-api/src/main/scala/pl/touk/nussknacker/engine/flink/api/serialization/SerializerWithSpecifiedClass.scala index e69de29bb2d..8b137891791 100644 --- a/engine/flink/extensions-api/src/main/scala/pl/touk/nussknacker/engine/flink/api/serialization/SerializerWithSpecifiedClass.scala +++ b/engine/flink/extensions-api/src/main/scala/pl/touk/nussknacker/engine/flink/api/serialization/SerializerWithSpecifiedClass.scala @@ -0,0 +1 @@ +