From a17bc4b4a94a8120afc58f4aa4ac4000fd643f26 Mon Sep 17 00:00:00 2001 From: Zeeshaun Date: Mon, 23 Mar 2026 11:21:52 -0500 Subject: [PATCH] Adding --- .../overlays/qa/appsettings/appsettings.json | 2 +- .../.dockerignore | 6 + .../.editorconfig | 30 + .../.eslintrc.json | 64 + .../.gitignore | 131 + .../.npmrc | 3 + .../.prettierrc.json | 4 + .../CHANGELOG.md | 207 + .../Dockerfile | 62 + .../README.md | 290 + .../SetupLocalSSL.ps1 | 6 + .../azdo_pipelines/azure-k8s-pipeline.yaml | 52 + .../azure-pipelines-feature-branches.yaml | 9 + .../azdo_pipelines/azure-pipelines-prd.yaml | 9 + .../azdo_pipelines/azure-pipelines-qa.yaml | 9 + .../azdo_pipelines/azure-pipelines-uat.yaml | 9 + .../config-files/prd-pvars.yaml | 7 + .../azdo_pipelines/config-files/qa-pvars.yaml | 7 + .../config-files/uat-pvars.yaml | 7 + .../copy-test-results.sh | 6 + .../docker-build.sh | 3 + .../docker-run.sh | 3 + .../docker-rundev.sh | 4 + .../isDockerEnv.js | 15 + .../jest.config.js | 17 + .../kustomize/base/Deployment.yaml | 54 + .../kustomize/base/Service.yaml | 11 + .../kustomize/base/kustomization.yaml | 4 + .../base/secrets-provider-class.yaml | 11 + .../kustomize/base/virtual-service.yaml | 18 + .../overlays/local/common/kustomization.yaml | 7 + .../common/set_appsettings_deployment.yaml | 12 + .../overlays/local/kustomization.yaml | 2 + .../overlays/local/normal/configmap.yaml | 17 + .../overlays/local/normal/kustomization.yaml | 5 + .../local/normal/set_virtual_service.yaml | 18 + .../overlays/prd/common/kustomization.yaml | 7 + .../common/set_appsettings_deployment.yaml | 12 + .../kustomize/overlays/prd/kustomization.yaml | 2 + .../overlays/prd/normal/configmap.yaml | 16 + .../overlays/prd/normal/kustomization.yaml | 5 + .../prd/normal/set_virtual_service.yaml | 18 + .../overlays/qa/common/kustomization.yaml | 7 + .../qa/common/set_appsettings_deployment.yaml | 12 + .../kustomize/overlays/qa/kustomization.yaml | 2 + .../overlays/qa/normal/configmap.yaml | 17 + .../overlays/qa/normal/kustomization.yaml | 5 + .../qa/normal/set_virtual_service.yaml | 18 + .../overlays/uat/common/kustomization.yaml | 7 + .../common/set_appsettings_deployment.yaml | 12 + .../kustomize/overlays/uat/kustomization.yaml | 2 + .../overlays/uat/normal/configmap.yaml | 17 + .../overlays/uat/normal/kustomization.yaml | 5 + .../uat/normal/set_virtual_service.yaml | 18 + .../nginx.conf | 8 + .../package-lock.json | 21919 ++++++++++++++++ .../package.json | 191 + .../public/favicon.ico | Bin 0 -> 15406 bytes .../public/index.html | 42 + .../public/logo192.png | Bin 0 -> 5347 bytes .../public/logo512.png | Bin 0 -> 9664 bytes .../public/manifest.json | 26 + .../public/robots.txt | 3 + .../src/App.test.tsx | 7 + .../src/App.tsx | 293 + .../src/actions/ActivityActions.ts | 172 + .../src/actions/CommissionActions.ts | 43 + .../src/actions/DeadActivityActions.ts | 125 + .../src/actions/DepartmentActions.ts | 91 + .../src/actions/EmployeeActions.ts | 113 + .../src/actions/GoalActions.ts | 153 + .../src/actions/PayoutActions.ts | 65 + .../src/actions/ProfileActions.ts | 63 + .../src/actions/RoleMappingActions.ts | 72 + .../src/actions/UserAssignmentActions.ts | 43 + .../src/authConfig.ts | 23 + .../activity-detail/ActivityApproval.test.tsx | 91 + .../activity-detail/ActivityApproval.tsx | 90 + .../ActivityDetailAlertBar.test.tsx | 162 + .../ActivityDetailAlertBar.tsx | 62 + .../ActivityDetailDisplay.test.tsx | 24 + .../activity-detail/ActivityDetailDisplay.tsx | 145 + .../ActivityDetailUpdatesTable.test.tsx | 56 + .../ActivityDetailUpdatesTable.tsx | 56 + .../activity-detail/ActivityDisplay.test.tsx | 57 + .../activity-detail/ActivityDisplay.tsx | 128 + .../ActivityMetaDataDisplay.tsx | 45 + .../ActivityPayoutsDisplay.test.tsx | 60 + .../ActivityPayoutsDisplay.tsx | 58 + .../ActivityStateChangesTable.test.tsx | 58 + .../ActivityStateChangesTable.tsx | 83 + .../ActivityTransferApproval.test.tsx | 263 + .../ActivityTransferApproval.tsx | 124 + .../ActivityTransferRequest.test.tsx | 154 + .../ActivityTransferRequest.tsx | 117 + ...ityTransferToOwnDepartmentRequest.test.tsx | 372 + ...ActivityTransferToOwnDepartmentRequest.tsx | 114 + .../ActivityAuditLog.test.tsx | 140 + .../activity-audit-log/ActivityAuditLog.tsx | 80 + .../activity-table/ActivitySummaryTable.tsx | 71 + .../activity-table/ActivityTable.scss | 9 + .../activity-table/ActivityTable.test.tsx | 61 + .../activity/activity-table/ActivityTable.tsx | 164 + .../ActivityTableWithStateFilter.tsx | 71 + .../ActivityDetailsForm.test.tsx | 271 + .../ActivityDetailsForm.tsx | 441 + .../ActivityProductForm.test.tsx | 354 + .../ActivityProductForm.tsx | 277 + .../ActivitySourceForm.test.tsx | 114 + .../ActivitySourceForm.tsx | 146 + .../ActivityWizardAlertBar.test.tsx | 71 + .../ActivityWizardAlertBar.tsx | 66 + .../ManualActivityWizard.scss | 0 .../ManualActivityWizard.tsx | 122 + .../UserSearchSelect.test.tsx | 93 + .../UserSearchSelect.tsx | 104 + .../components/auto-table/AutoTable.test.tsx | 93 + .../src/components/auto-table/AutoTable.tsx | 161 + .../auto-table-row/AutoTableRow.css | 3 + .../auto-table-row/AutoTableRow.test.tsx | 43 + .../auto-table-row/AutoTableRow.tsx | 39 + .../commissions/CommissionRatesTable.test.tsx | 20 + .../commissions/CommissionRatesTable.tsx | 57 + .../CommissionRatesTableCell.test.tsx | 22 + .../commissions/CommissionRatesTableCell.tsx | 51 + .../CommissionRatesTableRow.test.tsx | 22 + .../commissions/CommissionRatesTableRow.tsx | 54 + .../commissions/CommissionTable.test.tsx | 108 + .../commissions/CommissionTable.tsx | 60 + .../commissions/TabbedCommissionView.test.tsx | 15 + .../commissions/TabbedCommissionView.tsx | 34 + .../dead-activity/DeadActivityAuditLog.tsx | 161 + .../dead-activity/DeadActivityDisplay.tsx | 173 + .../dead-activity/DeadActivityForm.tsx | 247 + .../dead-activity/DeadActivityTable.tsx | 166 + .../DepartmentDetailAssignments.test.tsx | 77 + .../DepartmentDetailAssignments.tsx | 69 + .../DepartmentDetailDisplay.test.tsx | 123 + .../DepartmentDetailDisplay.tsx | 71 + .../DepartmentProgressDisplay.tsx | 68 + .../MultiDepartmentProgressDisplay.tsx | 59 + .../TabbedDepartmentProgressView.test.tsx | 125 + .../TabbedDepartmentProgressView.tsx | 50 + .../AddNewDepartmentRow.test.tsx | 473 + .../department-table/AddNewDepartmentRow.tsx | 249 + .../department-table/DepartmentTable.scss | 3 + .../department-table/DepartmentTable.test.tsx | 63 + .../department-table/DepartmentTable.tsx | 89 + .../DepartmentTableAlertBar.test.tsx | 68 + .../DepartmentTableAlertBar.tsx | 56 + .../DepartmentTableRow.test.tsx | 586 + .../department-table/DepartmentTableRow.tsx | 37 + .../EditDepartmentTableRow.tsx | 224 + .../ViewDepartmentTableRow.tsx | 48 + .../employee/EmployeeAlertBar.test.tsx | 118 + .../components/employee/EmployeeAlertBar.tsx | 91 + .../EmployeeDepartmentAssignments.test.tsx | 15 + .../EmployeeDepartmentAssignments.tsx | 73 + .../src/components/employee/EmployeeRole.tsx | 20 + .../goal/GoalProgressControls.test.tsx | 48 + .../components/goal/GoalProgressControls.tsx | 85 + .../src/components/goal/GoalTable.scss | 9 + .../src/components/goal/GoalTable.test.tsx | 50 + .../src/components/goal/GoalTable.tsx | 82 + .../src/components/goal/GoalsDisplay.test.tsx | 77 + .../src/components/goal/GoalsDisplay.tsx | 43 + .../goal/MonthRangeControl.test.tsx | 121 + .../src/components/goal/MonthRangeControl.tsx | 102 + .../goal/TabbedProgressView.test.tsx | 80 + .../components/goal/TabbedProgressView.tsx | 53 + .../dispersal/DisersalFromAlertBar.test.tsx | 65 + .../goal/dispersal/DispersalForm.test.tsx | 150 + .../goal/dispersal/DispersalForm.tsx | 164 + .../goal/dispersal/DispersalFormAlertBar.tsx | 102 + .../DispersalReceiptErrorDisplay.test.tsx | 122 + .../DispersalReceiptErrorDisplay.tsx | 42 + .../CustomWeightedDispersalForm.test.tsx | 307 + .../CustomWeightedDispersalForm.tsx | 188 + .../DispersalWeightInput.test.tsx | 228 + .../DispersalWeightInput.tsx | 84 + .../DispersalWeightInputRow.tsx | 110 + .../src/components/layout/AppContainer.tsx | 16 + .../src/components/layout/Container.tsx | 12 + .../src/components/layout/Footer.tsx | 22 + .../src/components/layout/Layout.scss | 10 + .../src/components/layout/Navigation.tsx | 386 + .../PagedContentControls.test.tsx | 172 + .../PagedContentControls.tsx | 68 + .../payouts/EndOfEmploymentPayoutView.tsx | 67 + .../payouts/PayoutEntryTable.test.tsx | 38 + .../components/payouts/PayoutEntryTable.tsx | 41 + .../payouts/PayoutLineItemDetailRow.tsx | 28 + .../payouts/PayoutLineItemTableRow.test.tsx | 26 + .../payouts/PayoutLineItemTableRow.tsx | 52 + .../components/payouts/PayoutProfileCard.scss | 4 + .../components/payouts/PayoutProfileCard.tsx | 104 + .../components/payouts/PayoutView.test.tsx | 38 + .../src/components/payouts/PayoutView.tsx | 66 + .../AdditionalPermissionAlertBar.tsx | 51 + .../AdditionalPermissionForm.test.tsx | 253 + .../permission/AdditionalPermissionForms.tsx | 130 + .../AdditionalPermissionsDisplay.tsx | 37 + .../src/components/permission/Permission.scss | 3 + .../permission/PermissionDisplay.test.tsx | 34 + .../permission/PermissionDisplay.tsx | 31 + .../permission/PermissionFormBadgeList.tsx | 74 + .../PermissionsFormBadgeList.test.tsx | 104 + .../components/permission/PermissionsPage.tsx | 85 + .../components/profile/ProfileView.test.tsx | 68 + .../src/components/profile/ProfileView.tsx | 87 + .../components/region/RegionTable.test.tsx | 183 + .../src/components/region/RegionTable.tsx | 61 + .../components/region/RegionTableRow.test.tsx | 194 + .../src/components/region/RegionTableRow.tsx | 127 + .../UnassignedRegionalManagerAlert.test.tsx | 10 + .../region/UnassignedRegionalManagerAlert.tsx | 12 + .../role-mapping/RoleMappingTable.tsx | 140 + .../role-mapping/RoleMappingTableCell.tsx | 45 + .../role-mapping/RoleMappingTableRow.tsx | 89 + .../role-mapping/RoleMappingTableRowEdit.tsx | 112 + .../components/shared/ProductTableHeader.tsx | 19 + .../shared/ProgressPercentageTableRow.tsx | 49 + .../components/shared/ProgressTableRow.tsx | 86 + .../status-pages/NotFound404Page.tsx | 22 + .../components/status-pages/StatusPage.scss | 12 + .../src/components/status-pages/TestPage.tsx | 13 + .../src/components/tab-view/TabView.css | 7 + .../src/components/tab-view/TabView.test.tsx | 66 + .../src/components/tab-view/TabView.tsx | 83 + .../ActivityDetailPage.test.tsx | 545 + .../ActivityDetailPage.tsx | 272 + .../activity-form-page/ActivityFormPage.tsx | 52 + .../activity-page/ActivityPage.test.tsx | 54 + .../containers/activity-page/ActivityPage.tsx | 77 + .../ActivityPageFilterForm.test.tsx | 67 + .../activity-page/ActivityPageFilterForm.tsx | 265 + .../CommissionRatesEditor.test.tsx | 250 + .../CommissionRatesEditor.tsx | 139 + .../commissions-page/CommissionsForRole.tsx | 80 + .../commissions-page/CommissionsPage.tsx | 30 + .../dashboard/BusinessLineDashboard.tsx | 94 + .../dashboard/BusinessLineSummaryTable.tsx | 57 + .../containers/dashboard/DashboardPage.tsx | 95 + .../dashboard/DepartmentDashboard.tsx | 62 + .../dashboard/EmployeeDashboard.tsx | 60 + .../dashboard/EmptyDashboard.test.tsx | 10 + .../containers/dashboard/EmptyDashboard.tsx | 15 + .../containers/dashboard/RegionDashboard.tsx | 97 + .../dashboard/RegionSummaryTable.tsx | 54 + .../DeadActivityPage.test.tsx | 41 + .../dead-activity-page/DeadActivityPage.tsx | 78 + .../DeadActivityPageFilterForm.test.tsx | 67 + .../DeadActivityPageFilterForm.tsx | 237 + .../DepartmentDetailPage.test.tsx | 135 + .../DepartmentDetailPage.tsx | 86 + .../EmployeeDetailsEditAlertBar.tsx | 49 + .../employee-page/EmployeeDetailsEditPage.tsx | 129 + .../employee-page/EmployeeDetailsPage.tsx | 109 + .../containers/employee-page/EmployeePage.tsx | 90 + .../BasicDeptAssignmentTable.tsx | 66 + .../DeptAssignmentAlertBar.tsx | 54 + .../DeptAssignmentAuditTable.tsx | 95 + .../DeptAssignmentTabGroup.test.tsx | 162 + .../DeptAssignmentTabGroup.tsx | 107 + .../department-assignments/HomeIcon.tsx | 24 + .../DeptAssignmentEdit.test.tsx | 229 + .../DeptAssignmentEdit.tsx | 196 + .../DeptAssignmentNew.test.tsx | 259 + .../DeptAssignmentNew.tsx | 264 + .../DeptAssignmentRow.test.tsx | 245 + .../DeptAssignmentRow.tsx | 143 + .../DeptAssignmentTable.test.tsx | 77 + .../DeptAssignmentTable.tsx | 133 + .../eoe-list/EndOfEmploymentPage.tsx | 140 + .../containers/goals/GoalEditCell.test.tsx | 25 + .../src/containers/goals/GoalEditCell.tsx | 48 + .../goals/department/AnnualProductTotal.tsx | 39 + .../DepartmentGoalsControls.test.tsx | 37 + .../department/DepartmentGoalsControls.tsx | 125 + .../department/DepartmentGoalsDetail.test.tsx | 133 + .../department/DepartmentGoalsDetail.tsx | 44 + .../department/DepartmentGoalsEditor.test.tsx | 73 + .../department/DepartmentGoalsEditor.tsx | 238 + .../goals/department/DepartmentGoalsPage.tsx | 35 + .../department/DepartmentGoalsTable.test.tsx | 140 + .../goals/department/DepartmentGoalsTable.tsx | 91 + .../personnel/MonthlyProductTotal.test.tsx | 150 + .../goals/personnel/MonthlyProductTotal.tsx | 54 + .../personnel/PersonnelGoalsControls.test.tsx | 129 + .../personnel/PersonnelGoalsControls.tsx | 203 + .../personnel/PersonnelGoalsDetail.test.tsx | 137 + .../goals/personnel/PersonnelGoalsDetail.tsx | 42 + .../personnel/PersonnelGoalsEditor.test.tsx | 176 + .../goals/personnel/PersonnelGoalsEditor.tsx | 284 + .../goals/personnel/PersonnelGoalsPage.tsx | 35 + .../personnel/PersonnelGoalsTable.test.tsx | 170 + .../goals/personnel/PersonnelGoalsTable.tsx | 152 + .../organization-view/BusinessLineTable.tsx | 37 + .../organization-view/OrganizationTable.tsx | 32 + .../organization-view/OrganizationView.scss | 0 .../organization-view/OrganizationView.tsx | 122 + .../payout-profile/EligiblePayouts.tsx | 45 + .../payout-profile/PayoutProfileForRole.tsx | 40 + .../payout-profile/PayoutProfilePage.scss | 10 + .../payout-profile/PayoutProfilePage.tsx | 37 + .../payouts/ExportPayoutPage.test.tsx | 17 + .../containers/payouts/ExportPayoutPage.tsx | 152 + .../containers/payouts/PayoutPage.test.tsx | 24 + .../src/containers/payouts/PayoutPage.tsx | 86 + .../containers/profile/ProfilePage.test.tsx | 39 + .../src/containers/profile/ProfilePage.tsx | 208 + .../profile/ProfileResultsTable.tsx | 44 + .../profile/ProfileSearchPage.test.tsx | 57 + .../containers/profile/ProfileSearchPage.tsx | 60 + .../containers/profile/ProfileSelector.tsx | 102 + .../src/containers/profile/readme.md | 17 + .../src/containers/report-page/ReportPage.tsx | 37 + .../role-management/RoleMangement.tsx | 22 + .../containers/role-management/RoleTable.tsx | 66 + .../role-mapping-page/RoleMappingPage.tsx | 45 + .../src/containers/shared/AsyncGuard.test.tsx | 48 + .../src/containers/shared/AsyncGuard.tsx | 35 + .../shared/BusinessLineDisplay.test.tsx | 25 + .../containers/shared/BusinessLineDisplay.tsx | 21 + .../shared/DepartmentDisplay.test.tsx | 58 + .../containers/shared/DepartmentDisplay.tsx | 47 + .../containers/shared/EmployeeStatusGuard.tsx | 17 + .../shared/PermissionGuard.test.tsx | 211 + .../src/containers/shared/PermissionGuard.tsx | 47 + .../containers/shared/ProductDisplay.test.tsx | 25 + .../src/containers/shared/ProductDisplay.tsx | 17 + .../src/containers/shared/UserDisplay.css | 7 + .../src/containers/shared/UserDisplay.tsx | 70 + .../src/environments/environment.dev.ts | 98 + .../src/environments/environment.local.ts | 15 + .../src/environments/environment.prod.ts | 13 + .../src/environments/environment.uat.ts | 16 + .../src/index.scss | 120 + .../src/index.tsx | 68 + .../src/logo.svg | 1 + .../src/react-app-env.d.ts | 1 + .../src/reducers/ActivityReducer.ts | 70 + .../src/reducers/CommissionReducer.ts | 33 + .../src/reducers/DeadActivityReducer.ts | 44 + .../src/reducers/DepartmentReducer.ts | 47 + .../src/reducers/EmployeeReducer.ts | 48 + .../src/reducers/GoalReducer.ts | 75 + .../src/reducers/PayoutReducer.ts | 40 + .../src/reducers/ProfileReducer.ts | 39 + .../src/reducers/RoleMappingReducer.ts | 33 + .../src/reducers/UserAssignmentReducer.ts | 33 + .../src/reportWebVitals.ts | 15 + .../src/setupTests.ts | 12 + .../src/shared/Routes.ts | 10 + .../src/shared/assets/LMCU-Blue-Logo-Only.png | Bin 0 -> 16243 bytes .../src/shared/assets/LMCU-Blue.png | Bin 0 -> 24751 bytes .../src/shared/assets/LMCU-Blue.svg | 70 + .../shared/assets/LMCU-White-Logo-Only.png | Bin 0 -> 7637 bytes .../assets/light-house-corner-watermark.png | Bin 0 -> 8305 bytes .../src/shared/configuration/Configuration.ts | 37 + .../src/shared/dao/ProfileDao.ts | 133 + .../src/shared/enumerations/AsyncState.ts | 10 + .../src/shared/enumerations/PermissionCode.ts | 53 + .../src/shared/enumerations/Product.ts | 202 + .../src/shared/models/Activity.ts | 328 + .../src/shared/models/Auditable.ts | 19 + .../src/shared/models/Commission.ts | 19 + .../src/shared/models/DeadActivity.ts | 101 + .../src/shared/models/Department.ts | 26 + .../src/shared/models/DepartmentAssignment.ts | 45 + .../src/shared/models/Employee.ts | 65 + .../src/shared/models/Goal.ts | 102 + .../src/shared/models/GoalDispersal.ts | 53 + .../src/shared/models/Payout.ts | 75 + .../src/shared/models/RoleMapping.ts | 16 + .../src/shared/models/Settings.ts | 76 + .../src/shared/models/UserAssignment.ts | 14 + .../src/shared/providers/SettingsProvider.tsx | 34 + .../src/shared/security/interceptor.ts | 47 + .../shared/services/ActivityService.test.ts | 999 + .../src/shared/services/ActivityService.ts | 852 + .../shared/services/CommissionService.test.ts | 81 + .../src/shared/services/CommissionService.ts | 80 + .../src/shared/services/CookieService.ts | 47 + .../src/shared/services/DateTimeService.ts | 23 + .../shared/services/DeadActivityService.ts | 146 + .../shared/services/DepartmentService.test.ts | 49 + .../src/shared/services/DepartmentService.ts | 158 + .../shared/services/EmployeeService.test.ts | 146 + .../src/shared/services/EmployeeService.ts | 189 + .../shared/services/ExportCsvService.test.ts | 234 + .../src/shared/services/ExportCsvService.ts | 180 + .../shared/services/GoalDispersalService.ts | 87 + .../src/shared/services/GoalService.test.ts | 727 + .../src/shared/services/GoalService.ts | 773 + .../src/shared/services/InputService.test.ts | 62 + .../src/shared/services/InputService.ts | 39 + .../shared/services/LocalStorageService.ts | 34 + .../src/shared/services/PayoutService.ts | 164 + .../shared/services/ProductService.test.ts | 114 + .../src/shared/services/ProductService.ts | 18 + .../shared/services/ProfileService.test.ts | 274 + .../src/shared/services/ProfileService.ts | 135 + .../src/shared/services/RoleMappingService.ts | 64 + .../shared/services/SettingsService.test.ts | 16 + .../src/shared/services/SettingsService.ts | 86 + .../services/UserAssignmentService.test.ts | 117 + .../shared/services/UserAssignmentService.ts | 57 + .../shared/testing/mocks/MockAppSettings.ts | 985 + .../mocks/MockDepartmentAssignments.ts | 101 + .../shared/testing/mocks/MockDepartments.ts | 24 + .../shared/testing/mocks/MockProfileState.ts | 36 + .../src/shared/testing/mocks/MockRoles.ts | 94 + .../src/shared/types/IAdalRoleActions.ts | 4 + .../src/shared/types/IAsyncStateContainer.ts | 13 + .../src/shared/types/IMemberSecurityMeta.ts | 5 + .../src/shared/types/IMonthRange.ts | 6 + .../src/shared/types/IMultiSelectOption.ts | 6 + .../src/shared/types/IPagedResponse.ts | 13 + .../src/store/Store.tsx | 95 + .../src/theme/theme.scss | 4 + .../src/translate/Translate.ts | 15 + .../src/translate/translate.json | 874 + .../src/types/minimatch/index.d.ts | 5 + .../tsconfig.json | 26 + .../version.js | 23 + 426 files changed, 60574 insertions(+), 1 deletion(-) create mode 100644 LCU.WebApps.Operations.IncentiveManager/.dockerignore create mode 100644 LCU.WebApps.Operations.IncentiveManager/.editorconfig create mode 100644 LCU.WebApps.Operations.IncentiveManager/.eslintrc.json create mode 100644 LCU.WebApps.Operations.IncentiveManager/.gitignore create mode 100644 LCU.WebApps.Operations.IncentiveManager/.npmrc create mode 100644 LCU.WebApps.Operations.IncentiveManager/.prettierrc.json create mode 100644 LCU.WebApps.Operations.IncentiveManager/CHANGELOG.md create mode 100644 LCU.WebApps.Operations.IncentiveManager/Dockerfile create mode 100644 LCU.WebApps.Operations.IncentiveManager/README.md create mode 100644 LCU.WebApps.Operations.IncentiveManager/SetupLocalSSL.ps1 create mode 100644 LCU.WebApps.Operations.IncentiveManager/azdo_pipelines/azure-k8s-pipeline.yaml create mode 100644 LCU.WebApps.Operations.IncentiveManager/azdo_pipelines/azure-pipelines-feature-branches.yaml create mode 100644 LCU.WebApps.Operations.IncentiveManager/azdo_pipelines/azure-pipelines-prd.yaml create mode 100644 LCU.WebApps.Operations.IncentiveManager/azdo_pipelines/azure-pipelines-qa.yaml create mode 100644 LCU.WebApps.Operations.IncentiveManager/azdo_pipelines/azure-pipelines-uat.yaml create mode 100644 LCU.WebApps.Operations.IncentiveManager/azdo_pipelines/config-files/prd-pvars.yaml create mode 100644 LCU.WebApps.Operations.IncentiveManager/azdo_pipelines/config-files/qa-pvars.yaml create mode 100644 LCU.WebApps.Operations.IncentiveManager/azdo_pipelines/config-files/uat-pvars.yaml create mode 100644 LCU.WebApps.Operations.IncentiveManager/copy-test-results.sh create mode 100644 LCU.WebApps.Operations.IncentiveManager/docker-build.sh create mode 100644 LCU.WebApps.Operations.IncentiveManager/docker-run.sh create mode 100644 LCU.WebApps.Operations.IncentiveManager/docker-rundev.sh create mode 100644 LCU.WebApps.Operations.IncentiveManager/isDockerEnv.js create mode 100644 LCU.WebApps.Operations.IncentiveManager/jest.config.js create mode 100644 LCU.WebApps.Operations.IncentiveManager/kustomize/base/Deployment.yaml create mode 100644 LCU.WebApps.Operations.IncentiveManager/kustomize/base/Service.yaml create mode 100644 LCU.WebApps.Operations.IncentiveManager/kustomize/base/kustomization.yaml create mode 100644 LCU.WebApps.Operations.IncentiveManager/kustomize/base/secrets-provider-class.yaml create mode 100644 LCU.WebApps.Operations.IncentiveManager/kustomize/base/virtual-service.yaml create mode 100644 LCU.WebApps.Operations.IncentiveManager/kustomize/overlays/local/common/kustomization.yaml create mode 100644 LCU.WebApps.Operations.IncentiveManager/kustomize/overlays/local/common/set_appsettings_deployment.yaml create mode 100644 LCU.WebApps.Operations.IncentiveManager/kustomize/overlays/local/kustomization.yaml create mode 100644 LCU.WebApps.Operations.IncentiveManager/kustomize/overlays/local/normal/configmap.yaml create mode 100644 LCU.WebApps.Operations.IncentiveManager/kustomize/overlays/local/normal/kustomization.yaml create mode 100644 LCU.WebApps.Operations.IncentiveManager/kustomize/overlays/local/normal/set_virtual_service.yaml create mode 100644 LCU.WebApps.Operations.IncentiveManager/kustomize/overlays/prd/common/kustomization.yaml create mode 100644 LCU.WebApps.Operations.IncentiveManager/kustomize/overlays/prd/common/set_appsettings_deployment.yaml create mode 100644 LCU.WebApps.Operations.IncentiveManager/kustomize/overlays/prd/kustomization.yaml create mode 100644 LCU.WebApps.Operations.IncentiveManager/kustomize/overlays/prd/normal/configmap.yaml create mode 100644 LCU.WebApps.Operations.IncentiveManager/kustomize/overlays/prd/normal/kustomization.yaml create mode 100644 LCU.WebApps.Operations.IncentiveManager/kustomize/overlays/prd/normal/set_virtual_service.yaml create mode 100644 LCU.WebApps.Operations.IncentiveManager/kustomize/overlays/qa/common/kustomization.yaml create mode 100644 LCU.WebApps.Operations.IncentiveManager/kustomize/overlays/qa/common/set_appsettings_deployment.yaml create mode 100644 LCU.WebApps.Operations.IncentiveManager/kustomize/overlays/qa/kustomization.yaml create mode 100644 LCU.WebApps.Operations.IncentiveManager/kustomize/overlays/qa/normal/configmap.yaml create mode 100644 LCU.WebApps.Operations.IncentiveManager/kustomize/overlays/qa/normal/kustomization.yaml create mode 100644 LCU.WebApps.Operations.IncentiveManager/kustomize/overlays/qa/normal/set_virtual_service.yaml create mode 100644 LCU.WebApps.Operations.IncentiveManager/kustomize/overlays/uat/common/kustomization.yaml create mode 100644 LCU.WebApps.Operations.IncentiveManager/kustomize/overlays/uat/common/set_appsettings_deployment.yaml create mode 100644 LCU.WebApps.Operations.IncentiveManager/kustomize/overlays/uat/kustomization.yaml create mode 100644 LCU.WebApps.Operations.IncentiveManager/kustomize/overlays/uat/normal/configmap.yaml create mode 100644 LCU.WebApps.Operations.IncentiveManager/kustomize/overlays/uat/normal/kustomization.yaml create mode 100644 LCU.WebApps.Operations.IncentiveManager/kustomize/overlays/uat/normal/set_virtual_service.yaml create mode 100644 LCU.WebApps.Operations.IncentiveManager/nginx.conf create mode 100644 LCU.WebApps.Operations.IncentiveManager/package-lock.json create mode 100644 LCU.WebApps.Operations.IncentiveManager/package.json create mode 100644 LCU.WebApps.Operations.IncentiveManager/public/favicon.ico create mode 100644 LCU.WebApps.Operations.IncentiveManager/public/index.html create mode 100644 LCU.WebApps.Operations.IncentiveManager/public/logo192.png create mode 100644 LCU.WebApps.Operations.IncentiveManager/public/logo512.png create mode 100644 LCU.WebApps.Operations.IncentiveManager/public/manifest.json create mode 100644 LCU.WebApps.Operations.IncentiveManager/public/robots.txt create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/App.test.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/App.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/actions/ActivityActions.ts create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/actions/CommissionActions.ts create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/actions/DeadActivityActions.ts create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/actions/DepartmentActions.ts create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/actions/EmployeeActions.ts create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/actions/GoalActions.ts create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/actions/PayoutActions.ts create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/actions/ProfileActions.ts create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/actions/RoleMappingActions.ts create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/actions/UserAssignmentActions.ts create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/authConfig.ts create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/components/activity-detail/ActivityApproval.test.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/components/activity-detail/ActivityApproval.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/components/activity-detail/ActivityDetailAlertBar.test.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/components/activity-detail/ActivityDetailAlertBar.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/components/activity-detail/ActivityDetailDisplay.test.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/components/activity-detail/ActivityDetailDisplay.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/components/activity-detail/ActivityDetailUpdatesTable.test.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/components/activity-detail/ActivityDetailUpdatesTable.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/components/activity-detail/ActivityDisplay.test.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/components/activity-detail/ActivityDisplay.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/components/activity-detail/ActivityMetaDataDisplay.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/components/activity-detail/ActivityPayoutsDisplay.test.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/components/activity-detail/ActivityPayoutsDisplay.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/components/activity-detail/ActivityStateChangesTable.test.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/components/activity-detail/ActivityStateChangesTable.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/components/activity-detail/ActivityTransferApproval.test.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/components/activity-detail/ActivityTransferApproval.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/components/activity-detail/ActivityTransferRequest.test.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/components/activity-detail/ActivityTransferRequest.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/components/activity-detail/ActivityTransferToOwnDepartmentRequest.test.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/components/activity-detail/ActivityTransferToOwnDepartmentRequest.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/components/activity/activity-audit-log/ActivityAuditLog.test.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/components/activity/activity-audit-log/ActivityAuditLog.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/components/activity/activity-table/ActivitySummaryTable.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/components/activity/activity-table/ActivityTable.scss create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/components/activity/activity-table/ActivityTable.test.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/components/activity/activity-table/ActivityTable.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/components/activity/activity-table/ActivityTableWithStateFilter.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/components/activity/manual-activity-wizard/ActivityDetailsForm.test.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/components/activity/manual-activity-wizard/ActivityDetailsForm.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/components/activity/manual-activity-wizard/ActivityProductForm.test.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/components/activity/manual-activity-wizard/ActivityProductForm.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/components/activity/manual-activity-wizard/ActivitySourceForm.test.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/components/activity/manual-activity-wizard/ActivitySourceForm.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/components/activity/manual-activity-wizard/ActivityWizardAlertBar.test.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/components/activity/manual-activity-wizard/ActivityWizardAlertBar.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/components/activity/manual-activity-wizard/ManualActivityWizard.scss create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/components/activity/manual-activity-wizard/ManualActivityWizard.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/components/activity/manual-activity-wizard/UserSearchSelect.test.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/components/activity/manual-activity-wizard/UserSearchSelect.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/components/auto-table/AutoTable.test.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/components/auto-table/AutoTable.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/components/auto-table/auto-table-row/AutoTableRow.css create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/components/auto-table/auto-table-row/AutoTableRow.test.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/components/auto-table/auto-table-row/AutoTableRow.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/components/commissions/CommissionRatesTable.test.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/components/commissions/CommissionRatesTable.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/components/commissions/CommissionRatesTableCell.test.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/components/commissions/CommissionRatesTableCell.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/components/commissions/CommissionRatesTableRow.test.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/components/commissions/CommissionRatesTableRow.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/components/commissions/CommissionTable.test.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/components/commissions/CommissionTable.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/components/commissions/TabbedCommissionView.test.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/components/commissions/TabbedCommissionView.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/components/dead-activity/DeadActivityAuditLog.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/components/dead-activity/DeadActivityDisplay.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/components/dead-activity/DeadActivityForm.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/components/dead-activity/DeadActivityTable.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/components/department-detail/DepartmentDetailAssignments.test.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/components/department-detail/DepartmentDetailAssignments.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/components/department-detail/DepartmentDetailDisplay.test.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/components/department-detail/DepartmentDetailDisplay.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/components/department-progress/DepartmentProgressDisplay.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/components/department-progress/MultiDepartmentProgressDisplay.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/components/department-progress/TabbedDepartmentProgressView.test.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/components/department-progress/TabbedDepartmentProgressView.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/components/department-table/AddNewDepartmentRow.test.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/components/department-table/AddNewDepartmentRow.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/components/department-table/DepartmentTable.scss create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/components/department-table/DepartmentTable.test.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/components/department-table/DepartmentTable.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/components/department-table/DepartmentTableAlertBar.test.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/components/department-table/DepartmentTableAlertBar.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/components/department-table/DepartmentTableRow.test.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/components/department-table/DepartmentTableRow.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/components/department-table/EditDepartmentTableRow.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/components/department-table/ViewDepartmentTableRow.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/components/employee/EmployeeAlertBar.test.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/components/employee/EmployeeAlertBar.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/components/employee/EmployeeDepartmentAssignments.test.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/components/employee/EmployeeDepartmentAssignments.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/components/employee/EmployeeRole.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/components/goal/GoalProgressControls.test.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/components/goal/GoalProgressControls.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/components/goal/GoalTable.scss create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/components/goal/GoalTable.test.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/components/goal/GoalTable.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/components/goal/GoalsDisplay.test.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/components/goal/GoalsDisplay.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/components/goal/MonthRangeControl.test.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/components/goal/MonthRangeControl.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/components/goal/TabbedProgressView.test.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/components/goal/TabbedProgressView.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/components/goal/dispersal/DisersalFromAlertBar.test.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/components/goal/dispersal/DispersalForm.test.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/components/goal/dispersal/DispersalForm.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/components/goal/dispersal/DispersalFormAlertBar.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/components/goal/dispersal/DispersalReceiptErrorDisplay.test.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/components/goal/dispersal/DispersalReceiptErrorDisplay.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/components/goal/dispersal/custom-weighted-dispersal/CustomWeightedDispersalForm.test.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/components/goal/dispersal/custom-weighted-dispersal/CustomWeightedDispersalForm.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/components/goal/dispersal/custom-weighted-dispersal/DispersalWeightInput.test.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/components/goal/dispersal/custom-weighted-dispersal/DispersalWeightInput.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/components/goal/dispersal/custom-weighted-dispersal/DispersalWeightInputRow.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/components/layout/AppContainer.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/components/layout/Container.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/components/layout/Footer.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/components/layout/Layout.scss create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/components/layout/Navigation.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/components/paged-content-controls/PagedContentControls.test.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/components/paged-content-controls/PagedContentControls.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/components/payouts/EndOfEmploymentPayoutView.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/components/payouts/PayoutEntryTable.test.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/components/payouts/PayoutEntryTable.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/components/payouts/PayoutLineItemDetailRow.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/components/payouts/PayoutLineItemTableRow.test.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/components/payouts/PayoutLineItemTableRow.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/components/payouts/PayoutProfileCard.scss create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/components/payouts/PayoutProfileCard.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/components/payouts/PayoutView.test.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/components/payouts/PayoutView.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/components/permission/AdditionalPermissionAlertBar.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/components/permission/AdditionalPermissionForm.test.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/components/permission/AdditionalPermissionForms.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/components/permission/AdditionalPermissionsDisplay.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/components/permission/Permission.scss create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/components/permission/PermissionDisplay.test.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/components/permission/PermissionDisplay.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/components/permission/PermissionFormBadgeList.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/components/permission/PermissionsFormBadgeList.test.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/components/permission/PermissionsPage.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/components/profile/ProfileView.test.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/components/profile/ProfileView.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/components/region/RegionTable.test.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/components/region/RegionTable.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/components/region/RegionTableRow.test.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/components/region/RegionTableRow.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/components/region/UnassignedRegionalManagerAlert.test.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/components/region/UnassignedRegionalManagerAlert.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/components/role-mapping/RoleMappingTable.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/components/role-mapping/RoleMappingTableCell.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/components/role-mapping/RoleMappingTableRow.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/components/role-mapping/RoleMappingTableRowEdit.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/components/shared/ProductTableHeader.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/components/shared/ProgressPercentageTableRow.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/components/shared/ProgressTableRow.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/components/status-pages/NotFound404Page.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/components/status-pages/StatusPage.scss create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/components/status-pages/TestPage.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/components/tab-view/TabView.css create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/components/tab-view/TabView.test.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/components/tab-view/TabView.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/containers/activity-detail-page/ActivityDetailPage.test.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/containers/activity-detail-page/ActivityDetailPage.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/containers/activity-form-page/ActivityFormPage.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/containers/activity-page/ActivityPage.test.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/containers/activity-page/ActivityPage.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/containers/activity-page/ActivityPageFilterForm.test.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/containers/activity-page/ActivityPageFilterForm.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/containers/commissions-page/CommissionRatesEditor.test.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/containers/commissions-page/CommissionRatesEditor.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/containers/commissions-page/CommissionsForRole.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/containers/commissions-page/CommissionsPage.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/containers/dashboard/BusinessLineDashboard.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/containers/dashboard/BusinessLineSummaryTable.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/containers/dashboard/DashboardPage.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/containers/dashboard/DepartmentDashboard.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/containers/dashboard/EmployeeDashboard.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/containers/dashboard/EmptyDashboard.test.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/containers/dashboard/EmptyDashboard.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/containers/dashboard/RegionDashboard.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/containers/dashboard/RegionSummaryTable.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/containers/dead-activity-page/DeadActivityPage.test.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/containers/dead-activity-page/DeadActivityPage.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/containers/dead-activity-page/DeadActivityPageFilterForm.test.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/containers/dead-activity-page/DeadActivityPageFilterForm.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/containers/department-detail-page/DepartmentDetailPage.test.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/containers/department-detail-page/DepartmentDetailPage.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/containers/employee-page/EmployeeDetailsEditAlertBar.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/containers/employee-page/EmployeeDetailsEditPage.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/containers/employee-page/EmployeeDetailsPage.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/containers/employee-page/EmployeePage.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/containers/employee-page/department-assignments/BasicDeptAssignmentTable.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/containers/employee-page/department-assignments/DeptAssignmentAlertBar.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/containers/employee-page/department-assignments/DeptAssignmentAuditTable.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/containers/employee-page/department-assignments/DeptAssignmentTabGroup.test.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/containers/employee-page/department-assignments/DeptAssignmentTabGroup.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/containers/employee-page/department-assignments/HomeIcon.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/containers/employee-page/department-assignments/department-assignment-table/DeptAssignmentEdit.test.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/containers/employee-page/department-assignments/department-assignment-table/DeptAssignmentEdit.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/containers/employee-page/department-assignments/department-assignment-table/DeptAssignmentNew.test.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/containers/employee-page/department-assignments/department-assignment-table/DeptAssignmentNew.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/containers/employee-page/department-assignments/department-assignment-table/DeptAssignmentRow.test.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/containers/employee-page/department-assignments/department-assignment-table/DeptAssignmentRow.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/containers/employee-page/department-assignments/department-assignment-table/DeptAssignmentTable.test.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/containers/employee-page/department-assignments/department-assignment-table/DeptAssignmentTable.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/containers/eoe-list/EndOfEmploymentPage.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/containers/goals/GoalEditCell.test.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/containers/goals/GoalEditCell.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/containers/goals/department/AnnualProductTotal.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/containers/goals/department/DepartmentGoalsControls.test.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/containers/goals/department/DepartmentGoalsControls.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/containers/goals/department/DepartmentGoalsDetail.test.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/containers/goals/department/DepartmentGoalsDetail.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/containers/goals/department/DepartmentGoalsEditor.test.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/containers/goals/department/DepartmentGoalsEditor.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/containers/goals/department/DepartmentGoalsPage.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/containers/goals/department/DepartmentGoalsTable.test.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/containers/goals/department/DepartmentGoalsTable.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/containers/goals/personnel/MonthlyProductTotal.test.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/containers/goals/personnel/MonthlyProductTotal.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/containers/goals/personnel/PersonnelGoalsControls.test.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/containers/goals/personnel/PersonnelGoalsControls.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/containers/goals/personnel/PersonnelGoalsDetail.test.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/containers/goals/personnel/PersonnelGoalsDetail.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/containers/goals/personnel/PersonnelGoalsEditor.test.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/containers/goals/personnel/PersonnelGoalsEditor.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/containers/goals/personnel/PersonnelGoalsPage.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/containers/goals/personnel/PersonnelGoalsTable.test.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/containers/goals/personnel/PersonnelGoalsTable.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/containers/organization-view/BusinessLineTable.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/containers/organization-view/OrganizationTable.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/containers/organization-view/OrganizationView.scss create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/containers/organization-view/OrganizationView.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/containers/payout-profile/EligiblePayouts.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/containers/payout-profile/PayoutProfileForRole.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/containers/payout-profile/PayoutProfilePage.scss create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/containers/payout-profile/PayoutProfilePage.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/containers/payouts/ExportPayoutPage.test.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/containers/payouts/ExportPayoutPage.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/containers/payouts/PayoutPage.test.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/containers/payouts/PayoutPage.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/containers/profile/ProfilePage.test.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/containers/profile/ProfilePage.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/containers/profile/ProfileResultsTable.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/containers/profile/ProfileSearchPage.test.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/containers/profile/ProfileSearchPage.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/containers/profile/ProfileSelector.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/containers/profile/readme.md create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/containers/report-page/ReportPage.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/containers/role-management/RoleMangement.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/containers/role-management/RoleTable.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/containers/role-mapping-page/RoleMappingPage.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/containers/shared/AsyncGuard.test.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/containers/shared/AsyncGuard.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/containers/shared/BusinessLineDisplay.test.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/containers/shared/BusinessLineDisplay.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/containers/shared/DepartmentDisplay.test.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/containers/shared/DepartmentDisplay.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/containers/shared/EmployeeStatusGuard.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/containers/shared/PermissionGuard.test.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/containers/shared/PermissionGuard.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/containers/shared/ProductDisplay.test.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/containers/shared/ProductDisplay.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/containers/shared/UserDisplay.css create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/containers/shared/UserDisplay.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/environments/environment.dev.ts create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/environments/environment.local.ts create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/environments/environment.prod.ts create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/environments/environment.uat.ts create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/index.scss create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/index.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/logo.svg create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/react-app-env.d.ts create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/reducers/ActivityReducer.ts create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/reducers/CommissionReducer.ts create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/reducers/DeadActivityReducer.ts create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/reducers/DepartmentReducer.ts create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/reducers/EmployeeReducer.ts create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/reducers/GoalReducer.ts create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/reducers/PayoutReducer.ts create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/reducers/ProfileReducer.ts create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/reducers/RoleMappingReducer.ts create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/reducers/UserAssignmentReducer.ts create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/reportWebVitals.ts create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/setupTests.ts create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/shared/Routes.ts create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/shared/assets/LMCU-Blue-Logo-Only.png create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/shared/assets/LMCU-Blue.png create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/shared/assets/LMCU-Blue.svg create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/shared/assets/LMCU-White-Logo-Only.png create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/shared/assets/light-house-corner-watermark.png create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/shared/configuration/Configuration.ts create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/shared/dao/ProfileDao.ts create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/shared/enumerations/AsyncState.ts create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/shared/enumerations/PermissionCode.ts create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/shared/enumerations/Product.ts create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/shared/models/Activity.ts create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/shared/models/Auditable.ts create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/shared/models/Commission.ts create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/shared/models/DeadActivity.ts create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/shared/models/Department.ts create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/shared/models/DepartmentAssignment.ts create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/shared/models/Employee.ts create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/shared/models/Goal.ts create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/shared/models/GoalDispersal.ts create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/shared/models/Payout.ts create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/shared/models/RoleMapping.ts create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/shared/models/Settings.ts create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/shared/models/UserAssignment.ts create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/shared/providers/SettingsProvider.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/shared/security/interceptor.ts create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/shared/services/ActivityService.test.ts create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/shared/services/ActivityService.ts create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/shared/services/CommissionService.test.ts create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/shared/services/CommissionService.ts create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/shared/services/CookieService.ts create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/shared/services/DateTimeService.ts create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/shared/services/DeadActivityService.ts create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/shared/services/DepartmentService.test.ts create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/shared/services/DepartmentService.ts create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/shared/services/EmployeeService.test.ts create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/shared/services/EmployeeService.ts create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/shared/services/ExportCsvService.test.ts create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/shared/services/ExportCsvService.ts create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/shared/services/GoalDispersalService.ts create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/shared/services/GoalService.test.ts create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/shared/services/GoalService.ts create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/shared/services/InputService.test.ts create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/shared/services/InputService.ts create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/shared/services/LocalStorageService.ts create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/shared/services/PayoutService.ts create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/shared/services/ProductService.test.ts create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/shared/services/ProductService.ts create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/shared/services/ProfileService.test.ts create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/shared/services/ProfileService.ts create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/shared/services/RoleMappingService.ts create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/shared/services/SettingsService.test.ts create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/shared/services/SettingsService.ts create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/shared/services/UserAssignmentService.test.ts create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/shared/services/UserAssignmentService.ts create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/shared/testing/mocks/MockAppSettings.ts create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/shared/testing/mocks/MockDepartmentAssignments.ts create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/shared/testing/mocks/MockDepartments.ts create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/shared/testing/mocks/MockProfileState.ts create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/shared/testing/mocks/MockRoles.ts create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/shared/types/IAdalRoleActions.ts create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/shared/types/IAsyncStateContainer.ts create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/shared/types/IMemberSecurityMeta.ts create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/shared/types/IMonthRange.ts create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/shared/types/IMultiSelectOption.ts create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/shared/types/IPagedResponse.ts create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/store/Store.tsx create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/theme/theme.scss create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/translate/Translate.ts create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/translate/translate.json create mode 100644 LCU.WebApps.Operations.IncentiveManager/src/types/minimatch/index.d.ts create mode 100644 LCU.WebApps.Operations.IncentiveManager/tsconfig.json create mode 100644 LCU.WebApps.Operations.IncentiveManager/version.js diff --git a/LCU.API.Collections.CollectionsApi/kustomize/overlays/qa/appsettings/appsettings.json b/LCU.API.Collections.CollectionsApi/kustomize/overlays/qa/appsettings/appsettings.json index 3e144d4..9005fec 100644 --- a/LCU.API.Collections.CollectionsApi/kustomize/overlays/qa/appsettings/appsettings.json +++ b/LCU.API.Collections.CollectionsApi/kustomize/overlays/qa/appsettings/appsettings.json @@ -4,6 +4,6 @@ }, "primary": { "SeqUrl": "https://qa-seq.lcu-internal.com", - "TemenosUrl": "https://lms-test-dmz-lcu.temenoscloudna.com" + "TemenosUrl": "https://lms-test-app-lcu.temenoscloudna.com" } } diff --git a/LCU.WebApps.Operations.IncentiveManager/.dockerignore b/LCU.WebApps.Operations.IncentiveManager/.dockerignore new file mode 100644 index 0000000..ac5a638 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/.dockerignore @@ -0,0 +1,6 @@ +node_modules +build +Dockerfile +./junit +junit.xml +src/environments/version.ts diff --git a/LCU.WebApps.Operations.IncentiveManager/.editorconfig b/LCU.WebApps.Operations.IncentiveManager/.editorconfig new file mode 100644 index 0000000..c6d06a0 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/.editorconfig @@ -0,0 +1,30 @@ + +# Set to true to +root = true + +# Indicates that the following rules will apply to all files +[*] +# Choose in between tab or space +indent_style = space +# number of columns used per indentation level +indent_size = 4 +# line break representation +end_of_line = lf +# character set +charset = utf-8 +# Remove whitespace at the end of lines +trim_trailing_whitespace = true +# Insert a line at the end of a file +insert_final_newline = true + +# applies to files with the .md extension +[*.md] +trim_trailing_whitespace = false + +# applies to js files inside the src folder +[src/**.js] +trim_trailing_whitespace = true + +# applied to package.json and .travis.yml +[{package.json,.travis.yml}] +indent_size = 2 \ No newline at end of file diff --git a/LCU.WebApps.Operations.IncentiveManager/.eslintrc.json b/LCU.WebApps.Operations.IncentiveManager/.eslintrc.json new file mode 100644 index 0000000..4b32a88 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/.eslintrc.json @@ -0,0 +1,64 @@ +{ + "parser": "@typescript-eslint/parser", + "extends": [ + "plugin:react/recommended", + "plugin:@typescript-eslint/recommended" + ], + "parserOptions": { + "ecmaVersion": 2018, + "sourceType": "module", + "ecmaFeatures": { + "jsx": true + } + }, + "settings": { + "react": { + "version": "detect" + } + }, + "rules": { + "react/prop-types": "off", + "@typescript-eslint/explicit-member-accessibility": [ + "off", + { + "accessibility": "explicit" + } + ], + "@typescript-eslint/member-delimiter-style": [ + "warn", + { + "multiline": { + "delimiter": "semi", + "requireLast": true + }, + "singleline": { + "delimiter": "semi", + "requireLast": false + } + } + ], + "@typescript-eslint/quotes": "warn", + "sort-imports": "off", + "no-console": "warn", + "prefer-const": "warn", + "react/jsx-boolean-value": "warn", + "react/jsx-curly-spacing": [ + "warn", + { + "when": "never" + } + ], + "react/jsx-equals-spacing": ["warn", "never"], + "react/jsx-key": "warn", + "react/jsx-no-bind": [ + "warn", + { + "allowArrowFunctions": true + } + ], + "react/jsx-wrap-multilines": "warn", + "@typescript-eslint/explicit-module-boundary-types": "off", + "@typescript-eslint/no-explicit-any": "off", + "semi": "warn" + } +} diff --git a/LCU.WebApps.Operations.IncentiveManager/.gitignore b/LCU.WebApps.Operations.IncentiveManager/.gitignore new file mode 100644 index 0000000..2d4f25d --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/.gitignore @@ -0,0 +1,131 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +.cert + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# Bower dependency directory (https://bower.io/) +bower_components + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (https://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules/ +jspm_packages/ + +# TypeScript v1 declaration files +typings/ + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variables file +.env + +# next.js build output +.next +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# Bower dependency directory (https://bower.io/) +bower_components + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (https://nodejs.org/api/addons.html) +build/ + +# Dependency directories +node_modules/ +jspm_packages/ + +# TypeScript v1 declaration files +typings/ + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variables file +.env + +# next.js build output +.next + +.idea + +# VSCode environment details +.vscode + +# the version file shouldn't be saved +src/environments/version.ts + +# output of tests +junit.xml +./junit diff --git a/LCU.WebApps.Operations.IncentiveManager/.npmrc b/LCU.WebApps.Operations.IncentiveManager/.npmrc new file mode 100644 index 0000000..7a04350 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/.npmrc @@ -0,0 +1,3 @@ +# registry=https://landmarkcu.pkgs.visualstudio.com/_packaging/LCU-Internal/npm/registry/ + +# always-auth=true diff --git a/LCU.WebApps.Operations.IncentiveManager/.prettierrc.json b/LCU.WebApps.Operations.IncentiveManager/.prettierrc.json new file mode 100644 index 0000000..38cbd39 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/.prettierrc.json @@ -0,0 +1,4 @@ +{ + "trailingComma": "none", + "semi": true +} diff --git a/LCU.WebApps.Operations.IncentiveManager/CHANGELOG.md b/LCU.WebApps.Operations.IncentiveManager/CHANGELOG.md new file mode 100644 index 0000000..96e7915 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/CHANGELOG.md @@ -0,0 +1,207 @@ +# Changelog + +All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. + +### [0.1.4](https://landmarkcu.visualstudio.com///compare/v0.1.3...v0.1.4) (2021-12-15) + +### [0.1.3](https://landmarkcu.visualstudio.com///compare/v0.1.2...v0.1.3) (2021-12-09) + +### Features + +- **component:** Adding in RequestToOwnDepartment to Activity ([88b0954](https://landmarkcu.visualstudio.com///commit/88b0954eadd9ff8fce45830284a9642b523ccdc8)) +- **CustomWeightedDispersal:** Add Custom Weighted Dispersal form ([f2cee9b](https://landmarkcu.visualstudio.com///commit/f2cee9b6f2fbacf3f8435321c369960045795349)) +- **DeadActivityDisplay:** Add Basic Dead Activity display Mirroring Living Activity Display ([e153223](https://landmarkcu.visualstudio.com///commit/e15322329a916099e94d68ad1118268c6832d199)) +- **Departments:** exclude inactive ([e9babe4](https://landmarkcu.visualstudio.com///commit/e9babe485cba4846a24d477718acb6f0763f8bc2)) +- **ManualActivityForm:** Add Source ID To Manual LIC Referrals ([928adfb](https://landmarkcu.visualstudio.com///commit/928adfba7dccc2b34a45ea9a9f1682a7d2492c06)) + +### Bug Fixes + +- **Department:** fix translate entry spelling ([fa029b0](https://landmarkcu.visualstudio.com///commit/fa029b07212f7c1dcb5de1ca185d26e890813355)) +- **Dispersal:** Require 100% ([d803f08](https://landmarkcu.visualstudio.com///commit/d803f085f93dd77951f0cc1adbedd42214c8f7c1)) +- **Dispersal:** restrict dispersal to goal products ([cdcd83e](https://landmarkcu.visualstudio.com///commit/cdcd83eca0ed0f0319acfbe4fb4cb43c70dc4293)) +- **EditDepartment:** fix spelling error ([de3b2e0](https://landmarkcu.visualstudio.com///commit/de3b2e06a33bfcba1da8eb461f65306f7fdb2b13)) +- **NewActivity:** remove account new balance from form ([710ab97](https://landmarkcu.visualstudio.com///commit/710ab977bcf3862637b5bfd560d25b8f50a6eec0)) + +### [0.1.2](https://landmarkcu.visualstudio.com///compare/v0.1.1...v0.1.2) (2021-11-23) + +### Features + +- **Auth:** ask server for auth ([afd7440](https://landmarkcu.visualstudio.com///commit/afd74404633f237031f2a6c9dbb1b98efb0f2bed)) +- **Departments:** hide deactivated, update org nbr ([b3128e5](https://landmarkcu.visualstudio.com///commit/b3128e5ee985e7456807029153e85ef272cc4401)) +- **Goals:** Import, Export all goals ([e26f573](https://landmarkcu.visualstudio.com///commit/e26f57329fbfd7dc8678b97c0caf6a63b3763c58)) + +### Bug Fixes + +- **Dashboard:** employee dashboard show name ([025fdfd](https://landmarkcu.visualstudio.com///commit/025fdfd78ab73dc430dfa5bc52573d800b3bc6dc)) +- **EoE:** fix index on eoe page ([af12cd6](https://landmarkcu.visualstudio.com///commit/af12cd6efe76801f6c3d7eb67287deecb3567d1a)) +- **ManualActivity:** less required fields for referrals ([0fec644](https://landmarkcu.visualstudio.com///commit/0fec6447e9e041b0577f9d9812c37fc40c489e53)) +- **NewAccountBalance:** remove from display ([7d1b16f](https://landmarkcu.visualstudio.com///commit/7d1b16fa7b608114727ba375cc0783414343afa2)) + +### 0.1.1 (2021-11-08) + +### Features + +- **Activity:** Add activity reducer, action, store for form ([a50ade1](https://landmarkcu.visualstudio.com///commit/a50ade1985c2c1a557bea35b59ae752c1b3a1730)) +- **Activity:** clean up activity table ([5b2d1a3](https://landmarkcu.visualstudio.com///commit/5b2d1a37377ead4086de37e78ac64ea53883e698)) +- **Activity:** Display user ([0807711](https://landmarkcu.visualstudio.com///commit/0807711f27a4bcb6f89fe2c54eac7d64e8ce2967)) +- **Activity:** filter activities ([f7971b8](https://landmarkcu.visualstudio.com///commit/f7971b88ab8e2aea76b0e21eded4866433ca2ee3)) +- **Activity:** manual entry validation ([1561d67](https://landmarkcu.visualstudio.com///commit/1561d67b136cf0bac15b57387ed9089c4ea2a61f)) +- **Activity:** search and sort audit page ([017aeb2](https://landmarkcu.visualstudio.com///commit/017aeb2a52b62cdb6b773ddeb715a001a69b434f)) +- **Activity:** split into activity view and form ([ae28fcf](https://landmarkcu.visualstudio.com///commit/ae28fcf56fbedc1553e79e8b5237d2b5482b6f90)) +- **Activity:** Update Activity Reducer/Action/State/Service to handle Pagination. Add Caching. ([a1af652](https://landmarkcu.visualstudio.com///commit/a1af65246e1b12c2064a8f75109347e72e9ef498)) +- **ActivityAuditLog:** Add Subject Org Nbr And Center Activity Audit Log ([62bb3b5](https://landmarkcu.visualstudio.com///commit/62bb3b5f7c421fb510b63f95c2bd0d054cb411c5)) +- **ActivityDetailPage:** Add page for single activity ([9415aa6](https://landmarkcu.visualstudio.com///commit/9415aa618c1c25725f22fe3f2560e1d692a8efc5)) +- **ActivityDetailPage:** Move loading message ([b62577d](https://landmarkcu.visualstudio.com///commit/b62577dc479fe1886c6ed39eec221555696a89f4)) +- **ActivityDetailsForm:** Wire ActivityDetailsForm to wizard and redux ([033a53c](https://landmarkcu.visualstudio.com///commit/033a53ca1d24ed29696cde013c21f6ab2cceaa59)) +- **ActivityDetailsPage:** Add approval buttons for activity state ([172d1d6](https://landmarkcu.visualstudio.com///commit/172d1d6d4a1f9e7245d47e7701d25ab1198ed538)) +- **ActivityEditor:** Select Department ([67bc2c6](https://landmarkcu.visualstudio.com///commit/67bc2c669ba863803d533836937742c9f59b0bcf)) +- **ActivityProductform:** Wire up activity product form to wizard and redux ([1942686](https://landmarkcu.visualstudio.com///commit/1942686e9b07c87d915de92bd39edfc7882d99b3)) +- **ActivityProductForm:** Restrict Product-ActivityType combinations ([db4b56a](https://landmarkcu.visualstudio.com///commit/db4b56aa1deaa5d74907d7c9508dd5dc56dfa991)) +- **ActivitySourceForm:** Wire ActivitySourceForm to wizard and redux ([d385551](https://landmarkcu.visualstudio.com///commit/d385551f984ed8c3b9b5ad026b1315c514bc83c6)) +- **ActivityTable:** Add Activity Auto-Table ([d35c3c8](https://landmarkcu.visualstudio.com///commit/d35c3c8fd61e903874bf62fd2e7670615d0bba4e)) +- **ActivityTransferApproval:** Add component to approve activity transfer ([85ca39f](https://landmarkcu.visualstudio.com///commit/85ca39f083a206186c329e696cde0815368b9f91)) +- **ActivityTransferRequest:** Add form to request activity transfer ([3ffac6e](https://landmarkcu.visualstudio.com///commit/3ffac6ef721c418b27985b0ad0b27e7dc2f40d31)) +- **AdditionalPermissionsForm:** Add Additional Permissions Form ([5d03b89](https://landmarkcu.visualstudio.com///commit/5d03b89bba892ae48ebcbc64b450101d0b27724b)) +- **Admin:** can force status change ([dc809d5](https://landmarkcu.visualstudio.com///commit/dc809d5c6a7d0d971ef87db46ec87c41a11a2a54)) +- **BusinessLineTable:** Add business line display to organization overview ([4a5ba10](https://landmarkcu.visualstudio.com///commit/4a5ba109ecbe6933786ec7c8728e7dd4528d4e27)) +- **CommissionPage:** Add Commission Page, Reducer, Actions ([bfefac4](https://landmarkcu.visualstudio.com///commit/bfefac468fcf63ebdfb12aaeccfe6da81ea5dc77)) +- **CommissionPage:** Add rate editor and audit table ([08a4502](https://landmarkcu.visualstudio.com///commit/08a45024641077d666c00b87ac93c866dd741cfc)) +- **dashboard:** show all departments ([123ffe3](https://landmarkcu.visualstudio.com///commit/123ffe3e9a019be9575cf7d948da2e41308ba0f7)) +- **Dashboard:** add new dashboards ([a9b7cdf](https://landmarkcu.visualstudio.com///commit/a9b7cdf8861944d31e2a552304605b19969b18ad)) +- **Dashboard:** dept dash for all ([844d79a](https://landmarkcu.visualstudio.com///commit/844d79a37c3aa38bd30f14cdf745b711b277fd5c)) +- **Dashboard:** determine dashboard ([70035a6](https://landmarkcu.visualstudio.com///commit/70035a61e48d9ac9403a15991d0c77c83c456e22)) +- **Dashboard:** region to department ([88c2bb1](https://landmarkcu.visualstudio.com///commit/88c2bb1a30dc620d4b919f71b3b5f1101073cbc7)) +- **Dashboard:** Start dashboard ([e8f15a6](https://landmarkcu.visualstudio.com///commit/e8f15a69eddde7e4d7c6862b29701d9d3f05e349)) +- **DebtProt:** show as if it had goals ([f7b1371](https://landmarkcu.visualstudio.com///commit/f7b1371238c47d7e56ded3ff85cfbc16e9ee57f7)) +- **Decision:** Update State Liist ([60ca6f8](https://landmarkcu.visualstudio.com///commit/60ca6f8aa64aeaf786fd96622e55f8f75a930130)) +- **Department:** Add Department Model, Service, Reducer, Actions ([f79feb1](https://landmarkcu.visualstudio.com///commit/f79feb189ec8e3e6876ce6e08eb2dca2151014f8)) +- **Department:** detail page stub ([76ce51d](https://landmarkcu.visualstudio.com///commit/76ce51d8d18177ad6739a296711a7d086dfacee9)) +- **DepartmentAssignmentAlertBar:** Add Notification for Submiting Auction Assignment ([6f2b724](https://landmarkcu.visualstudio.com///commit/6f2b724552d20598a11bbdd24e3311c20dd4e4fd)) +- **DepartmentAssignmentTabGroup:** Add Tab Group for Audit and inActive Assignments ([6fcc360](https://landmarkcu.visualstudio.com///commit/6fcc360ad20da8b60a2fdec00e1fdf3133727e90)) +- **DepartmentDetail:** Add component to display department details ([97bcd1f](https://landmarkcu.visualstudio.com///commit/97bcd1fe986073f90842ec95dfc4601bee8615ae)) +- **DepartmentGoalsEditor:** Add export to csv ([28d1add](https://landmarkcu.visualstudio.com///commit/28d1addbda64245d7ab7e30639f73fb1629d49f7)) +- **DepartmentTable:** Add basic display for departments ([86e81fa](https://landmarkcu.visualstudio.com///commit/86e81fafb9d9c710c629ba4a71e3d52f98c3627f)) +- **DepartmentTable:** Add region selection to department form ([e4ca89c](https://landmarkcu.visualstudio.com///commit/e4ca89c1e3844e86a6304ff3fa29b652e8c6c22a)) +- **DepartmentTableRow:** Add Update Form to Department Table Row ([5eb0a51](https://landmarkcu.visualstudio.com///commit/5eb0a51f767f1ce6c596f5329cc7b2fdf82378bc)) +- **DeptAssign:** Role based departments ([6a006f1](https://landmarkcu.visualstudio.com///commit/6a006f1257107c94f6883a5cfb85820ea73f3045)) +- **Detail:** make note a link ([5496ad2](https://landmarkcu.visualstudio.com///commit/5496ad2d3f02116a0a04c716b11dc176badc1c52)) +- **DispersalForm:** Add dispersal form and displays ([c0e9e6d](https://landmarkcu.visualstudio.com///commit/c0e9e6d706725964161babe50d3bd94452d26610)) +- **Employee:** Search for employees ([7b46dae](https://landmarkcu.visualstudio.com///commit/7b46dae5a6171598ca707e2e3c8735cb6839ffe0)) +- **EmployeeDashboard:** Add component to select month range ([5879750](https://landmarkcu.visualstudio.com///commit/58797500bd27915ba645ba540d7845b1d9b001ea)) +- **EmployeeDashboard:** Add progress views for user's departments ([f8877ab](https://landmarkcu.visualstudio.com///commit/f8877abc0a0be8cad1dde3e34f364152e74ce245)) +- **EmployeeDashboard:** Update personnel goals and activities to use month range ([1dd3bad](https://landmarkcu.visualstudio.com///commit/1dd3bad680ab69509719ac3d7b2ab2dd44310462)) +- **EmployeeDashboardPage:** Add alert for regional manager without a region ([c942408](https://landmarkcu.visualstudio.com///commit/c94240815d5823db8fead4c6543d6ae22d6a69be)) +- **Employeepage:** Add initial employee models and a route with parameter ([9f63a8a](https://landmarkcu.visualstudio.com///commit/9f63a8a3be1cdd2aee35500fb172d43bd3fddaf3)) +- **EmployeePage:** Add Employee Page, Reducer, Action ([eace35c](https://landmarkcu.visualstudio.com///commit/eace35cf16a7f28fd8d1e8ab7c3da97b2168d4cc)) +- **eoe:** supply termed employee list ([2a71148](https://landmarkcu.visualstudio.com///commit/2a711486725a57c01113d2a03775489fca7db48f)) +- **GoalDispersalForm:** Add Goal Dispersal to Personnel Goals ([ec3b176](https://landmarkcu.visualstudio.com///commit/ec3b176fe26d16b326e95cb2b53bdd9c39e0fb4c)) +- **GoalEditor:** Personnel goals must add up to department ([a9f4260](https://landmarkcu.visualstudio.com///commit/a9f426099806b3e70c47d0fd5b753aed4effdca0)) +- **GoalEditors:** show goal details ([c931357](https://landmarkcu.visualstudio.com///commit/c931357d171ca9cccacbaa361bd2bddb663cbc03)) +- **GoalReducer:** Create Goal Actions, Reducer, Redux Store Entry ([ad58c82](https://landmarkcu.visualstudio.com///commit/ad58c8292e2a450b356b28adcc4df61d616cc083)) +- **Goals:** display basic progress and audit ([60fb43a](https://landmarkcu.visualstudio.com///commit/60fb43ab2600af72c662627a2e4aebb4edb20551)) +- **Goals:** Manage goals, fix months ([d234144](https://landmarkcu.visualstudio.com///commit/d234144ba2c9aec4a0d2fe1f0fecdbe856261c74)) +- **Goals:** Personal Dept summary ([23edbf7](https://landmarkcu.visualstudio.com///commit/23edbf73803415fe9295288c693949217f52d6aa)) +- **Goals:** Save department goals ([d134f12](https://landmarkcu.visualstudio.com///commit/d134f12545c1add67320833b40f80d24ea724def)) +- **Goals:** Show editor totals ([23b3bf4](https://landmarkcu.visualstudio.com///commit/23b3bf404bbfa9af9c1bd41d4a50eefd5b1ee5f1)) +- **Goals:** Show personnel goals ([e070cb4](https://landmarkcu.visualstudio.com///commit/e070cb44ae86e12e309b05456062d80567e830f7)) +- **Goals:** Use real departments ([e718a96](https://landmarkcu.visualstudio.com///commit/e718a9645285599179d0ff51c1703463621cfc7f)) +- **Goals:** View Department Goals ([fc45182](https://landmarkcu.visualstudio.com///commit/fc45182e9cac77ba5d3c011c0d3e7d87a7836bcd)) +- **GoalsPage:** Add GoalsPage to retreive state and GoalDisplay to display list ([7007c0f](https://landmarkcu.visualstudio.com///commit/7007c0f293648540e496d25b4ea9de7cd3bce01e)) +- **Layout:** Add admin navigation, Footer fix ([e310f82](https://landmarkcu.visualstudio.com///commit/e310f823623f785dc93a8dcb3e4d8f7d79f3da9c)) +- **ManualActivity:** Add New Fields to Manual Activity Form ([b2fb8e9](https://landmarkcu.visualstudio.com///commit/b2fb8e98aee8cff1d10e361bc2ddc26613568bd3)) +- **ManualActivityForm:** Add base inputs to wizard with dropdown options ([1f1dfdb](https://landmarkcu.visualstudio.com///commit/1f1dfdb3b9bd9ea2013fcfcd918203b956c16da3)) +- **ManualActivityWiard:** Wire wizard submit and clear, Add Notification bar ([8ea370d](https://landmarkcu.visualstudio.com///commit/8ea370d5bc1f9f5234c5add694a0c13d11a57498)) +- **ManualActivityWizard:** Clear form data on successful submit to avoid duplication ([7b910d2](https://landmarkcu.visualstudio.com///commit/7b910d23033fc8e84358aa10cee7d67eb6d74f0b)) +- **MSAL:** Switch from ADAL to MSAL ([19f3f42](https://landmarkcu.visualstudio.com///commit/19f3f42b1a67cdbb1f1e4274a5116255281d89d8)) +- **Navigation:** Add User Information to Nav Bar ([06d25d5](https://landmarkcu.visualstudio.com///commit/06d25d5cc97d22f70724565fd3454a2b1eb95495)) +- **NewDepartmentAssignmentRow:** Add very basic add form to department assignment table ([3fc10f1](https://landmarkcu.visualstudio.com///commit/3fc10f15132380063fa25dfd9014518521e8de6f)) +- **OrganizationView:** Add admin org view ([e1db531](https://landmarkcu.visualstudio.com///commit/e1db53142a177e57ef97036afb28b26bb2d0fae3)) +- **PagedContentCotrols:** Add paged content controls ([3d9b59e](https://landmarkcu.visualstudio.com///commit/3d9b59e8d29d7901875eb6aad5e7be41abdf271e)) +- **PayDay:** show next bonus payday ([e325bd4](https://landmarkcu.visualstudio.com///commit/e325bd4f439a4d8b50e7053d369c0ac6cbab5be5)) +- **Payout:** Show eligible payout profiles ([020c082](https://landmarkcu.visualstudio.com///commit/020c0825e5f35bd327e885f3c630eb4112ac89c9)) +- **Payouts:** Add basic payout display ([92fb7eb](https://landmarkcu.visualstudio.com///commit/92fb7eb5668f27a9fc6599656e8b4cbca92b8995)) +- **Payouts:** export summary items ([a4e1be0](https://landmarkcu.visualstudio.com///commit/a4e1be0a6340bef9ac3bf88eb444c4c9f46422bd)) +- **Payouts:** load and export payouts ([69bb552](https://landmarkcu.visualstudio.com///commit/69bb552343931c0574c15ea6178be633fa1a2f4c)) +- **Payouts:** Payout on employee profile ([d69766f](https://landmarkcu.visualstudio.com///commit/d69766ff03e7e059b602c7084d7f55336c369c0a)) +- **Permissions:** Display permissions ([c1033ed](https://landmarkcu.visualstudio.com///commit/c1033ed69bf38d5f933f9edc0c6f400a3125c3ec)) +- **PersonnelGoalsEditor:** Add export to csv ([b7dfd24](https://landmarkcu.visualstudio.com///commit/b7dfd244402815695516a04d70bc7383059ec5d3)) +- **PersonnelGoalsEditor:** Add option to toggle showing all personnel ([ae59e83](https://landmarkcu.visualstudio.com///commit/ae59e837e7db929137d31f749690c1523e256c38)) +- **Product2:** Add Debt Protection Elligible Loan Product ([25af51c](https://landmarkcu.visualstudio.com///commit/25af51c2c9b2772b44146e95e61c00d845294b33)) +- **Profile:** Add basic profile ([71dd308](https://landmarkcu.visualstudio.com///commit/71dd308bd45851d890ecb908104193109c89bd63)) +- **Profile:** Add profile link ([ed31530](https://landmarkcu.visualstudio.com///commit/ed315308a2e9eb89c294c6c42e48cc7c4195e69e)) +- **Profile:** Combine Profile ([4cef97d](https://landmarkcu.visualstudio.com///commit/4cef97dc5bc3ac3696a6806fb09e6e2ed1ecc791)) +- **Profile:** show commissions ([efaf24d](https://landmarkcu.visualstudio.com///commit/efaf24d3f4c513e7a54e3bac860a86836eb9e670)) +- **Profile:** Temporary profile components ([0cb4cd2](https://landmarkcu.visualstudio.com///commit/0cb4cd2300f9f5d3a8d0721a42d71782a8903011)) +- **Progress:** show sales and referrals ([8c68df6](https://landmarkcu.visualstudio.com///commit/8c68df687477763ae16a3cab521298b59705c910)) +- **Region:** Regional dashboard ([3bda580](https://landmarkcu.visualstudio.com///commit/3bda580053515fa24b40caf606d7ecd7f8027885)) +- **RegionTable:** Add form to set regional manager ([95d481c](https://landmarkcu.visualstudio.com///commit/95d481c7f396b98a39ef67d41c3a6384852aa934)) +- **RegionTable:** Add region table ([64ad89d](https://landmarkcu.visualstudio.com///commit/64ad89d38a397ed63a4648d580aa697e6c475a0e)) +- **Roles:** Rank roles, restrict assignments ([fc5f15a](https://landmarkcu.visualstudio.com///commit/fc5f15a013499c13df23411cefbe36f6a7aac41f)) +- **RoleTable:** Add Role Table and Role Management Page ([7c70510](https://landmarkcu.visualstudio.com///commit/7c70510ef45848b99cd73a8f28db814fb9f0784f)) +- **Search:** search for users ([602bf13](https://landmarkcu.visualstudio.com///commit/602bf138d57077bab8327626749c852d07a0a41d)) +- **Security:** Basic permission guard ([93d823a](https://landmarkcu.visualstudio.com///commit/93d823a8e7021a75ac2cbb31fa2879bfab78c28b)) +- **Settings:** Add Settings Reducer, Actions, Store, Service ([a4bb37e](https://landmarkcu.visualstudio.com///commit/a4bb37ec16772cb3a854e30933741eb9bf1b2da1)) +- **Tables:** center some columns ([ddde5f3](https://landmarkcu.visualstudio.com///commit/ddde5f3ee2e9b70e23c877727b22e1e4437d787a)) +- **UserDept:** Role per dept, sliding payout ([e0f50b8](https://landmarkcu.visualstudio.com///commit/e0f50b84bd506093fd7f80c13d63d5eb1b884f9e)) +- **UserSearchSelect:** Add control to search and select person ([d7f418b](https://landmarkcu.visualstudio.com///commit/d7f418bb54ba266f8e0d4cfbf3117d6b0044bebb)) +- **Validation:** conditional required fields ([525d0ff](https://landmarkcu.visualstudio.com///commit/525d0ff075d4da477743fc26b294479f99276158)) + +### Bug Fixes + +- **Activity:** Allow cents ([21b9db2](https://landmarkcu.visualstudio.com///commit/21b9db2524fe7da3b1ffad9ea7abbe90b7f4bb76)) +- **Activity:** manual entry bugs ([8fb6f68](https://landmarkcu.visualstudio.com///commit/8fb6f683d60133ecb71282d6f6f7458fadf433f9)) +- **Activity:** note for state change ([cc14b0d](https://landmarkcu.visualstudio.com///commit/cc14b0d421d8ea1c375e5fbf0c1b0b37fe6ac1f3)) +- **Activity:** save correctly, correct grammar ([68f641c](https://landmarkcu.visualstudio.com///commit/68f641c4303e59cf40b7ee7114f0582b9f097f87)) +- **ActivityAudit:** some columns do not sort ([9093b25](https://landmarkcu.visualstudio.com///commit/9093b253cae5d339e6c70d062584635be099820c)) +- **ActivityDetailDisplay:** Fix missing fields in Activity Detail ([adf3b60](https://landmarkcu.visualstudio.com///commit/adf3b60c9aed24dbfe14b56982ce0726a96a0d63)) +- **ActivityDetailPage:** Update state change handling logic ([6f949cc](https://landmarkcu.visualstudio.com///commit/6f949ccdd552391babac22bf22d1b5ee4244a45f)) +- **ActivityService:** Fix URL parsing/formatting bug ([69d879f](https://landmarkcu.visualstudio.com///commit/69d879f79e6e10dc56312579ad8f9efd146278c8)) +- **ActivityTable:** Fix Activity Table Headers ([d75c6b5](https://landmarkcu.visualstudio.com///commit/d75c6b5b115ed940d2dc290915a5da83bb9e7e57)) +- **appsettings:** remove localhost ([f35f6da](https://landmarkcu.visualstudio.com///commit/f35f6dac40cc8262d93041941d9ec92020792d31)) +- **BasicDeptAssignmentTable:** Fix time format string ([c62a552](https://landmarkcu.visualstudio.com///commit/c62a552b993f1193925b520b407d066172fcd678)) +- **CommissionPage:** Display timestamps in local time ([5a8ad15](https://landmarkcu.visualstudio.com///commit/5a8ad15ba966ebe0eca515b76d9e4e1ad024a225)) +- **CommissionRatesEditor:** Fix logic to detect pending changes ([b58a487](https://landmarkcu.visualstudio.com///commit/b58a487883dd614a3b3b14aa3a5ea995da8a4c87)) +- **CommissionRatesEditor:** Include roles with commissions ([9faec49](https://landmarkcu.visualstudio.com///commit/9faec497840791de6b455113b6285c06aec01a0b)) +- **CommissionTable:** Sort by updated date descending ([42fc1a2](https://landmarkcu.visualstudio.com///commit/42fc1a2a7293771fab73101290d24412c2807d0f)) +- **Dashboard:** remove route props ([a7c495a](https://landmarkcu.visualstudio.com///commit/a7c495ac6c021ef59a2d9d5fb2b7e6afda02afb2)) +- **DebtProtection:** Correct dp totals ([8ad0ef6](https://landmarkcu.visualstudio.com///commit/8ad0ef60d103074491f187314df5ed66a95abafe)) +- **Departments:** use provider! ([ba65661](https://landmarkcu.visualstudio.com///commit/ba65661f2fcce8b5ae14342a788dabb0e91503b7)) +- **DeptAssign:** Submit UTC Timestamps ([ab86a1b](https://landmarkcu.visualstudio.com///commit/ab86a1ba79ec35b776d4ce6bb753891cf830585e)) +- **DeptAssign:** Use correct end date field ([4a35bb2](https://landmarkcu.visualstudio.com///commit/4a35bb2c2a3e126e318b46e91d1c4b6376daa0df)) +- **Editor:** Wait for departments ([e66cf08](https://landmarkcu.visualstudio.com///commit/e66cf08dcde5b53690c34361b1f8b50e25916f67)) +- **EmployeeSearch:** missing permission ([9d36c9c](https://landmarkcu.visualstudio.com///commit/9d36c9c06b3902eb8383f5edcb7d01c7c40b0ca1)) +- **EoE:** add 1 to month ([f1aef06](https://landmarkcu.visualstudio.com///commit/f1aef06c46173314a9e3a1a91e227199886c799b)) +- **Feedback:** Rename button, nesting doll, env ([5e42b16](https://landmarkcu.visualstudio.com///commit/5e42b16111bcf7bcdc93559cea281862574d3de0)) +- **Form:** cannot set value? ([1eb64ed](https://landmarkcu.visualstudio.com///commit/1eb64ed86a76ba0548de25da50fcc1b4f4d10f5a)) +- **GoalEditors:** correct tab name to details ([0b330db](https://landmarkcu.visualstudio.com///commit/0b330db7dd9524aa770f3ef06824fcd3fcc8d463)) +- **Goals:** Allow 999 ([6dc6837](https://landmarkcu.visualstudio.com///commit/6dc6837f7c6c9e608f390681d8b8a135b6d318de)) +- **Goals:** Allow zeroes ([d5cfe30](https://landmarkcu.visualstudio.com///commit/d5cfe308b63855bc2dd3e31e087e5a4085a0356b)) +- **Goals:** include additional perms ([a78d4b9](https://landmarkcu.visualstudio.com///commit/a78d4b96a0ea56e050d6ee0b45f04be04b71e88b)) +- **Goals:** Show updated in utc ([a52ed06](https://landmarkcu.visualstudio.com///commit/a52ed061aebc1a5bc3b1158c85e0e15e046b911c)) +- **Goals:** Use employee not string ([125814e](https://landmarkcu.visualstudio.com///commit/125814e057014399bdafcdc42dbc9886fb8eb526)) +- **GoalTotals:** display 0 instead of nan ([42247f1](https://landmarkcu.visualstudio.com///commit/42247f104a9601f188c5fa9245b8ef7c616ae184)) +- **Jest:** Switch from ejcted jest tests to CRA tests with reporters ([0395f58](https://landmarkcu.visualstudio.com///commit/0395f585380d37fa755d2e92e5fe21de14d70f9e)) +- **LIC:** show Lic as LIC ([41ca697](https://landmarkcu.visualstudio.com///commit/41ca697538a8a1d8b4231ebc0490579eaca6315c)) +- **ManualActivityWizard:** Enable datetime inputs, submit with credentials ([5568603](https://landmarkcu.visualstudio.com///commit/5568603a8599ec9a41817fee86dc1ae9ac8f34d0)) +- **ManualActivityWizard:** Map empty string to null, cast balance to number ([44aa06d](https://landmarkcu.visualstudio.com///commit/44aa06defb341567886a6b3f377befb77f01a87f)) +- **MSAL:** url and log ([7ae8ebe](https://landmarkcu.visualstudio.com///commit/7ae8ebe79ce8d9307bd5e0f4327f41ffecaf839f)) +- **Multi:** department work ([8a5cd58](https://landmarkcu.visualstudio.com///commit/8a5cd58ba6af55eba576ef797ea9e6aec337e648)) +- **Multi:** fix 22711,22999,21785 ([a6fb13f](https://landmarkcu.visualstudio.com///commit/a6fb13f85dc101ee5da9b952e6eeff11adf07a56)) +- **Navigation:** Fix navbar navigation by prefixing basename ([a6158ba](https://landmarkcu.visualstudio.com///commit/a6158ba36304750c538a33b61b048afcf5b448cc)) +- **Payout:** allow partial payout ([a7555db](https://landmarkcu.visualstudio.com///commit/a7555db37c1d3e3e91e1f74cb506894a0db12525)) +- **Permission:** get active departments ([11221a3](https://landmarkcu.visualstudio.com///commit/11221a31f4a020ff36330028d323feec90352f87)) +- **Permission:** remove all permission ([5485f89](https://landmarkcu.visualstudio.com///commit/5485f896df2d98a71ee56591160b368935073968)) +- **Permissions:** do not center permissions ([56e181c](https://landmarkcu.visualstudio.com///commit/56e181cc82b4c01af8b7188e5780bc0328581dc1)) +- **PersonnelGoalsEditor:** Include employees with set goals ([728913f](https://landmarkcu.visualstudio.com///commit/728913f99de9619f6100d62cb54ef66f97749b40)) +- **Profile:** keep current user profile loaded ([62fa6e4](https://landmarkcu.visualstudio.com///commit/62fa6e485e20f121827072112d1a455d927cd7cf)) +- **Profile:** re-order column, fixes 23676 ([c09db52](https://landmarkcu.visualstudio.com///commit/c09db521ac22d18a05d19217504ef7e9c182e480)) +- **Profile:** remove oops ([0801c8a](https://landmarkcu.visualstudio.com///commit/0801c8a42f282d11b32b6c422b361901f34f4eb9)) +- **Profile:** show full name from dayforce ([69ea7d9](https://landmarkcu.visualstudio.com///commit/69ea7d9dba1a305db5ce0950bc927851537aa611)) +- **Role:** handle null role ([264636d](https://landmarkcu.visualstudio.com///commit/264636d386d6f329a4bcfd768b508fa6a00faa94)) +- **Role:** use role from department assignments ([4f4ed0f](https://landmarkcu.visualstudio.com///commit/4f4ed0f046978905fb1d0b64fc3deef7940670b9)) +- **Router:** Remove permission guard wrapping a route ([b668176](https://landmarkcu.visualstudio.com///commit/b668176772f42c49c0a9102b89e7bbcaa19362f9)) +- **Settings:** update all useSelectors ([9340b2d](https://landmarkcu.visualstudio.com///commit/9340b2d16bdd35f4ca55651d68e5599e342f9084)) +- **Spelling:** fix some grammar up ([1689051](https://landmarkcu.visualstudio.com///commit/1689051e2c95b8fea3203ee7361e91be3b66ccee)) +- **Tests:** Add receipts to tests ([47ff204](https://landmarkcu.visualstudio.com///commit/47ff204298bff30be210886471d69f69608de987)) +- **TimeStamp:** Display in local time ([611519c](https://landmarkcu.visualstudio.com///commit/611519c565735211a6e88d41e41bd31108e24cf5)) +- **Translate:** Fix merge error ([cba901e](https://landmarkcu.visualstudio.com///commit/cba901e29c3d826a11039e204732750365db5248)) diff --git a/LCU.WebApps.Operations.IncentiveManager/Dockerfile b/LCU.WebApps.Operations.IncentiveManager/Dockerfile new file mode 100644 index 0000000..bbd595c --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/Dockerfile @@ -0,0 +1,62 @@ +FROM node:12-buster AS node-install + +# Fix EOL apt sources and install git +RUN set -eux; \ + cp /etc/apt/sources.list /etc/apt/sources.list.dist || true; \ + printf '%s\n' \ + 'deb http://archive.debian.org/debian buster main contrib non-free' \ + 'deb http://archive.debian.org/debian buster-updates main contrib non-free' \ + 'deb http://archive.debian.org/debian-security buster/updates main contrib non-free' \ + > /etc/apt/sources.list; \ + echo 'Acquire::Check-Valid-Until "false";' > /etc/apt/apt.conf.d/99no-check-valid-until; \ + apt-get -o Acquire::Check-Valid-Until=false update; \ + apt-get install -y --no-install-recommends git ca-certificates; \ + rm -rf /var/lib/apt/lists/* + +RUN git --version + +# Project env (skips version.js in postinstall per your comment) +ENV ENV_IS_DOCKER=true + +WORKDIR /app +COPY package.json package-lock.json isDockerEnv.js ./ + +# ---- Optional: Azure Artifacts auth +# Pass these in your pipeline only if you need the private feed: +# --build-arg FEED_URL=https://pkgs.dev.azure.com///_packaging//npm/registry +# --build-arg PAT=$(System.AccessToken) # or a PAT with Packaging read +ARG FEED_URL="" +ARG PAT="" +RUN set -eux; \ + if [ -n "$FEED_URL" ] && [ -n "$PAT" ]; then \ + host="$(echo "$FEED_URL" | sed -E 's|^https?://||')"; \ + printf 'registry=%s/\n//%s/:_authToken=%s\nalways-auth=true\n' "$FEED_URL" "$host" "$PAT" > .npmrc; \ + fi + +# Install deps and ensure node-sass binary matches Node 12 +RUN npm install --before=2025-09-1 --unsafe-perm \ + && npm rebuild node-sass || true + +# ---- Build +FROM node-install AS node-build +#Copy React Project Files +COPY . . +#Generate version.ts file and run production build +RUN node version.js && npm run build + +FROM node-build AS node-test +#Set label to reference this container from pipeline to get test results +LABEL test=true +RUN npm run test-ci; + +#Nginx layer for serving build +FROM nginx:stable-alpine AS nginx +#Set working directory to default nginx html directory +WORKDIR /usr/share/nginx/html +#Remove default assests from base directory +RUN rm -rf ./* +#Copy build folder to nginx working directory +COPY --from=node-build /app/build . +#Copy Nginx Config From Project Files. +COPY --from=node-build /app/nginx.conf /etc/nginx/conf.d/default.conf +ENTRYPOINT ["nginx","-g","daemon off;"] diff --git a/LCU.WebApps.Operations.IncentiveManager/README.md b/LCU.WebApps.Operations.IncentiveManager/README.md new file mode 100644 index 0000000..83d0d8f --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/README.md @@ -0,0 +1,290 @@ +# Incentive Manager + +React/Redux client using IncentiveAPI to manage incentives and display progress for associates, branches and regions. + +# Getting Started + +If you haven't set up your local environment for react development yet clone this repo and run the setup script to +install the required tools [here](https://landmarkcu.visualstudio.com/Platform/_git/LCU.Setup.Internal.EnvironmentSetup?version=GBmaf%2FinitialCommit) + +Once that has finished you can run the SetupLocalSSL script in the root directory of the project, +follow the prompts to install a local SSL cert for your project. +Once that is complete you can run the "start-https" npm script and begin adding your feature + +# Style Concerns + +ESLint and Prettier are the managers for code style. You may need to add an extension to your code editor for both Eslint and Prettier. There is a git commit hook setup to auto-apply Prettier rules after each commit. All edited files will be cleaned up. + +On top of these linters we do have a few preferences that you should keep in mind: + +- React - Functional Components Only, API Calls in Services +- Testing - Enzyme/Jest +- Forms - Conform to outlined standards +- Releases - Handle in Azure DevOps +- Commits - Commitizen style +- ADA Compliance + +## React + +**Functional Components Only** +In React 16.8 hooks were added to functional components giving them the same functionality as class components. These new functional components are much easier to read and do not require the 'Connection' declaration that class components did. + +Learn more about react hooks [here](https://reactjs.org/docs/hooks-overview.html) + +Learn more about using Typescript with react hooks [here](https://react-typescript-cheatsheet.netlify.app/docs/basic/getting-started/hooks/) + +**API Calls In Services** +To better keep our react projects DRY we move API calls to services instead of using them in actions or react hooks. This allows us to easily update all API calls throughout the entire application safely should an API change. + +You can find services in `src/shared/services` + +## Testing + +Enzyme and Jest are the testing libraries. + +- https://devdocs.io/enzyme/ +- https://devdocs.io/jest/ + +## Forms + +While forms have various roles and requirements, there are a set of standards that we can agree to when approaching form +creation. Please keep these standards in mind as a guide and not a hard rule - although variations should likely be discussed with the team. + +- Disable submit for invalid forms, ensure 'enter' doesn't bypass this. +- Mark required fields with a red \* +- Prefer 'immediate' form feedback +- Do not store form state in redux +- Use React-Strap components when possible: https://reactstrap.github.io/components/form +- Use the React-Strap form validation UX: + - Outlined fields in Green/Red + - Interior Green/Red icon + - Message below input + - You may include a success message when appropriate + - https://reactstrap.github.io/components/form/#Form-Validation +- Technical Recommendations: + - Fields should have basic validation 'locally' in their component + - Form-Wide validation should be handled in a parent component + - When possible, store validation functions separately in the shared folder for re-use +- Present success or failure in a banner above or below the form +- Server feedback should be presented in a way that the appUser can either correct the form or provide a useful bug report + +## Connected Vs. Components Vs. Layout - Where does this component go? + +React Components in this project are broken into 3 folders under the src directory _Connected_, _Components_, and _Layout_. + +On first glance there is practically no difference between the react components saved in each. The distinction comes from its role within the application and its relationship to the redux store. + +**Connected - The Redux Folder** +A _Container_ is a concept recommended by the Redux team to isolate app state from the display logic. In this project the folder is renamed _Connected_ as a 'container' is a lose term and can be applied to unconnected react components as well. + +_Connected_ components are the 'pages' that contain non-connected components that depend upon data from the Redux Store. More specifically, _Connected_ components are top level components that hold Redux functionality (such as Selectors and Dispatchers) and distribute it to child display components. + +Anything that uses a Selector hook or several Dispatch hooks should be saved in the _Containers_ folder and should not display any of its own data. + +**Components - The React Folder** +The _Components_ folder is where the bulk of the React Components should be saved. The _Components_ folder should contain the lowest level and most specific components. They are unconcerned with where their props come from and are in charge of collecting or displaying a piece of state. + +None of these components should use a Selector (arguments can be made for allowing Dispatch, this project tends to avoid it), instead, the data will flow into these components from the app state via a _Connected_ component's Selector. The final stop for all data is always a _Component_. + +Glue components, or components that group together multiple children components, help transform data as well as manage state between several display components. While their function is similar to that of a Connected Component, glue components belong in the _Component_ folder, as they are not dependent on the Redux store. Keep in mind glue components are not explicitly named as such. + +The entire _Components_ folder can be exported as a Library and used in any React application regardless of the presence of a Redux store. + +**Layout - The DOM folder** +The _Layout_ folder defines the 'Global' styling and structure of the application. This includes any Navigation, Footers, Menus and the like. These components are purely structural and agnostic to any data that the app intends to process or display. + +These components should almost always be [Higher Order Components](https://reactjs.org/docs/higher-order-components.html) or Wrapper Components. These are components that augment or wrap their children (in this case the child is typically the content of the application). + +If a layout item needs to be connected to the Redux store, create a connected component that will be wrapped by the parent Layout component. For example + +```jsx +//PageContainer.tsx +import {Navbar, AnimatedLogo, PageContainer} from "src/Layout" // Control Page Layout +import {AppMenu, MenuItem} from "menu-library" +import {ConnectedNavbarItem} from "src/Connected/ConnectedNavbarItem" //Navbar item with redux state +... +return ( + + + + + Home + + + + {...props.children} + +) +``` + +Application state does not belong inside of the Layout Folder. This folder represents the "canvas" that the rest of the application will be painted onto. + +In some applications, the layout folder can be found under the _Components_ directory. This is acceptable, however going forward it will be separated. The intention is to guarantee app state does not find its way to the layout folder. + +**I found X in a folder and it doesn't belong there** +This is more likely a mistake than an exception. Consider fixing this in a separate PR. Double check that there isn't a comment justifying its acceptability. + +**I think I found an Exception** +Exceptions are rare but may happen. When they do, open a discussion with the team and well document the exception in both the readme and comments in the compone + +## Releases + +Releases are handled by anyone on the team with access to Azure DevOps. Generally when a pull request is accepted to develop, that will be automatically built and deployed to QA. The QA application is here: [https://qa-optin.azurewebsites.net/#/associate]. The same is true for the front end / Client. + +To get to UAT, develop must be merged into the master branch. This will automatically cause the main branch to be built and the resulting release to be pushed to UAT. + +When creating releases, it's important not to forget to make a release and update the change notes as needed. There is information on creating a release below. + +## Setup Commitizen and Produce changelogs and release notes + +Commitizen is a command line tool that helps format commit messages with a series of prompts that are used to +generate a commit message. We use this tool to standardize our commits for tracking a changelog and producing release +notes. + +- `npm install commitizen -g` +- `npm install commitizen --save-dev` // Already done for you, skip. +- `npm install cz-conventional-changelog --save-dev` // Already done for you, skip. +- `npm install husky --save-dev` // Already done for you, skip. +- `npm install --save-dev validate-commit-msg` // Already done for you, skip. +- `npm install -g conventional-changelog-cli` + +Instead of committing with `git commit` commit with `git cz` +This will force your messages into the format (): + +| Definition | Description | +| ---------- | ------------------------------------------------------------------------------------------------------ | +| feat | A new feature | +| fix | A bug fix | +| docs | Documentation only changes | +| style | Changes that do not affect the meaning of the code (white-space, formatting, missing semi-colons, etc) | +| refactor | A code change that neither fixes a bug nor adds a feature | +| perf | A code change that improves performance | +| test | Adding missing or correcting existing tests | +| chore | Changes to the build process or auxiliary tools and libraries such as documentation generation | + +Here is how semantic-release will map these to release types and how it maps to [semantic verioning](https://semver.org/#summary): + +| Commit message | Release type | +| ---------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------ | +| `fix(pencil): stop graphite breaking `
`when too much pressure applied` | Patch Release (PATCH) | +| `feat(pencil): add 'graphiteWidth' option` | Feature Release (MINOR) | +| `BREAKING CHANGE: The graphiteWidth option `
`has been removed. The default graphite`
` width of 10mm is always used for performance reasons.` | Breaking Release (MAJOR) | + +### Produce or update the changelog + +You shouldn't need to do this, it should be generated for you when you create a release (below) + +- Create changelog from scratch: `conventional-changelog -p angular -i CHANGELOG.md -s -r 0` +- Update the changelog: `conventional-changelog -p angular -i CHANGELOG.md -s ` + +### Produce a set of release notes and bump version + +- Use: `npm run release` +- You should check the ChangeLog.MD after this and make any corrections. + +Resources: + +- http://www.nullfactory.net/2017/04/enforce-commit-message-standards-and-generate-changelogs-via-git/ +- https://dev.bleacherreport.com/how-we-use-commitizen-to-clean-up-commit-messages-a16790dcd2fd +- http://devboosts.com/2017/03/28/conventional-commits/ +- https://medium.com/@LeonardoBrAu/why-git-squash-and-rebase-commits-and-how-we-do-it-87aa17406e21 + +# ADA Compliance + +This application is targeting level AA ADA Compliance, here is the checklist you should keep in mind when developing: + +- Every image, video file, audio file, plug-in, etc. has an alt tag +- Complex graphics are accompanied by detailed text descriptions +- The alt descriptions describe the purpose of the objects +- If an image is also used as a link, the alt tag describes the graphics and the link destination +- Decorative graphics with no other function have empty alt descriptions (alt=””) +- Data tables have the column and row headers appropriately identified (using the tag) +- Make sure the page does not contain repeatedly flashing images +- Check to make sure the page does not contain a strobe effect +- All scripts and plug-ins and the content within them are accessible to assistive technologies, or else an alternative means of accessing equivalent content is provided +- When form controls are text input fields use the LABEL element +- When text is not available use the title attribute +- Include any special instructions within field labels +- Make sure that form fields are in a logical tab order +- Include a ‘Skip Navigation’ button to help those using text readers +- SVG icons should not focusable by tabbing in IE (utilize `focusable="false"` on all icon tags) + +## Coding tools for ADA Compliance + +- Refer to here for help: https://getbootstrap.com/docs/4.0/getting-started/accessibility/ +- Refer to here for practical examples: http://www.punkchip.com/aria-basic-findings/ +- use aria-label, aria-expanded, and aria-hidden as needed. +- Bootstrap: sr-only, bootstrap provides sr-only so that you can show things to people with screen readers. (sr-only stands for screen readers only) + +# Learning React, Redux, Typescript, etc + +### Docs: + +React, Redux And Typescript have great quick-start guides that walk through their features step by step with examples: + +- React: [https://reactjs.org/docs/getting-started.html](https://reactjs.org/docs/getting-started.html) +- Redux: [https://redux.js.org/introduction/core-concepts](https://redux.js.org/introduction/core-concepts) +- Typescript: [https://www.typescriptlang.org/docs/home.html](https://www.typescriptlang.org/docs/home.html) + +### Bootstrap: + +Unified Opt in uses Bootstrap 4 + +Search the Docs here: https://getbootstrap.com/docs/4.0/getting-started/introduction/ + +If you see className with "fa fa-noun" we are expecting bootstrap to render that text as an icon. Search all the possible bootstrap fontawesome icons here: https://fontawesome.com/v4.7.0/icons/ + +### Useful Resources: + +~~1. https://medium.com/@tkssharma/understanding-redux-react-in-easiest-way-part-1-81f3209fc0e5~~ 2. https://blog.usejournal.com/using-react-with-redux-and-typescript-c7ec48c211f6 3. https://hackernoon.com/redux-step-by-step-a-simple-and-robust-workflow-for-real-life-apps-1fdf7df46092 4. https://medium.com/naukri-engineering/email-sending-architecture-using-messaging-queue-314a18f8595c 5. https://reactjs.org/docs/higher-order-components.html#convention-maximizing-composability 6. https://blog.logrocket.com/react-redux-connect-when-and-how-to-use-it-f2a1edab2013/ 7. https://stackoverflow.com/questions/54154673/react-typescript-redux-connect-function-not-working 8. https://redux.js.org/recipes/structuring-reducers/basic-reducer-structure + +# Redux in TypeScript Quick Guide + +1. Know what a Higher Order Component is. +2. The types: https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/react-redux/index.d.ts#L59 +3. Connect is a higher order component used to wrap your component giving it access to state from the store and dispatch actions. + +### Build + +Run `npm run build` to build the project with default settings, which for create-react-app is production. The development +server will do a debug build. + +### Run (Development server) + +Run `npm start` for a dev server. Navigate to `https://localhost:3000/`. The app will automatically reload if you change any of the source files. +Note that this will run with a proxy in place so that you can contact the web services. + +### Deploy (QA, UAT, Prod) + +Deployments to QA, UAT, and Prod are automatically handled by Azure Dev-Ops. See the additional resources section below; as there is a document outlining the software development process, as well as the cloud setup Azure. + +### Running unit tests + +Run `npm run test` to run the unit tests interactively, they will re-run whenever you make changes. To simulate +the CI server and run them once, `npm run test-ci` or set the CI environment variable before running `npm run test` + +### Suggestion for IDE + +- Visual Studio Code +- Webstorm + +### Other Tools: + +- [Postman](https://www.postman.com/) +- [React Dev Tools Extension](https://chrome.google.com/webstore/detail/react-developer-tools/fmkadmapgofadopljbjfkapdkoienihi?hl=en) + +# FAQ + +Q. What the heck is a 'thunk'? +A. https://daveceddia.com/what-is-a-thunk/ + +Q. I'm running the unit test and I see output like this: +`HeadlessChrome 68.0.3440 (Windows 10.0.0) XYZComponent should use the service to add address controls to the form group FAILED [object ErrorEvent] thrown ` +A. Re-run the tests using `npm run test-debug`, which will show the full stack trace but not provide code coverage. + +Q. What do I need to know about Security? +A. The major security concerns are outlined in the architecture documents in the share (see resources above). The main security mechanism +is for Landmark employees, and uses the React-ADAL library to authenticate against Azure AD. + +Q. Where was this project started from? +A. This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app). diff --git a/LCU.WebApps.Operations.IncentiveManager/SetupLocalSSL.ps1 b/LCU.WebApps.Operations.IncentiveManager/SetupLocalSSL.ps1 new file mode 100644 index 0000000..9c1c867 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/SetupLocalSSL.ps1 @@ -0,0 +1,6 @@ +# If you don't already have your environment setup using the internal chocolately setup script +# or have chocolately installed on your local machine you will need to perform that step first +choco install mkcert +mkcert -install +mkdir -p .cert +mkcert -key-file ./.cert/key.pem -cert-file ./.cert/cert.pem "localhost" diff --git a/LCU.WebApps.Operations.IncentiveManager/azdo_pipelines/azure-k8s-pipeline.yaml b/LCU.WebApps.Operations.IncentiveManager/azdo_pipelines/azure-k8s-pipeline.yaml new file mode 100644 index 0000000..a4075e9 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/azdo_pipelines/azure-k8s-pipeline.yaml @@ -0,0 +1,52 @@ +parameters: + - name: target_environment + displayName: Target Environment + type: string + default: qa + values: + - qa + - uat + - prd + - name: k8s_namespace + displayName: K8s Namespace to deploy to + type: string + default: landmarkcu + values: + - landmarkcu +variables: + - template: ./config-files/${{ parameters.target_environment }}-pvars.yaml + - name: source_branch_name + value: $[replace(replace(variables['Build.SourceBranch'], 'refs/heads/', ''), '/', '-')] + - name: docker_container_repository_name + value: landmarkcu/lcu.api.internal.incentivemanager-incentivemanager + +resources: + repositories: + - repository: azdo_pipeline_templates + type: git + name: Infrastructure Platform/azdo-pipeline-templates + ref: main + +stages: + # DOCKER BUILD AND PUSH + - template: docker-build-push.yaml@azdo_pipeline_templates + parameters: + azdo_agent_pool: $(pvar_azdo_agent_pool) + docker_registry_service_connection: $(pvar_docker_registry_service_connection) + docker_container_repository_name: $(docker_container_repository_name) + dockerfile_path: ./Dockerfile + docker_image_tags: | + build-$(Build.BuildId) + $(source_branch_name) + - template: k8s-deploy.yaml@azdo_pipeline_templates + # K8S DEPLOY + parameters: + dependsOn: CI + condition: or(eq(variables['Build.SourceBranchName'], 'develop'), eq(variables['Build.SourceBranchName'], 'main')) + azdo_agent_pool: $(pvar_azdo_agent_pool) + kubectl_version: $(pvar_kubectl_version) + k8s_service_connection: $(pvar_k8s_service_connection) + k8s_namespace: ${{ parameters.k8s_namespace }} + k8s_kustomization_path: ./kustomize/overlays/${{ parameters.target_environment }} + docker_registry_service_connection: $(pvar_docker_registry_service_connection) + docker_image_to_deploy: $(docker_container_repository_name):build-$(Build.BuildId) diff --git a/LCU.WebApps.Operations.IncentiveManager/azdo_pipelines/azure-pipelines-feature-branches.yaml b/LCU.WebApps.Operations.IncentiveManager/azdo_pipelines/azure-pipelines-feature-branches.yaml new file mode 100644 index 0000000..b513a92 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/azdo_pipelines/azure-pipelines-feature-branches.yaml @@ -0,0 +1,9 @@ +trigger: + branches: + include: + - "*" + exclude: + - "main" + - "develop" +extends: + template: azure-k8s-pipeline.yaml diff --git a/LCU.WebApps.Operations.IncentiveManager/azdo_pipelines/azure-pipelines-prd.yaml b/LCU.WebApps.Operations.IncentiveManager/azdo_pipelines/azure-pipelines-prd.yaml new file mode 100644 index 0000000..7c85a9c --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/azdo_pipelines/azure-pipelines-prd.yaml @@ -0,0 +1,9 @@ +trigger: + branches: + include: + - "main" +extends: + template: azure-k8s-pipeline.yaml + parameters: + target_environment: prd + k8s_namespace: landmarkcu diff --git a/LCU.WebApps.Operations.IncentiveManager/azdo_pipelines/azure-pipelines-qa.yaml b/LCU.WebApps.Operations.IncentiveManager/azdo_pipelines/azure-pipelines-qa.yaml new file mode 100644 index 0000000..3c78a32 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/azdo_pipelines/azure-pipelines-qa.yaml @@ -0,0 +1,9 @@ +trigger: + branches: + include: + - "develop" +extends: + template: azure-k8s-pipeline.yaml + parameters: + target_environment: qa + k8s_namespace: landmarkcu diff --git a/LCU.WebApps.Operations.IncentiveManager/azdo_pipelines/azure-pipelines-uat.yaml b/LCU.WebApps.Operations.IncentiveManager/azdo_pipelines/azure-pipelines-uat.yaml new file mode 100644 index 0000000..b6828f5 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/azdo_pipelines/azure-pipelines-uat.yaml @@ -0,0 +1,9 @@ +trigger: + branches: + include: + - "main" +extends: + template: azure-k8s-pipeline.yaml + parameters: + target_environment: uat + k8s_namespace: landmarkcu diff --git a/LCU.WebApps.Operations.IncentiveManager/azdo_pipelines/config-files/prd-pvars.yaml b/LCU.WebApps.Operations.IncentiveManager/azdo_pipelines/config-files/prd-pvars.yaml new file mode 100644 index 0000000..2733192 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/azdo_pipelines/config-files/prd-pvars.yaml @@ -0,0 +1,7 @@ +--- +variables: + pvar_azdo_agent_pool: vmss-sub-df-lcu-apps-prd-lz-uscz + pvar_docker_registry_service_connection: Docker Registry LMCU + pvar_k8s_service_connection: svcon-sub-df-lcu-apps-prd-aks-k8s-uscz + pvar_kubectl_version: 1.25.9 + pvar_azdo_agent_pool_nuget: LCUOnPrem diff --git a/LCU.WebApps.Operations.IncentiveManager/azdo_pipelines/config-files/qa-pvars.yaml b/LCU.WebApps.Operations.IncentiveManager/azdo_pipelines/config-files/qa-pvars.yaml new file mode 100644 index 0000000..3763059 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/azdo_pipelines/config-files/qa-pvars.yaml @@ -0,0 +1,7 @@ +--- +variables: + pvar_azdo_agent_pool: vmss-sub-df-lcu-apps-qa-lz-uscz + pvar_docker_registry_service_connection: Docker Registry LMCU + pvar_k8s_service_connection: svcon-sub-df-lcu-apps-qa-aks-k8s-uscz + pvar_kubectl_version: 1.25.9 + pvar_azdo_agent_pool_nuget: LCUOnPrem diff --git a/LCU.WebApps.Operations.IncentiveManager/azdo_pipelines/config-files/uat-pvars.yaml b/LCU.WebApps.Operations.IncentiveManager/azdo_pipelines/config-files/uat-pvars.yaml new file mode 100644 index 0000000..a847c82 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/azdo_pipelines/config-files/uat-pvars.yaml @@ -0,0 +1,7 @@ +--- +variables: + pvar_azdo_agent_pool: vmss-sub-df-lcu-apps-uat-lz-uscz + pvar_docker_registry_service_connection: Docker Registry LMCU + pvar_k8s_service_connection: svcon-sub-df-lcu-apps-uat-aks-k8s-uscz + pvar_kubectl_version: 1.25.9 + pvar_azdo_agent_pool_nuget: LCUOnPrem diff --git a/LCU.WebApps.Operations.IncentiveManager/copy-test-results.sh b/LCU.WebApps.Operations.IncentiveManager/copy-test-results.sh new file mode 100644 index 0000000..2a2c30e --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/copy-test-results.sh @@ -0,0 +1,6 @@ +#!/bin/bash +echo "Copy Test Results Out Of Temporary Container"; +CONTAINER_ID=`docker images --filter "label=test=true" -q | awk '{ print $1 }'` +docker create --name testcontainer $CONTAINER_ID +docker cp testcontainer:/app/junit ./junit +docker rm testcontainer diff --git a/LCU.WebApps.Operations.IncentiveManager/docker-build.sh b/LCU.WebApps.Operations.IncentiveManager/docker-build.sh new file mode 100644 index 0000000..b964644 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/docker-build.sh @@ -0,0 +1,3 @@ +echo "Building Incentive Manager Client Docker Image"; + +docker build -t incentive-log -f ./Dockerfile .; diff --git a/LCU.WebApps.Operations.IncentiveManager/docker-run.sh b/LCU.WebApps.Operations.IncentiveManager/docker-run.sh new file mode 100644 index 0000000..f14c3ef --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/docker-run.sh @@ -0,0 +1,3 @@ +echo "Starting Docker Image For Incentive Manager: will run on port 8080."; + +docker run --rm -d -p 8080:80 incentive-log diff --git a/LCU.WebApps.Operations.IncentiveManager/docker-rundev.sh b/LCU.WebApps.Operations.IncentiveManager/docker-rundev.sh new file mode 100644 index 0000000..07d4cc2 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/docker-rundev.sh @@ -0,0 +1,4 @@ +echo "[InteractiveMode] Starting Docker Image For Incentive Manager: will run on port 8080."; +echo "Nginx logs will be sent to your command line. Exiting the process desteroys the container." + +docker run -it -p 8080:80 incentive-log diff --git a/LCU.WebApps.Operations.IncentiveManager/isDockerEnv.js b/LCU.WebApps.Operations.IncentiveManager/isDockerEnv.js new file mode 100644 index 0000000..a540bfd --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/isDockerEnv.js @@ -0,0 +1,15 @@ +/*eslint-disable*/ +/* + Returns Error When Docker Environment Variable is NOT present. + + When used with || will SKIP the next step when ENV_IS_DOCKER is true + > node isDockerEnv.js || node version.js + + When used with && will stop scripts from running when ENV_IS_DOCKER is missing or false and error out + > node isDockerEnv.js && node test.js +*/ +if (process.env.ENV_IS_DOCKER) { + console.log("isDockerEnv: Script is running in Docker."); + process.exit(0); +} +process.exit(1); diff --git a/LCU.WebApps.Operations.IncentiveManager/jest.config.js b/LCU.WebApps.Operations.IncentiveManager/jest.config.js new file mode 100644 index 0000000..b16ea75 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/jest.config.js @@ -0,0 +1,17 @@ +module.exports = { + "roots": [ + "/src" + ], + "transform": { + "^.+\\.tsx?$": "ts-jest" + }, + "testRegex": "(/__tests__/.*|(\\.|/)(test|spec))\\.tsx?$", + "moduleFileExtensions": [ + "ts", + "tsx", + "js", + "jsx", + "json", + "node" + ], + } \ No newline at end of file diff --git a/LCU.WebApps.Operations.IncentiveManager/kustomize/base/Deployment.yaml b/LCU.WebApps.Operations.IncentiveManager/kustomize/base/Deployment.yaml new file mode 100644 index 0000000..eeb46a8 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/kustomize/base/Deployment.yaml @@ -0,0 +1,54 @@ +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: incentive-manager +spec: + replicas: 1 + selector: + matchLabels: + app: incentive-manager + strategy: + rollingUpdate: + maxSurge: 25% + maxUnavailable: 25% + type: RollingUpdate + template: + metadata: + labels: + app: incentive-manager + spec: + containers: + - name: incentive-manager + image: incentive-manager + resources: + limits: + memory: "1536Mi" + cpu: "500m" + ports: + - containerPort: 80 + livenessProbe: + initialDelaySeconds: 5 + periodSeconds: 5 + httpGet: + path: / + port: 80 + readinessProbe: + initialDelaySeconds: 5 + periodSeconds: 5 + httpGet: + path: / + port: 80 + volumeMounts: + - name: volume-lcu-internal-cert + mountPath: /etc/ssl/certs/lcu-internal-cert.pem + subPath: lcu-internal-cert.pem + - name: volume-appsettings + mountPath: /app/config/mapped-appsettings + volumes: + - name: volume-lcu-internal-cert + configMap: + name: configmap-lcu-internal-cert + - name: volume-appsettings + configMap: + name: incentivemanager-configmap diff --git a/LCU.WebApps.Operations.IncentiveManager/kustomize/base/Service.yaml b/LCU.WebApps.Operations.IncentiveManager/kustomize/base/Service.yaml new file mode 100644 index 0000000..dd43693 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/kustomize/base/Service.yaml @@ -0,0 +1,11 @@ +--- +apiVersion: v1 +kind: Service +metadata: + name: incentive-manager +spec: + selector: + app: incentive-manager + ports: + - port: 80 + targetPort: 80 diff --git a/LCU.WebApps.Operations.IncentiveManager/kustomize/base/kustomization.yaml b/LCU.WebApps.Operations.IncentiveManager/kustomize/base/kustomization.yaml new file mode 100644 index 0000000..88175e9 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/kustomize/base/kustomization.yaml @@ -0,0 +1,4 @@ +resources: + - Deployment.yaml + - Service.yaml + - virtual-service.yaml diff --git a/LCU.WebApps.Operations.IncentiveManager/kustomize/base/secrets-provider-class.yaml b/LCU.WebApps.Operations.IncentiveManager/kustomize/base/secrets-provider-class.yaml new file mode 100644 index 0000000..1f1ea92 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/kustomize/base/secrets-provider-class.yaml @@ -0,0 +1,11 @@ +--- +apiVersion: secrets-store.csi.x-k8s.io/v1 +kind: SecretProviderClass +metadata: + name: incentive-manager-csi-key-vault-secrets-provider +spec: + provider: azure + parameters: + usePodIdentity: "false" + useVMManagedIdentity: "true" + tenantId: 63cd1554-2783-4a86-ae50-b9e6c21c9499 diff --git a/LCU.WebApps.Operations.IncentiveManager/kustomize/base/virtual-service.yaml b/LCU.WebApps.Operations.IncentiveManager/kustomize/base/virtual-service.yaml new file mode 100644 index 0000000..03dd775 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/kustomize/base/virtual-service.yaml @@ -0,0 +1,18 @@ +--- +apiVersion: networking.istio.io/v1beta1 +kind: VirtualService +metadata: + name: incentive-manager +spec: + gateways: + - istio-system/ingress-gateway + http: + - name: incentive-manager + match: + - uri: + prefix: / + route: + - destination: + host: incentive-manager + port: + number: 80 diff --git a/LCU.WebApps.Operations.IncentiveManager/kustomize/overlays/local/common/kustomization.yaml b/LCU.WebApps.Operations.IncentiveManager/kustomize/overlays/local/common/kustomization.yaml new file mode 100644 index 0000000..3310883 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/kustomize/overlays/local/common/kustomization.yaml @@ -0,0 +1,7 @@ +resources: + - ../../../base +patchesStrategicMerge: + - set_appsettings_deployment.yaml +images: + - name: incentive-manager + newName: landmarkcu/lcu.api.internal.incentivemanager-incentivemanager diff --git a/LCU.WebApps.Operations.IncentiveManager/kustomize/overlays/local/common/set_appsettings_deployment.yaml b/LCU.WebApps.Operations.IncentiveManager/kustomize/overlays/local/common/set_appsettings_deployment.yaml new file mode 100644 index 0000000..89ec507 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/kustomize/overlays/local/common/set_appsettings_deployment.yaml @@ -0,0 +1,12 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: incentive-manager +spec: + template: + spec: + containers: + - name: incentive-manager + env: + - name: ASPNETCORE_ENVIRONMENT + value: "Development" diff --git a/LCU.WebApps.Operations.IncentiveManager/kustomize/overlays/local/kustomization.yaml b/LCU.WebApps.Operations.IncentiveManager/kustomize/overlays/local/kustomization.yaml new file mode 100644 index 0000000..d748ab6 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/kustomize/overlays/local/kustomization.yaml @@ -0,0 +1,2 @@ +resources: + - normal diff --git a/LCU.WebApps.Operations.IncentiveManager/kustomize/overlays/local/normal/configmap.yaml b/LCU.WebApps.Operations.IncentiveManager/kustomize/overlays/local/normal/configmap.yaml new file mode 100644 index 0000000..72eeb6c --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/kustomize/overlays/local/normal/configmap.yaml @@ -0,0 +1,17 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: incentivemanager-configmap +data: + primary.json: | + { + "Primary": { + "SeqUrl": "https://qa-seq.lcu-internal.com", + "CorsAllowedOrigins": [ + "http://localhost:3000", + "http://lcu-internal.com", + "https://lcu-internal.com", + "https://qa-api.lcu-internal.com" + ] + } + } diff --git a/LCU.WebApps.Operations.IncentiveManager/kustomize/overlays/local/normal/kustomization.yaml b/LCU.WebApps.Operations.IncentiveManager/kustomize/overlays/local/normal/kustomization.yaml new file mode 100644 index 0000000..6a4285e --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/kustomize/overlays/local/normal/kustomization.yaml @@ -0,0 +1,5 @@ +resources: + - configmap.yaml + - ../common +patchesStrategicMerge: + - set_virtual_service.yaml diff --git a/LCU.WebApps.Operations.IncentiveManager/kustomize/overlays/local/normal/set_virtual_service.yaml b/LCU.WebApps.Operations.IncentiveManager/kustomize/overlays/local/normal/set_virtual_service.yaml new file mode 100644 index 0000000..7375307 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/kustomize/overlays/local/normal/set_virtual_service.yaml @@ -0,0 +1,18 @@ + --- +apiVersion: networking.istio.io/v1beta1 +kind: VirtualService +metadata: + name: incentive-manager +spec: + hosts: + - qa-incentive-manager.apps.azure.lcu-internal.com + http: + - name: incentive-manager + match: + - uri: + prefix: / + route: + - destination: + host: incentive-manager + port: + number: 80 diff --git a/LCU.WebApps.Operations.IncentiveManager/kustomize/overlays/prd/common/kustomization.yaml b/LCU.WebApps.Operations.IncentiveManager/kustomize/overlays/prd/common/kustomization.yaml new file mode 100644 index 0000000..2663567 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/kustomize/overlays/prd/common/kustomization.yaml @@ -0,0 +1,7 @@ +resources: +- ../../../base +patchesStrategicMerge: +- set_appsettings_deployment.yaml +images: +- name: incentive-manager + newName: landmarkcu/lcu.api.internal.incentivemanager-incentivemanager diff --git a/LCU.WebApps.Operations.IncentiveManager/kustomize/overlays/prd/common/set_appsettings_deployment.yaml b/LCU.WebApps.Operations.IncentiveManager/kustomize/overlays/prd/common/set_appsettings_deployment.yaml new file mode 100644 index 0000000..33fd2a0 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/kustomize/overlays/prd/common/set_appsettings_deployment.yaml @@ -0,0 +1,12 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: incentive-manager +spec: + template: + spec: + containers: + - name: incentive-manager + env: + - name: ASPNETCORE_ENVIRONMENT + value: "Production" diff --git a/LCU.WebApps.Operations.IncentiveManager/kustomize/overlays/prd/kustomization.yaml b/LCU.WebApps.Operations.IncentiveManager/kustomize/overlays/prd/kustomization.yaml new file mode 100644 index 0000000..1905c8f --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/kustomize/overlays/prd/kustomization.yaml @@ -0,0 +1,2 @@ +resources: +- normal diff --git a/LCU.WebApps.Operations.IncentiveManager/kustomize/overlays/prd/normal/configmap.yaml b/LCU.WebApps.Operations.IncentiveManager/kustomize/overlays/prd/normal/configmap.yaml new file mode 100644 index 0000000..e97f8e7 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/kustomize/overlays/prd/normal/configmap.yaml @@ -0,0 +1,16 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: incentivemanager-configmap +data: + primary.json: | + { + "Primary": { + "SeqUrl": "https://seq.lcu-internal.com", + "CorsAllowedOrigins": [ + "http://lcu-internal.com", + "https://lcu-internal.com", + "https://api.lcu-internal.com" + ] + } + } diff --git a/LCU.WebApps.Operations.IncentiveManager/kustomize/overlays/prd/normal/kustomization.yaml b/LCU.WebApps.Operations.IncentiveManager/kustomize/overlays/prd/normal/kustomization.yaml new file mode 100644 index 0000000..7fa3e2a --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/kustomize/overlays/prd/normal/kustomization.yaml @@ -0,0 +1,5 @@ +resources: +- configmap.yaml +- ../common +patchesStrategicMerge: +- set_virtual_service.yaml diff --git a/LCU.WebApps.Operations.IncentiveManager/kustomize/overlays/prd/normal/set_virtual_service.yaml b/LCU.WebApps.Operations.IncentiveManager/kustomize/overlays/prd/normal/set_virtual_service.yaml new file mode 100644 index 0000000..92f59b2 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/kustomize/overlays/prd/normal/set_virtual_service.yaml @@ -0,0 +1,18 @@ +--- +apiVersion: networking.istio.io/v1beta1 +kind: VirtualService +metadata: + name: incentive-manager +spec: + hosts: + - prd-incentive-manager.apps.azure.lcu-internal.com + http: + - name: incentive-manager + match: + - uri: + prefix: / + route: + - destination: + host: incentive-manager + port: + number: 80 diff --git a/LCU.WebApps.Operations.IncentiveManager/kustomize/overlays/qa/common/kustomization.yaml b/LCU.WebApps.Operations.IncentiveManager/kustomize/overlays/qa/common/kustomization.yaml new file mode 100644 index 0000000..2663567 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/kustomize/overlays/qa/common/kustomization.yaml @@ -0,0 +1,7 @@ +resources: +- ../../../base +patchesStrategicMerge: +- set_appsettings_deployment.yaml +images: +- name: incentive-manager + newName: landmarkcu/lcu.api.internal.incentivemanager-incentivemanager diff --git a/LCU.WebApps.Operations.IncentiveManager/kustomize/overlays/qa/common/set_appsettings_deployment.yaml b/LCU.WebApps.Operations.IncentiveManager/kustomize/overlays/qa/common/set_appsettings_deployment.yaml new file mode 100644 index 0000000..d334166 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/kustomize/overlays/qa/common/set_appsettings_deployment.yaml @@ -0,0 +1,12 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: incentive-manager +spec: + template: + spec: + containers: + - name: incentive-manager + env: + - name: ASPNETCORE_ENVIRONMENT + value: "Development" diff --git a/LCU.WebApps.Operations.IncentiveManager/kustomize/overlays/qa/kustomization.yaml b/LCU.WebApps.Operations.IncentiveManager/kustomize/overlays/qa/kustomization.yaml new file mode 100644 index 0000000..1905c8f --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/kustomize/overlays/qa/kustomization.yaml @@ -0,0 +1,2 @@ +resources: +- normal diff --git a/LCU.WebApps.Operations.IncentiveManager/kustomize/overlays/qa/normal/configmap.yaml b/LCU.WebApps.Operations.IncentiveManager/kustomize/overlays/qa/normal/configmap.yaml new file mode 100644 index 0000000..20a211c --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/kustomize/overlays/qa/normal/configmap.yaml @@ -0,0 +1,17 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: incentivemanager-configmap +data: + primary.json: | + { + "Primary": { + "SeqUrl": "https://qa-seq.lcu-internal.com", + "CorsAllowedOrigins": [ + "http://localhost:3000", + "http://lcu-internal.com", + "https://lcu-internal.com", + "https://qa-api.lcu-internal.com" + ] + } + } diff --git a/LCU.WebApps.Operations.IncentiveManager/kustomize/overlays/qa/normal/kustomization.yaml b/LCU.WebApps.Operations.IncentiveManager/kustomize/overlays/qa/normal/kustomization.yaml new file mode 100644 index 0000000..7fa3e2a --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/kustomize/overlays/qa/normal/kustomization.yaml @@ -0,0 +1,5 @@ +resources: +- configmap.yaml +- ../common +patchesStrategicMerge: +- set_virtual_service.yaml diff --git a/LCU.WebApps.Operations.IncentiveManager/kustomize/overlays/qa/normal/set_virtual_service.yaml b/LCU.WebApps.Operations.IncentiveManager/kustomize/overlays/qa/normal/set_virtual_service.yaml new file mode 100644 index 0000000..3ced7b6 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/kustomize/overlays/qa/normal/set_virtual_service.yaml @@ -0,0 +1,18 @@ +--- +apiVersion: networking.istio.io/v1beta1 +kind: VirtualService +metadata: + name: incentive-manager +spec: + hosts: + - qa-incentive-manager.apps.azure.lcu-internal.com + http: + - name: incentive-manager + match: + - uri: + prefix: / + route: + - destination: + host: incentive-manager + port: + number: 80 diff --git a/LCU.WebApps.Operations.IncentiveManager/kustomize/overlays/uat/common/kustomization.yaml b/LCU.WebApps.Operations.IncentiveManager/kustomize/overlays/uat/common/kustomization.yaml new file mode 100644 index 0000000..2663567 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/kustomize/overlays/uat/common/kustomization.yaml @@ -0,0 +1,7 @@ +resources: +- ../../../base +patchesStrategicMerge: +- set_appsettings_deployment.yaml +images: +- name: incentive-manager + newName: landmarkcu/lcu.api.internal.incentivemanager-incentivemanager diff --git a/LCU.WebApps.Operations.IncentiveManager/kustomize/overlays/uat/common/set_appsettings_deployment.yaml b/LCU.WebApps.Operations.IncentiveManager/kustomize/overlays/uat/common/set_appsettings_deployment.yaml new file mode 100644 index 0000000..24c8047 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/kustomize/overlays/uat/common/set_appsettings_deployment.yaml @@ -0,0 +1,12 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: incentive-manager +spec: + template: + spec: + containers: + - name: incentive-manager + env: + - name: ASPNETCORE_ENVIRONMENT + value: "Staging" diff --git a/LCU.WebApps.Operations.IncentiveManager/kustomize/overlays/uat/kustomization.yaml b/LCU.WebApps.Operations.IncentiveManager/kustomize/overlays/uat/kustomization.yaml new file mode 100644 index 0000000..1905c8f --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/kustomize/overlays/uat/kustomization.yaml @@ -0,0 +1,2 @@ +resources: +- normal diff --git a/LCU.WebApps.Operations.IncentiveManager/kustomize/overlays/uat/normal/configmap.yaml b/LCU.WebApps.Operations.IncentiveManager/kustomize/overlays/uat/normal/configmap.yaml new file mode 100644 index 0000000..6c51565 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/kustomize/overlays/uat/normal/configmap.yaml @@ -0,0 +1,17 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: incentivemanager-configmap +data: + primary.json: | + { + "Primary": { + "SeqUrl": "https://uat-seq.lcu-internal.com", + "CorsAllowedOrigins": [ + "http://localhost:3000", + "http://lcu-internal.com", + "https://lcu-internal.com", + "https://uat-api.lcu-internal.com" + ] + } + } diff --git a/LCU.WebApps.Operations.IncentiveManager/kustomize/overlays/uat/normal/kustomization.yaml b/LCU.WebApps.Operations.IncentiveManager/kustomize/overlays/uat/normal/kustomization.yaml new file mode 100644 index 0000000..7fa3e2a --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/kustomize/overlays/uat/normal/kustomization.yaml @@ -0,0 +1,5 @@ +resources: +- configmap.yaml +- ../common +patchesStrategicMerge: +- set_virtual_service.yaml diff --git a/LCU.WebApps.Operations.IncentiveManager/kustomize/overlays/uat/normal/set_virtual_service.yaml b/LCU.WebApps.Operations.IncentiveManager/kustomize/overlays/uat/normal/set_virtual_service.yaml new file mode 100644 index 0000000..656fcdc --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/kustomize/overlays/uat/normal/set_virtual_service.yaml @@ -0,0 +1,18 @@ +--- +apiVersion: networking.istio.io/v1beta1 +kind: VirtualService +metadata: + name: incentive-manager +spec: + hosts: + - uat-incentive-manager.apps.azure.lcu-internal.com + http: + - name: incentive-manager + match: + - uri: + prefix: / + route: + - destination: + host: incentive-manager + port: + number: 80 diff --git a/LCU.WebApps.Operations.IncentiveManager/nginx.conf b/LCU.WebApps.Operations.IncentiveManager/nginx.conf new file mode 100644 index 0000000..84d0fb3 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/nginx.conf @@ -0,0 +1,8 @@ +server { + listen 80; + location / { + root /usr/share/nginx/html; + index index.html index.htm; + try_files $uri $uri/ /index.html =404; + } +} diff --git a/LCU.WebApps.Operations.IncentiveManager/package-lock.json b/LCU.WebApps.Operations.IncentiveManager/package-lock.json new file mode 100644 index 0000000..ceba251 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/package-lock.json @@ -0,0 +1,21919 @@ +{ + "name": "incentive-manager", + "version": "0.1.4", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "@azure/msal-browser": { + "version": "2.14.2", + "resolved": "https://landmarkcu.pkgs.visualstudio.com/_packaging/LCU-Internal/npm/registry/@azure/msal-browser/-/msal-browser-2.14.2.tgz", + "integrity": "sha1-TvoDGtFtWjpSft22Ii4V3W4uo+g=", + "requires": { + "@azure/msal-common": "^4.3.0" + } + }, + "@azure/msal-common": { + "version": "4.3.0", + "resolved": "https://landmarkcu.pkgs.visualstudio.com/_packaging/LCU-Internal/npm/registry/@azure/msal-common/-/msal-common-4.3.0.tgz", + "integrity": "sha1-tUDpJ0hlZyQIi/dxkuWZQ6kxNbw=", + "requires": { + "debug": "^4.1.1" + } + }, + "@azure/msal-react": { + "version": "1.0.0", + "resolved": "https://landmarkcu.pkgs.visualstudio.com/_packaging/LCU-Internal/npm/registry/@azure/msal-react/-/msal-react-1.0.0.tgz", + "integrity": "sha1-hCjlHTlUds4TotiWbRYbvYdeAfY=" + }, + "@babel/code-frame": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.13.tgz", + "integrity": "sha512-HV1Cm0Q3ZrpCR93tkWOYiuYIgLxZXZFVG2VgK+MBWjUqZTundupbfx2aXarXuw5Ko5aMcjtJgbSs4vUGBS5v6g==", + "requires": { + "@babel/highlight": "^7.12.13" + } + }, + "@babel/compat-data": { + "version": "7.13.15", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.13.15.tgz", + "integrity": "sha512-ltnibHKR1VnrU4ymHyQ/CXtNXI6yZC0oJThyW78Hft8XndANwi+9H+UIklBDraIjFEJzw8wmcM427oDd9KS5wA==" + }, + "@babel/core": { + "version": "7.12.3", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.12.3.tgz", + "integrity": "sha512-0qXcZYKZp3/6N2jKYVxZv0aNCsxTSVCiK72DTiTYZAu7sjg73W0/aynWjMbiGd87EQL4WyA8reiJVh92AVla9g==", + "requires": { + "@babel/code-frame": "^7.10.4", + "@babel/generator": "^7.12.1", + "@babel/helper-module-transforms": "^7.12.1", + "@babel/helpers": "^7.12.1", + "@babel/parser": "^7.12.3", + "@babel/template": "^7.10.4", + "@babel/traverse": "^7.12.1", + "@babel/types": "^7.12.1", + "convert-source-map": "^1.7.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.1", + "json5": "^2.1.2", + "lodash": "^4.17.19", + "resolve": "^1.3.2", + "semver": "^5.4.1", + "source-map": "^0.5.0" + }, + "dependencies": { + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" + } + } + }, + "@babel/generator": { + "version": "7.13.16", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.13.16.tgz", + "integrity": "sha512-grBBR75UnKOcUWMp8WoDxNsWCFl//XCK6HWTrBQKTr5SV9f5g0pNOjdyzi/DTBv12S9GnYPInIXQBTky7OXEMg==", + "requires": { + "@babel/types": "^7.13.16", + "jsesc": "^2.5.1", + "source-map": "^0.5.0" + } + }, + "@babel/helper-annotate-as-pure": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.12.13.tgz", + "integrity": "sha512-7YXfX5wQ5aYM/BOlbSccHDbuXXFPxeoUmfWtz8le2yTkTZc+BxsiEnENFoi2SlmA8ewDkG2LgIMIVzzn2h8kfw==", + "requires": { + "@babel/types": "^7.12.13" + } + }, + "@babel/helper-builder-binary-assignment-operator-visitor": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.12.13.tgz", + "integrity": "sha512-CZOv9tGphhDRlVjVkAgm8Nhklm9RzSmWpX2my+t7Ua/KT616pEzXsQCjinzvkRvHWJ9itO4f296efroX23XCMA==", + "requires": { + "@babel/helper-explode-assignable-expression": "^7.12.13", + "@babel/types": "^7.12.13" + } + }, + "@babel/helper-compilation-targets": { + "version": "7.13.16", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.13.16.tgz", + "integrity": "sha512-3gmkYIrpqsLlieFwjkGgLaSHmhnvlAYzZLlYVjlW+QwI+1zE17kGxuJGmIqDQdYp56XdmGeD+Bswx0UTyG18xA==", + "requires": { + "@babel/compat-data": "^7.13.15", + "@babel/helper-validator-option": "^7.12.17", + "browserslist": "^4.14.5", + "semver": "^6.3.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" + } + } + }, + "@babel/helper-create-class-features-plugin": { + "version": "7.13.11", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.13.11.tgz", + "integrity": "sha512-ays0I7XYq9xbjCSvT+EvysLgfc3tOkwCULHjrnscGT3A9qD4sk3wXnJ3of0MAWsWGjdinFvajHU2smYuqXKMrw==", + "requires": { + "@babel/helper-function-name": "^7.12.13", + "@babel/helper-member-expression-to-functions": "^7.13.0", + "@babel/helper-optimise-call-expression": "^7.12.13", + "@babel/helper-replace-supers": "^7.13.0", + "@babel/helper-split-export-declaration": "^7.12.13" + } + }, + "@babel/helper-create-regexp-features-plugin": { + "version": "7.12.17", + "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.12.17.tgz", + "integrity": "sha512-p2VGmBu9oefLZ2nQpgnEnG0ZlRPvL8gAGvPUMQwUdaE8k49rOMuZpOwdQoy5qJf6K8jL3bcAMhVUlHAjIgJHUg==", + "requires": { + "@babel/helper-annotate-as-pure": "^7.12.13", + "regexpu-core": "^4.7.1" + } + }, + "@babel/helper-define-polyfill-provider": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.2.0.tgz", + "integrity": "sha512-JT8tHuFjKBo8NnaUbblz7mIu1nnvUDiHVjXXkulZULyidvo/7P6TY7+YqpV37IfF+KUFxmlK04elKtGKXaiVgw==", + "requires": { + "@babel/helper-compilation-targets": "^7.13.0", + "@babel/helper-module-imports": "^7.12.13", + "@babel/helper-plugin-utils": "^7.13.0", + "@babel/traverse": "^7.13.0", + "debug": "^4.1.1", + "lodash.debounce": "^4.0.8", + "resolve": "^1.14.2", + "semver": "^6.1.2" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" + } + } + }, + "@babel/helper-explode-assignable-expression": { + "version": "7.13.0", + "resolved": "https://registry.npmjs.org/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.13.0.tgz", + "integrity": "sha512-qS0peLTDP8kOisG1blKbaoBg/o9OSa1qoumMjTK5pM+KDTtpxpsiubnCGP34vK8BXGcb2M9eigwgvoJryrzwWA==", + "requires": { + "@babel/types": "^7.13.0" + } + }, + "@babel/helper-function-name": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.12.13.tgz", + "integrity": "sha512-TZvmPn0UOqmvi5G4vvw0qZTpVptGkB1GL61R6lKvrSdIxGm5Pky7Q3fpKiIkQCAtRCBUwB0PaThlx9vebCDSwA==", + "requires": { + "@babel/helper-get-function-arity": "^7.12.13", + "@babel/template": "^7.12.13", + "@babel/types": "^7.12.13" + } + }, + "@babel/helper-get-function-arity": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.12.13.tgz", + "integrity": "sha512-DjEVzQNz5LICkzN0REdpD5prGoidvbdYk1BVgRUOINaWJP2t6avB27X1guXK1kXNrX0WMfsrm1A/ZBthYuIMQg==", + "requires": { + "@babel/types": "^7.12.13" + } + }, + "@babel/helper-hoist-variables": { + "version": "7.13.16", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.13.16.tgz", + "integrity": "sha512-1eMtTrXtrwscjcAeO4BVK+vvkxaLJSPFz1w1KLawz6HLNi9bPFGBNwwDyVfiu1Tv/vRRFYfoGaKhmAQPGPn5Wg==", + "requires": { + "@babel/traverse": "^7.13.15", + "@babel/types": "^7.13.16" + } + }, + "@babel/helper-member-expression-to-functions": { + "version": "7.13.12", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.13.12.tgz", + "integrity": "sha512-48ql1CLL59aKbU94Y88Xgb2VFy7a95ykGRbJJaaVv+LX5U8wFpLfiGXJJGUozsmA1oEh/o5Bp60Voq7ACyA/Sw==", + "requires": { + "@babel/types": "^7.13.12" + } + }, + "@babel/helper-module-imports": { + "version": "7.13.12", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.13.12.tgz", + "integrity": "sha512-4cVvR2/1B693IuOvSI20xqqa/+bl7lqAMR59R4iu39R9aOX8/JoYY1sFaNvUMyMBGnHdwvJgUrzNLoUZxXypxA==", + "requires": { + "@babel/types": "^7.13.12" + } + }, + "@babel/helper-module-transforms": { + "version": "7.13.14", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.13.14.tgz", + "integrity": "sha512-QuU/OJ0iAOSIatyVZmfqB0lbkVP0kDRiKj34xy+QNsnVZi/PA6BoSoreeqnxxa9EHFAIL0R9XOaAR/G9WlIy5g==", + "requires": { + "@babel/helper-module-imports": "^7.13.12", + "@babel/helper-replace-supers": "^7.13.12", + "@babel/helper-simple-access": "^7.13.12", + "@babel/helper-split-export-declaration": "^7.12.13", + "@babel/helper-validator-identifier": "^7.12.11", + "@babel/template": "^7.12.13", + "@babel/traverse": "^7.13.13", + "@babel/types": "^7.13.14" + } + }, + "@babel/helper-optimise-call-expression": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.12.13.tgz", + "integrity": "sha512-BdWQhoVJkp6nVjB7nkFWcn43dkprYauqtk++Py2eaf/GRDFm5BxRqEIZCiHlZUGAVmtwKcsVL1dC68WmzeFmiA==", + "requires": { + "@babel/types": "^7.12.13" + } + }, + "@babel/helper-plugin-utils": { + "version": "7.13.0", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.13.0.tgz", + "integrity": "sha512-ZPafIPSwzUlAoWT8DKs1W2VyF2gOWthGd5NGFMsBcMMol+ZhK+EQY/e6V96poa6PA/Bh+C9plWN0hXO1uB8AfQ==" + }, + "@babel/helper-remap-async-to-generator": { + "version": "7.13.0", + "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.13.0.tgz", + "integrity": "sha512-pUQpFBE9JvC9lrQbpX0TmeNIy5s7GnZjna2lhhcHC7DzgBs6fWn722Y5cfwgrtrqc7NAJwMvOa0mKhq6XaE4jg==", + "requires": { + "@babel/helper-annotate-as-pure": "^7.12.13", + "@babel/helper-wrap-function": "^7.13.0", + "@babel/types": "^7.13.0" + } + }, + "@babel/helper-replace-supers": { + "version": "7.13.12", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.13.12.tgz", + "integrity": "sha512-Gz1eiX+4yDO8mT+heB94aLVNCL+rbuT2xy4YfyNqu8F+OI6vMvJK891qGBTqL9Uc8wxEvRW92Id6G7sDen3fFw==", + "requires": { + "@babel/helper-member-expression-to-functions": "^7.13.12", + "@babel/helper-optimise-call-expression": "^7.12.13", + "@babel/traverse": "^7.13.0", + "@babel/types": "^7.13.12" + } + }, + "@babel/helper-simple-access": { + "version": "7.13.12", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.13.12.tgz", + "integrity": "sha512-7FEjbrx5SL9cWvXioDbnlYTppcZGuCY6ow3/D5vMggb2Ywgu4dMrpTJX0JdQAIcRRUElOIxF3yEooa9gUb9ZbA==", + "requires": { + "@babel/types": "^7.13.12" + } + }, + "@babel/helper-skip-transparent-expression-wrappers": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.12.1.tgz", + "integrity": "sha512-Mf5AUuhG1/OCChOJ/HcADmvcHM42WJockombn8ATJG3OnyiSxBK/Mm5x78BQWvmtXZKHgbjdGL2kin/HOLlZGA==", + "requires": { + "@babel/types": "^7.12.1" + } + }, + "@babel/helper-split-export-declaration": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.12.13.tgz", + "integrity": "sha512-tCJDltF83htUtXx5NLcaDqRmknv652ZWCHyoTETf1CXYJdPC7nohZohjUgieXhv0hTJdRf2FjDueFehdNucpzg==", + "requires": { + "@babel/types": "^7.12.13" + } + }, + "@babel/helper-validator-identifier": { + "version": "7.12.11", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.12.11.tgz", + "integrity": "sha512-np/lG3uARFybkoHokJUmf1QfEvRVCPbmQeUQpKow5cQ3xWrV9i3rUHodKDJPQfTVX61qKi+UdYk8kik84n7XOw==" + }, + "@babel/helper-validator-option": { + "version": "7.12.17", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.12.17.tgz", + "integrity": "sha512-TopkMDmLzq8ngChwRlyjR6raKD6gMSae4JdYDB8bByKreQgG0RBTuKe9LRxW3wFtUnjxOPRKBDwEH6Mg5KeDfw==" + }, + "@babel/helper-wrap-function": { + "version": "7.13.0", + "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.13.0.tgz", + "integrity": "sha512-1UX9F7K3BS42fI6qd2A4BjKzgGjToscyZTdp1DjknHLCIvpgne6918io+aL5LXFcER/8QWiwpoY902pVEqgTXA==", + "requires": { + "@babel/helper-function-name": "^7.12.13", + "@babel/template": "^7.12.13", + "@babel/traverse": "^7.13.0", + "@babel/types": "^7.13.0" + } + }, + "@babel/helpers": { + "version": "7.13.17", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.13.17.tgz", + "integrity": "sha512-Eal4Gce4kGijo1/TGJdqp3WuhllaMLSrW6XcL0ulyUAQOuxHcCafZE8KHg9857gcTehsm/v7RcOx2+jp0Ryjsg==", + "requires": { + "@babel/template": "^7.12.13", + "@babel/traverse": "^7.13.17", + "@babel/types": "^7.13.17" + } + }, + "@babel/highlight": { + "version": "7.13.10", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.13.10.tgz", + "integrity": "sha512-5aPpe5XQPzflQrFwL1/QoeHkP2MsA4JCntcXHRhEsdsfPVkvPi2w7Qix4iV7t5S/oC9OodGrggd8aco1g3SZFg==", + "requires": { + "@babel/helper-validator-identifier": "^7.12.11", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + } + }, + "@babel/parser": { + "version": "7.13.16", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.13.16.tgz", + "integrity": "sha512-6bAg36mCwuqLO0hbR+z7PHuqWiCeP7Dzg73OpQwsAB1Eb8HnGEz5xYBzCfbu+YjoaJsJs+qheDxVAuqbt3ILEw==" + }, + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { + "version": "7.13.12", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.13.12.tgz", + "integrity": "sha512-d0u3zWKcoZf379fOeJdr1a5WPDny4aOFZ6hlfKivgK0LY7ZxNfoaHL2fWwdGtHyVvra38FC+HVYkO+byfSA8AQ==", + "requires": { + "@babel/helper-plugin-utils": "^7.13.0", + "@babel/helper-skip-transparent-expression-wrappers": "^7.12.1", + "@babel/plugin-proposal-optional-chaining": "^7.13.12" + } + }, + "@babel/plugin-proposal-async-generator-functions": { + "version": "7.13.15", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.13.15.tgz", + "integrity": "sha512-VapibkWzFeoa6ubXy/NgV5U2U4MVnUlvnx6wo1XhlsaTrLYWE0UFpDQsVrmn22q5CzeloqJ8gEMHSKxuee6ZdA==", + "requires": { + "@babel/helper-plugin-utils": "^7.13.0", + "@babel/helper-remap-async-to-generator": "^7.13.0", + "@babel/plugin-syntax-async-generators": "^7.8.4" + } + }, + "@babel/plugin-proposal-class-properties": { + "version": "7.13.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.13.0.tgz", + "integrity": "sha512-KnTDjFNC1g+45ka0myZNvSBFLhNCLN+GeGYLDEA8Oq7MZ6yMgfLoIRh86GRT0FjtJhZw8JyUskP9uvj5pHM9Zg==", + "requires": { + "@babel/helper-create-class-features-plugin": "^7.13.0", + "@babel/helper-plugin-utils": "^7.13.0" + } + }, + "@babel/plugin-proposal-decorators": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.12.1.tgz", + "integrity": "sha512-knNIuusychgYN8fGJHONL0RbFxLGawhXOJNLBk75TniTsZZeA+wdkDuv6wp4lGwzQEKjZi6/WYtnb3udNPmQmQ==", + "requires": { + "@babel/helper-create-class-features-plugin": "^7.12.1", + "@babel/helper-plugin-utils": "^7.10.4", + "@babel/plugin-syntax-decorators": "^7.12.1" + } + }, + "@babel/plugin-proposal-dynamic-import": { + "version": "7.13.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.13.8.tgz", + "integrity": "sha512-ONWKj0H6+wIRCkZi9zSbZtE/r73uOhMVHh256ys0UzfM7I3d4n+spZNWjOnJv2gzopumP2Wxi186vI8N0Y2JyQ==", + "requires": { + "@babel/helper-plugin-utils": "^7.13.0", + "@babel/plugin-syntax-dynamic-import": "^7.8.3" + } + }, + "@babel/plugin-proposal-export-namespace-from": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.12.13.tgz", + "integrity": "sha512-INAgtFo4OnLN3Y/j0VwAgw3HDXcDtX+C/erMvWzuV9v71r7urb6iyMXu7eM9IgLr1ElLlOkaHjJ0SbCmdOQ3Iw==", + "requires": { + "@babel/helper-plugin-utils": "^7.12.13", + "@babel/plugin-syntax-export-namespace-from": "^7.8.3" + } + }, + "@babel/plugin-proposal-json-strings": { + "version": "7.13.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.13.8.tgz", + "integrity": "sha512-w4zOPKUFPX1mgvTmL/fcEqy34hrQ1CRcGxdphBc6snDnnqJ47EZDIyop6IwXzAC8G916hsIuXB2ZMBCExC5k7Q==", + "requires": { + "@babel/helper-plugin-utils": "^7.13.0", + "@babel/plugin-syntax-json-strings": "^7.8.3" + } + }, + "@babel/plugin-proposal-logical-assignment-operators": { + "version": "7.13.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.13.8.tgz", + "integrity": "sha512-aul6znYB4N4HGweImqKn59Su9RS8lbUIqxtXTOcAGtNIDczoEFv+l1EhmX8rUBp3G1jMjKJm8m0jXVp63ZpS4A==", + "requires": { + "@babel/helper-plugin-utils": "^7.13.0", + "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4" + } + }, + "@babel/plugin-proposal-nullish-coalescing-operator": { + "version": "7.13.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.13.8.tgz", + "integrity": "sha512-iePlDPBn//UhxExyS9KyeYU7RM9WScAG+D3Hhno0PLJebAEpDZMocbDe64eqynhNAnwz/vZoL/q/QB2T1OH39A==", + "requires": { + "@babel/helper-plugin-utils": "^7.13.0", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3" + } + }, + "@babel/plugin-proposal-numeric-separator": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.12.13.tgz", + "integrity": "sha512-O1jFia9R8BUCl3ZGB7eitaAPu62TXJRHn7rh+ojNERCFyqRwJMTmhz+tJ+k0CwI6CLjX/ee4qW74FSqlq9I35w==", + "requires": { + "@babel/helper-plugin-utils": "^7.12.13", + "@babel/plugin-syntax-numeric-separator": "^7.10.4" + } + }, + "@babel/plugin-proposal-object-rest-spread": { + "version": "7.13.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.13.8.tgz", + "integrity": "sha512-DhB2EuB1Ih7S3/IRX5AFVgZ16k3EzfRbq97CxAVI1KSYcW+lexV8VZb7G7L8zuPVSdQMRn0kiBpf/Yzu9ZKH0g==", + "requires": { + "@babel/compat-data": "^7.13.8", + "@babel/helper-compilation-targets": "^7.13.8", + "@babel/helper-plugin-utils": "^7.13.0", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-transform-parameters": "^7.13.0" + } + }, + "@babel/plugin-proposal-optional-catch-binding": { + "version": "7.13.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.13.8.tgz", + "integrity": "sha512-0wS/4DUF1CuTmGo+NiaHfHcVSeSLj5S3e6RivPTg/2k3wOv3jO35tZ6/ZWsQhQMvdgI7CwphjQa/ccarLymHVA==", + "requires": { + "@babel/helper-plugin-utils": "^7.13.0", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3" + } + }, + "@babel/plugin-proposal-optional-chaining": { + "version": "7.13.12", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.13.12.tgz", + "integrity": "sha512-fcEdKOkIB7Tf4IxrgEVeFC4zeJSTr78no9wTdBuZZbqF64kzllU0ybo2zrzm7gUQfxGhBgq4E39oRs8Zx/RMYQ==", + "requires": { + "@babel/helper-plugin-utils": "^7.13.0", + "@babel/helper-skip-transparent-expression-wrappers": "^7.12.1", + "@babel/plugin-syntax-optional-chaining": "^7.8.3" + } + }, + "@babel/plugin-proposal-private-methods": { + "version": "7.13.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.13.0.tgz", + "integrity": "sha512-MXyyKQd9inhx1kDYPkFRVOBXQ20ES8Pto3T7UZ92xj2mY0EVD8oAVzeyYuVfy/mxAdTSIayOvg+aVzcHV2bn6Q==", + "requires": { + "@babel/helper-create-class-features-plugin": "^7.13.0", + "@babel/helper-plugin-utils": "^7.13.0" + } + }, + "@babel/plugin-proposal-unicode-property-regex": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.12.13.tgz", + "integrity": "sha512-XyJmZidNfofEkqFV5VC/bLabGmO5QzenPO/YOfGuEbgU+2sSwMmio3YLb4WtBgcmmdwZHyVyv8on77IUjQ5Gvg==", + "requires": { + "@babel/helper-create-regexp-features-plugin": "^7.12.13", + "@babel/helper-plugin-utils": "^7.12.13" + } + }, + "@babel/plugin-syntax-async-generators": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", + "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-bigint": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", + "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-class-properties": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", + "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", + "requires": { + "@babel/helper-plugin-utils": "^7.12.13" + } + }, + "@babel/plugin-syntax-decorators": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-decorators/-/plugin-syntax-decorators-7.12.13.tgz", + "integrity": "sha512-Rw6aIXGuqDLr6/LoBBYE57nKOzQpz/aDkKlMqEwH+Vp0MXbG6H/TfRjaY343LKxzAKAMXIHsQ8JzaZKuDZ9MwA==", + "requires": { + "@babel/helper-plugin-utils": "^7.12.13" + } + }, + "@babel/plugin-syntax-dynamic-import": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz", + "integrity": "sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==", + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-export-namespace-from": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-export-namespace-from/-/plugin-syntax-export-namespace-from-7.8.3.tgz", + "integrity": "sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q==", + "requires": { + "@babel/helper-plugin-utils": "^7.8.3" + } + }, + "@babel/plugin-syntax-flow": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.12.13.tgz", + "integrity": "sha512-J/RYxnlSLXZLVR7wTRsozxKT8qbsx1mNKJzXEEjQ0Kjx1ZACcyHgbanNWNCFtc36IzuWhYWPpvJFFoexoOWFmA==", + "requires": { + "@babel/helper-plugin-utils": "^7.12.13" + } + }, + "@babel/plugin-syntax-import-meta": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", + "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-syntax-json-strings": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", + "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-jsx": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.12.13.tgz", + "integrity": "sha512-d4HM23Q1K7oq/SLNmG6mRt85l2csmQ0cHRaxRXjKW0YFdEXqlZ5kzFQKH5Uc3rDJECgu+yCRgPkG04Mm98R/1g==", + "requires": { + "@babel/helper-plugin-utils": "^7.12.13" + } + }, + "@babel/plugin-syntax-logical-assignment-operators": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", + "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-syntax-nullish-coalescing-operator": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", + "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-numeric-separator": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", + "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-syntax-object-rest-spread": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", + "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-optional-catch-binding": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", + "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-optional-chaining": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", + "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-top-level-await": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.12.13.tgz", + "integrity": "sha512-A81F9pDwyS7yM//KwbCSDqy3Uj4NMIurtplxphWxoYtNPov7cJsDkAFNNyVlIZ3jwGycVsurZ+LtOA8gZ376iQ==", + "requires": { + "@babel/helper-plugin-utils": "^7.12.13" + } + }, + "@babel/plugin-syntax-typescript": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.12.13.tgz", + "integrity": "sha512-cHP3u1JiUiG2LFDKbXnwVad81GvfyIOmCD6HIEId6ojrY0Drfy2q1jw7BwN7dE84+kTnBjLkXoL3IEy/3JPu2w==", + "requires": { + "@babel/helper-plugin-utils": "^7.12.13" + } + }, + "@babel/plugin-transform-arrow-functions": { + "version": "7.13.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.13.0.tgz", + "integrity": "sha512-96lgJagobeVmazXFaDrbmCLQxBysKu7U6Do3mLsx27gf5Dk85ezysrs2BZUpXD703U/Su1xTBDxxar2oa4jAGg==", + "requires": { + "@babel/helper-plugin-utils": "^7.13.0" + } + }, + "@babel/plugin-transform-async-to-generator": { + "version": "7.13.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.13.0.tgz", + "integrity": "sha512-3j6E004Dx0K3eGmhxVJxwwI89CTJrce7lg3UrtFuDAVQ/2+SJ/h/aSFOeE6/n0WB1GsOffsJp6MnPQNQ8nmwhg==", + "requires": { + "@babel/helper-module-imports": "^7.12.13", + "@babel/helper-plugin-utils": "^7.13.0", + "@babel/helper-remap-async-to-generator": "^7.13.0" + } + }, + "@babel/plugin-transform-block-scoped-functions": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.12.13.tgz", + "integrity": "sha512-zNyFqbc3kI/fVpqwfqkg6RvBgFpC4J18aKKMmv7KdQ/1GgREapSJAykLMVNwfRGO3BtHj3YQZl8kxCXPcVMVeg==", + "requires": { + "@babel/helper-plugin-utils": "^7.12.13" + } + }, + "@babel/plugin-transform-block-scoping": { + "version": "7.13.16", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.13.16.tgz", + "integrity": "sha512-ad3PHUxGnfWF4Efd3qFuznEtZKoBp0spS+DgqzVzRPV7urEBvPLue3y2j80w4Jf2YLzZHj8TOv/Lmvdmh3b2xg==", + "requires": { + "@babel/helper-plugin-utils": "^7.13.0" + } + }, + "@babel/plugin-transform-classes": { + "version": "7.13.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.13.0.tgz", + "integrity": "sha512-9BtHCPUARyVH1oXGcSJD3YpsqRLROJx5ZNP6tN5vnk17N0SVf9WCtf8Nuh1CFmgByKKAIMstitKduoCmsaDK5g==", + "requires": { + "@babel/helper-annotate-as-pure": "^7.12.13", + "@babel/helper-function-name": "^7.12.13", + "@babel/helper-optimise-call-expression": "^7.12.13", + "@babel/helper-plugin-utils": "^7.13.0", + "@babel/helper-replace-supers": "^7.13.0", + "@babel/helper-split-export-declaration": "^7.12.13", + "globals": "^11.1.0" + } + }, + "@babel/plugin-transform-computed-properties": { + "version": "7.13.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.13.0.tgz", + "integrity": "sha512-RRqTYTeZkZAz8WbieLTvKUEUxZlUTdmL5KGMyZj7FnMfLNKV4+r5549aORG/mgojRmFlQMJDUupwAMiF2Q7OUg==", + "requires": { + "@babel/helper-plugin-utils": "^7.13.0" + } + }, + "@babel/plugin-transform-destructuring": { + "version": "7.13.17", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.13.17.tgz", + "integrity": "sha512-UAUqiLv+uRLO+xuBKKMEpC+t7YRNVRqBsWWq1yKXbBZBje/t3IXCiSinZhjn/DC3qzBfICeYd2EFGEbHsh5RLA==", + "requires": { + "@babel/helper-plugin-utils": "^7.13.0" + } + }, + "@babel/plugin-transform-dotall-regex": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.12.13.tgz", + "integrity": "sha512-foDrozE65ZFdUC2OfgeOCrEPTxdB3yjqxpXh8CH+ipd9CHd4s/iq81kcUpyH8ACGNEPdFqbtzfgzbT/ZGlbDeQ==", + "requires": { + "@babel/helper-create-regexp-features-plugin": "^7.12.13", + "@babel/helper-plugin-utils": "^7.12.13" + } + }, + "@babel/plugin-transform-duplicate-keys": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.12.13.tgz", + "integrity": "sha512-NfADJiiHdhLBW3pulJlJI2NB0t4cci4WTZ8FtdIuNc2+8pslXdPtRRAEWqUY+m9kNOk2eRYbTAOipAxlrOcwwQ==", + "requires": { + "@babel/helper-plugin-utils": "^7.12.13" + } + }, + "@babel/plugin-transform-exponentiation-operator": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.12.13.tgz", + "integrity": "sha512-fbUelkM1apvqez/yYx1/oICVnGo2KM5s63mhGylrmXUxK/IAXSIf87QIxVfZldWf4QsOafY6vV3bX8aMHSvNrA==", + "requires": { + "@babel/helper-builder-binary-assignment-operator-visitor": "^7.12.13", + "@babel/helper-plugin-utils": "^7.12.13" + } + }, + "@babel/plugin-transform-flow-strip-types": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.12.1.tgz", + "integrity": "sha512-8hAtkmsQb36yMmEtk2JZ9JnVyDSnDOdlB+0nEGzIDLuK4yR3JcEjfuFPYkdEPSh8Id+rAMeBEn+X0iVEyho6Hg==", + "requires": { + "@babel/helper-plugin-utils": "^7.10.4", + "@babel/plugin-syntax-flow": "^7.12.1" + } + }, + "@babel/plugin-transform-for-of": { + "version": "7.13.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.13.0.tgz", + "integrity": "sha512-IHKT00mwUVYE0zzbkDgNRP6SRzvfGCYsOxIRz8KsiaaHCcT9BWIkO+H9QRJseHBLOGBZkHUdHiqj6r0POsdytg==", + "requires": { + "@babel/helper-plugin-utils": "^7.13.0" + } + }, + "@babel/plugin-transform-function-name": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.12.13.tgz", + "integrity": "sha512-6K7gZycG0cmIwwF7uMK/ZqeCikCGVBdyP2J5SKNCXO5EOHcqi+z7Jwf8AmyDNcBgxET8DrEtCt/mPKPyAzXyqQ==", + "requires": { + "@babel/helper-function-name": "^7.12.13", + "@babel/helper-plugin-utils": "^7.12.13" + } + }, + "@babel/plugin-transform-literals": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.12.13.tgz", + "integrity": "sha512-FW+WPjSR7hiUxMcKqyNjP05tQ2kmBCdpEpZHY1ARm96tGQCCBvXKnpjILtDplUnJ/eHZ0lALLM+d2lMFSpYJrQ==", + "requires": { + "@babel/helper-plugin-utils": "^7.12.13" + } + }, + "@babel/plugin-transform-member-expression-literals": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.12.13.tgz", + "integrity": "sha512-kxLkOsg8yir4YeEPHLuO2tXP9R/gTjpuTOjshqSpELUN3ZAg2jfDnKUvzzJxObun38sw3wm4Uu69sX/zA7iRvg==", + "requires": { + "@babel/helper-plugin-utils": "^7.12.13" + } + }, + "@babel/plugin-transform-modules-amd": { + "version": "7.13.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.13.0.tgz", + "integrity": "sha512-EKy/E2NHhY/6Vw5d1k3rgoobftcNUmp9fGjb9XZwQLtTctsRBOTRO7RHHxfIky1ogMN5BxN7p9uMA3SzPfotMQ==", + "requires": { + "@babel/helper-module-transforms": "^7.13.0", + "@babel/helper-plugin-utils": "^7.13.0", + "babel-plugin-dynamic-import-node": "^2.3.3" + } + }, + "@babel/plugin-transform-modules-commonjs": { + "version": "7.13.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.13.8.tgz", + "integrity": "sha512-9QiOx4MEGglfYZ4XOnU79OHr6vIWUakIj9b4mioN8eQIoEh+pf5p/zEB36JpDFWA12nNMiRf7bfoRvl9Rn79Bw==", + "requires": { + "@babel/helper-module-transforms": "^7.13.0", + "@babel/helper-plugin-utils": "^7.13.0", + "@babel/helper-simple-access": "^7.12.13", + "babel-plugin-dynamic-import-node": "^2.3.3" + } + }, + "@babel/plugin-transform-modules-systemjs": { + "version": "7.13.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.13.8.tgz", + "integrity": "sha512-hwqctPYjhM6cWvVIlOIe27jCIBgHCsdH2xCJVAYQm7V5yTMoilbVMi9f6wKg0rpQAOn6ZG4AOyvCqFF/hUh6+A==", + "requires": { + "@babel/helper-hoist-variables": "^7.13.0", + "@babel/helper-module-transforms": "^7.13.0", + "@babel/helper-plugin-utils": "^7.13.0", + "@babel/helper-validator-identifier": "^7.12.11", + "babel-plugin-dynamic-import-node": "^2.3.3" + } + }, + "@babel/plugin-transform-modules-umd": { + "version": "7.13.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.13.0.tgz", + "integrity": "sha512-D/ILzAh6uyvkWjKKyFE/W0FzWwasv6vPTSqPcjxFqn6QpX3u8DjRVliq4F2BamO2Wee/om06Vyy+vPkNrd4wxw==", + "requires": { + "@babel/helper-module-transforms": "^7.13.0", + "@babel/helper-plugin-utils": "^7.13.0" + } + }, + "@babel/plugin-transform-named-capturing-groups-regex": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.12.13.tgz", + "integrity": "sha512-Xsm8P2hr5hAxyYblrfACXpQKdQbx4m2df9/ZZSQ8MAhsadw06+jW7s9zsSw6he+mJZXRlVMyEnVktJo4zjk1WA==", + "requires": { + "@babel/helper-create-regexp-features-plugin": "^7.12.13" + } + }, + "@babel/plugin-transform-new-target": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.12.13.tgz", + "integrity": "sha512-/KY2hbLxrG5GTQ9zzZSc3xWiOy379pIETEhbtzwZcw9rvuaVV4Fqy7BYGYOWZnaoXIQYbbJ0ziXLa/sKcGCYEQ==", + "requires": { + "@babel/helper-plugin-utils": "^7.12.13" + } + }, + "@babel/plugin-transform-object-super": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.12.13.tgz", + "integrity": "sha512-JzYIcj3XtYspZDV8j9ulnoMPZZnF/Cj0LUxPOjR89BdBVx+zYJI9MdMIlUZjbXDX+6YVeS6I3e8op+qQ3BYBoQ==", + "requires": { + "@babel/helper-plugin-utils": "^7.12.13", + "@babel/helper-replace-supers": "^7.12.13" + } + }, + "@babel/plugin-transform-parameters": { + "version": "7.13.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.13.0.tgz", + "integrity": "sha512-Jt8k/h/mIwE2JFEOb3lURoY5C85ETcYPnbuAJ96zRBzh1XHtQZfs62ChZ6EP22QlC8c7Xqr9q+e1SU5qttwwjw==", + "requires": { + "@babel/helper-plugin-utils": "^7.13.0" + } + }, + "@babel/plugin-transform-property-literals": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.12.13.tgz", + "integrity": "sha512-nqVigwVan+lR+g8Fj8Exl0UQX2kymtjcWfMOYM1vTYEKujeyv2SkMgazf2qNcK7l4SDiKyTA/nHCPqL4e2zo1A==", + "requires": { + "@babel/helper-plugin-utils": "^7.12.13" + } + }, + "@babel/plugin-transform-react-constant-elements": { + "version": "7.13.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-constant-elements/-/plugin-transform-react-constant-elements-7.13.13.tgz", + "integrity": "sha512-SNJU53VM/SjQL0bZhyU+f4kJQz7bQQajnrZRSaU21hruG/NWY41AEM9AWXeXX90pYr/C2yAmTgI6yW3LlLrAUQ==", + "requires": { + "@babel/helper-plugin-utils": "^7.13.0" + } + }, + "@babel/plugin-transform-react-display-name": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.12.13.tgz", + "integrity": "sha512-MprESJzI9O5VnJZrL7gg1MpdqmiFcUv41Jc7SahxYsNP2kDkFqClxxTZq+1Qv4AFCamm+GXMRDQINNn+qrxmiA==", + "requires": { + "@babel/helper-plugin-utils": "^7.12.13" + } + }, + "@babel/plugin-transform-react-jsx": { + "version": "7.13.12", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.13.12.tgz", + "integrity": "sha512-jcEI2UqIcpCqB5U5DRxIl0tQEProI2gcu+g8VTIqxLO5Iidojb4d77q+fwGseCvd8af/lJ9masp4QWzBXFE2xA==", + "requires": { + "@babel/helper-annotate-as-pure": "^7.12.13", + "@babel/helper-module-imports": "^7.13.12", + "@babel/helper-plugin-utils": "^7.13.0", + "@babel/plugin-syntax-jsx": "^7.12.13", + "@babel/types": "^7.13.12" + } + }, + "@babel/plugin-transform-react-jsx-development": { + "version": "7.12.17", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.12.17.tgz", + "integrity": "sha512-BPjYV86SVuOaudFhsJR1zjgxxOhJDt6JHNoD48DxWEIxUCAMjV1ys6DYw4SDYZh0b1QsS2vfIA9t/ZsQGsDOUQ==", + "requires": { + "@babel/plugin-transform-react-jsx": "^7.12.17" + } + }, + "@babel/plugin-transform-react-jsx-self": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.12.13.tgz", + "integrity": "sha512-FXYw98TTJ125GVCCkFLZXlZ1qGcsYqNQhVBQcZjyrwf8FEUtVfKIoidnO8S0q+KBQpDYNTmiGo1gn67Vti04lQ==", + "requires": { + "@babel/helper-plugin-utils": "^7.12.13" + } + }, + "@babel/plugin-transform-react-jsx-source": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.12.13.tgz", + "integrity": "sha512-O5JJi6fyfih0WfDgIJXksSPhGP/G0fQpfxYy87sDc+1sFmsCS6wr3aAn+whbzkhbjtq4VMqLRaSzR6IsshIC0Q==", + "requires": { + "@babel/helper-plugin-utils": "^7.12.13" + } + }, + "@babel/plugin-transform-react-pure-annotations": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.12.1.tgz", + "integrity": "sha512-RqeaHiwZtphSIUZ5I85PEH19LOSzxfuEazoY7/pWASCAIBuATQzpSVD+eT6MebeeZT2F4eSL0u4vw6n4Nm0Mjg==", + "requires": { + "@babel/helper-annotate-as-pure": "^7.10.4", + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-transform-regenerator": { + "version": "7.13.15", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.13.15.tgz", + "integrity": "sha512-Bk9cOLSz8DiurcMETZ8E2YtIVJbFCPGW28DJWUakmyVWtQSm6Wsf0p4B4BfEr/eL2Nkhe/CICiUiMOCi1TPhuQ==", + "requires": { + "regenerator-transform": "^0.14.2" + } + }, + "@babel/plugin-transform-reserved-words": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.12.13.tgz", + "integrity": "sha512-xhUPzDXxZN1QfiOy/I5tyye+TRz6lA7z6xaT4CLOjPRMVg1ldRf0LHw0TDBpYL4vG78556WuHdyO9oi5UmzZBg==", + "requires": { + "@babel/helper-plugin-utils": "^7.12.13" + } + }, + "@babel/plugin-transform-runtime": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.12.1.tgz", + "integrity": "sha512-Ac/H6G9FEIkS2tXsZjL4RAdS3L3WHxci0usAnz7laPWUmFiGtj7tIASChqKZMHTSQTQY6xDbOq+V1/vIq3QrWg==", + "requires": { + "@babel/helper-module-imports": "^7.12.1", + "@babel/helper-plugin-utils": "^7.10.4", + "resolve": "^1.8.1", + "semver": "^5.5.1" + }, + "dependencies": { + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" + } + } + }, + "@babel/plugin-transform-shorthand-properties": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.12.13.tgz", + "integrity": "sha512-xpL49pqPnLtf0tVluuqvzWIgLEhuPpZzvs2yabUHSKRNlN7ScYU7aMlmavOeyXJZKgZKQRBlh8rHbKiJDraTSw==", + "requires": { + "@babel/helper-plugin-utils": "^7.12.13" + } + }, + "@babel/plugin-transform-spread": { + "version": "7.13.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.13.0.tgz", + "integrity": "sha512-V6vkiXijjzYeFmQTr3dBxPtZYLPcUfY34DebOU27jIl2M/Y8Egm52Hw82CSjjPqd54GTlJs5x+CR7HeNr24ckg==", + "requires": { + "@babel/helper-plugin-utils": "^7.13.0", + "@babel/helper-skip-transparent-expression-wrappers": "^7.12.1" + } + }, + "@babel/plugin-transform-sticky-regex": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.12.13.tgz", + "integrity": "sha512-Jc3JSaaWT8+fr7GRvQP02fKDsYk4K/lYwWq38r/UGfaxo89ajud321NH28KRQ7xy1Ybc0VUE5Pz8psjNNDUglg==", + "requires": { + "@babel/helper-plugin-utils": "^7.12.13" + } + }, + "@babel/plugin-transform-template-literals": { + "version": "7.13.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.13.0.tgz", + "integrity": "sha512-d67umW6nlfmr1iehCcBv69eSUSySk1EsIS8aTDX4Xo9qajAh6mYtcl4kJrBkGXuxZPEgVr7RVfAvNW6YQkd4Mw==", + "requires": { + "@babel/helper-plugin-utils": "^7.13.0" + } + }, + "@babel/plugin-transform-typeof-symbol": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.12.13.tgz", + "integrity": "sha512-eKv/LmUJpMnu4npgfvs3LiHhJua5fo/CysENxa45YCQXZwKnGCQKAg87bvoqSW1fFT+HA32l03Qxsm8ouTY3ZQ==", + "requires": { + "@babel/helper-plugin-utils": "^7.12.13" + } + }, + "@babel/plugin-transform-typescript": { + "version": "7.13.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.13.0.tgz", + "integrity": "sha512-elQEwluzaU8R8dbVuW2Q2Y8Nznf7hnjM7+DSCd14Lo5fF63C9qNLbwZYbmZrtV9/ySpSUpkRpQXvJb6xyu4hCQ==", + "requires": { + "@babel/helper-create-class-features-plugin": "^7.13.0", + "@babel/helper-plugin-utils": "^7.13.0", + "@babel/plugin-syntax-typescript": "^7.12.13" + } + }, + "@babel/plugin-transform-unicode-escapes": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.12.13.tgz", + "integrity": "sha512-0bHEkdwJ/sN/ikBHfSmOXPypN/beiGqjo+o4/5K+vxEFNPRPdImhviPakMKG4x96l85emoa0Z6cDflsdBusZbw==", + "requires": { + "@babel/helper-plugin-utils": "^7.12.13" + } + }, + "@babel/plugin-transform-unicode-regex": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.12.13.tgz", + "integrity": "sha512-mDRzSNY7/zopwisPZ5kM9XKCfhchqIYwAKRERtEnhYscZB79VRekuRSoYbN0+KVe3y8+q1h6A4svXtP7N+UoCA==", + "requires": { + "@babel/helper-create-regexp-features-plugin": "^7.12.13", + "@babel/helper-plugin-utils": "^7.12.13" + } + }, + "@babel/preset-env": { + "version": "7.13.15", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.13.15.tgz", + "integrity": "sha512-D4JAPMXcxk69PKe81jRJ21/fP/uYdcTZ3hJDF5QX2HSI9bBxxYw/dumdR6dGumhjxlprHPE4XWoPaqzZUVy2MA==", + "requires": { + "@babel/compat-data": "^7.13.15", + "@babel/helper-compilation-targets": "^7.13.13", + "@babel/helper-plugin-utils": "^7.13.0", + "@babel/helper-validator-option": "^7.12.17", + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.13.12", + "@babel/plugin-proposal-async-generator-functions": "^7.13.15", + "@babel/plugin-proposal-class-properties": "^7.13.0", + "@babel/plugin-proposal-dynamic-import": "^7.13.8", + "@babel/plugin-proposal-export-namespace-from": "^7.12.13", + "@babel/plugin-proposal-json-strings": "^7.13.8", + "@babel/plugin-proposal-logical-assignment-operators": "^7.13.8", + "@babel/plugin-proposal-nullish-coalescing-operator": "^7.13.8", + "@babel/plugin-proposal-numeric-separator": "^7.12.13", + "@babel/plugin-proposal-object-rest-spread": "^7.13.8", + "@babel/plugin-proposal-optional-catch-binding": "^7.13.8", + "@babel/plugin-proposal-optional-chaining": "^7.13.12", + "@babel/plugin-proposal-private-methods": "^7.13.0", + "@babel/plugin-proposal-unicode-property-regex": "^7.12.13", + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/plugin-syntax-class-properties": "^7.12.13", + "@babel/plugin-syntax-dynamic-import": "^7.8.3", + "@babel/plugin-syntax-export-namespace-from": "^7.8.3", + "@babel/plugin-syntax-json-strings": "^7.8.3", + "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", + "@babel/plugin-syntax-numeric-separator": "^7.10.4", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", + "@babel/plugin-syntax-optional-chaining": "^7.8.3", + "@babel/plugin-syntax-top-level-await": "^7.12.13", + "@babel/plugin-transform-arrow-functions": "^7.13.0", + "@babel/plugin-transform-async-to-generator": "^7.13.0", + "@babel/plugin-transform-block-scoped-functions": "^7.12.13", + "@babel/plugin-transform-block-scoping": "^7.12.13", + "@babel/plugin-transform-classes": "^7.13.0", + "@babel/plugin-transform-computed-properties": "^7.13.0", + "@babel/plugin-transform-destructuring": "^7.13.0", + "@babel/plugin-transform-dotall-regex": "^7.12.13", + "@babel/plugin-transform-duplicate-keys": "^7.12.13", + "@babel/plugin-transform-exponentiation-operator": "^7.12.13", + "@babel/plugin-transform-for-of": "^7.13.0", + "@babel/plugin-transform-function-name": "^7.12.13", + "@babel/plugin-transform-literals": "^7.12.13", + "@babel/plugin-transform-member-expression-literals": "^7.12.13", + "@babel/plugin-transform-modules-amd": "^7.13.0", + "@babel/plugin-transform-modules-commonjs": "^7.13.8", + "@babel/plugin-transform-modules-systemjs": "^7.13.8", + "@babel/plugin-transform-modules-umd": "^7.13.0", + "@babel/plugin-transform-named-capturing-groups-regex": "^7.12.13", + "@babel/plugin-transform-new-target": "^7.12.13", + "@babel/plugin-transform-object-super": "^7.12.13", + "@babel/plugin-transform-parameters": "^7.13.0", + "@babel/plugin-transform-property-literals": "^7.12.13", + "@babel/plugin-transform-regenerator": "^7.13.15", + "@babel/plugin-transform-reserved-words": "^7.12.13", + "@babel/plugin-transform-shorthand-properties": "^7.12.13", + "@babel/plugin-transform-spread": "^7.13.0", + "@babel/plugin-transform-sticky-regex": "^7.12.13", + "@babel/plugin-transform-template-literals": "^7.13.0", + "@babel/plugin-transform-typeof-symbol": "^7.12.13", + "@babel/plugin-transform-unicode-escapes": "^7.12.13", + "@babel/plugin-transform-unicode-regex": "^7.12.13", + "@babel/preset-modules": "^0.1.4", + "@babel/types": "^7.13.14", + "babel-plugin-polyfill-corejs2": "^0.2.0", + "babel-plugin-polyfill-corejs3": "^0.2.0", + "babel-plugin-polyfill-regenerator": "^0.2.0", + "core-js-compat": "^3.9.0", + "semver": "^6.3.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" + } + } + }, + "@babel/preset-modules": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.4.tgz", + "integrity": "sha512-J36NhwnfdzpmH41M1DrnkkgAqhZaqr/NBdPfQ677mLzlaXo+oDiv1deyCDtgAhz8p328otdob0Du7+xgHGZbKg==", + "requires": { + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/plugin-proposal-unicode-property-regex": "^7.4.4", + "@babel/plugin-transform-dotall-regex": "^7.4.4", + "@babel/types": "^7.4.4", + "esutils": "^2.0.2" + } + }, + "@babel/preset-react": { + "version": "7.13.13", + "resolved": "https://registry.npmjs.org/@babel/preset-react/-/preset-react-7.13.13.tgz", + "integrity": "sha512-gx+tDLIE06sRjKJkVtpZ/t3mzCDOnPG+ggHZG9lffUbX8+wC739x20YQc9V35Do6ZAxaUc/HhVHIiOzz5MvDmA==", + "requires": { + "@babel/helper-plugin-utils": "^7.13.0", + "@babel/helper-validator-option": "^7.12.17", + "@babel/plugin-transform-react-display-name": "^7.12.13", + "@babel/plugin-transform-react-jsx": "^7.13.12", + "@babel/plugin-transform-react-jsx-development": "^7.12.17", + "@babel/plugin-transform-react-pure-annotations": "^7.12.1" + } + }, + "@babel/preset-typescript": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.12.1.tgz", + "integrity": "sha512-hNK/DhmoJPsksdHuI/RVrcEws7GN5eamhi28JkO52MqIxU8Z0QpmiSOQxZHWOHV7I3P4UjHV97ay4TcamMA6Kw==", + "requires": { + "@babel/helper-plugin-utils": "^7.10.4", + "@babel/plugin-transform-typescript": "^7.12.1" + } + }, + "@babel/runtime": { + "version": "7.13.17", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.13.17.tgz", + "integrity": "sha512-NCdgJEelPTSh+FEFylhnP1ylq848l1z9t9N0j1Lfbcw0+KXGjsTvUmkxy+voLLXB5SOKMbLLx4jxYliGrYQseA==", + "requires": { + "regenerator-runtime": "^0.13.4" + } + }, + "@babel/runtime-corejs3": { + "version": "7.13.17", + "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.13.17.tgz", + "integrity": "sha512-RGXINY1YvduBlGrP+vHjJqd/nK7JVpfM4rmZLGMx77WoL3sMrhheA0qxii9VNn1VHnxJLEyxmvCB+Wqc+x/FMw==", + "requires": { + "core-js-pure": "^3.0.0", + "regenerator-runtime": "^0.13.4" + } + }, + "@babel/template": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.12.13.tgz", + "integrity": "sha512-/7xxiGA57xMo/P2GVvdEumr8ONhFOhfgq2ihK3h1e6THqzTAkHbkXgB0xI9yeTfIUoH3+oAeHhqm/I43OTbbjA==", + "requires": { + "@babel/code-frame": "^7.12.13", + "@babel/parser": "^7.12.13", + "@babel/types": "^7.12.13" + } + }, + "@babel/traverse": { + "version": "7.13.17", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.13.17.tgz", + "integrity": "sha512-BMnZn0R+X6ayqm3C3To7o1j7Q020gWdqdyP50KEoVqaCO2c/Im7sYZSmVgvefp8TTMQ+9CtwuBp0Z1CZ8V3Pvg==", + "requires": { + "@babel/code-frame": "^7.12.13", + "@babel/generator": "^7.13.16", + "@babel/helper-function-name": "^7.12.13", + "@babel/helper-split-export-declaration": "^7.12.13", + "@babel/parser": "^7.13.16", + "@babel/types": "^7.13.17", + "debug": "^4.1.0", + "globals": "^11.1.0" + } + }, + "@babel/types": { + "version": "7.13.17", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.13.17.tgz", + "integrity": "sha512-RawydLgxbOPDlTLJNtoIypwdmAy//uQIzlKt2+iBiJaRlVuI6QLUxVAyWGNfOzp8Yu4L4lLIacoCyTNtpb4wiA==", + "requires": { + "@babel/helper-validator-identifier": "^7.12.11", + "to-fast-properties": "^2.0.0" + } + }, + "@bcoe/v8-coverage": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", + "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==" + }, + "@cnakazawa/watch": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@cnakazawa/watch/-/watch-1.0.4.tgz", + "integrity": "sha512-v9kIhKwjeZThiWrLmj0y17CWoyddASLj9O2yvbZkbvw/N3rWOYy9zkV66ursAoVr0mV15bL8g0c4QZUE6cdDoQ==", + "requires": { + "exec-sh": "^0.3.2", + "minimist": "^1.2.0" + } + }, + "@commitlint/execute-rule": { + "version": "12.1.1", + "resolved": "https://registry.npmjs.org/@commitlint/execute-rule/-/execute-rule-12.1.1.tgz", + "integrity": "sha512-6mplMGvLCKF5LieL7BRhydpg32tm6LICnWQADrWU4S5g9PKi2utNvhiaiuNPoHUXr29RdbNaGNcyyPv8DSjJsQ==", + "dev": true, + "optional": true + }, + "@commitlint/load": { + "version": "12.1.1", + "resolved": "https://registry.npmjs.org/@commitlint/load/-/load-12.1.1.tgz", + "integrity": "sha512-qOQtgNdJRULUQWP9jkpTwhj7aEtnqUtqeUpbQ9rjS+GIUST65HZbteNUX4S0mAEGPWqy2aK5xGd73cUfFSvuuw==", + "dev": true, + "optional": true, + "requires": { + "@commitlint/execute-rule": "^12.1.1", + "@commitlint/resolve-extends": "^12.1.1", + "@commitlint/types": "^12.1.1", + "chalk": "^4.0.0", + "cosmiconfig": "^7.0.0", + "lodash": "^4.17.19", + "resolve-from": "^5.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "optional": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", + "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", + "dev": true, + "optional": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "optional": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "optional": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "optional": true + }, + "resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "optional": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "optional": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "@commitlint/resolve-extends": { + "version": "12.1.1", + "resolved": "https://registry.npmjs.org/@commitlint/resolve-extends/-/resolve-extends-12.1.1.tgz", + "integrity": "sha512-/DXRt0S0U3o9lq5cc8OL1Lkx0IjW0HcDWjUkUXshAajBIKBYSJB8x/loNCi1krNEJ8SwLXUEFt5OLxNO6wE9yQ==", + "dev": true, + "optional": true, + "requires": { + "import-fresh": "^3.0.0", + "lodash": "^4.17.19", + "resolve-from": "^5.0.0", + "resolve-global": "^1.0.0" + }, + "dependencies": { + "resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "optional": true + } + } + }, + "@commitlint/types": { + "version": "12.1.1", + "resolved": "https://registry.npmjs.org/@commitlint/types/-/types-12.1.1.tgz", + "integrity": "sha512-+qGH+s2Lo6qwacV2X3/ZypZwaAI84ift+1HBjXdXtI/q0F5NtmXucV3lcQOTviMTNiJhq4qWON2fjci2NItASw==", + "dev": true, + "optional": true, + "requires": { + "chalk": "^4.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "optional": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", + "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", + "dev": true, + "optional": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "optional": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "optional": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "optional": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "optional": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "@csstools/convert-colors": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@csstools/convert-colors/-/convert-colors-1.4.0.tgz", + "integrity": "sha512-5a6wqoJV/xEdbRNKVo6I4hO3VjyDq//8q2f9I6PBAvMesJHFauXDorcNCsr9RzvsZnaWi5NYCcfyqP1QeFHFbw==" + }, + "@csstools/normalize.css": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/@csstools/normalize.css/-/normalize.css-10.1.0.tgz", + "integrity": "sha512-ij4wRiunFfaJxjB0BdrYHIH8FxBJpOwNPhhAcunlmPdXudL1WQV1qoP9un6JsEBAgQH+7UXyyjh0g7jTxXK6tg==" + }, + "@emotion/cache": { + "version": "11.7.1", + "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.7.1.tgz", + "integrity": "sha512-r65Zy4Iljb8oyjtLeCuBH8Qjiy107dOYC6SJq7g7GV5UCQWMObY4SJDPGFjiiVpPrOJ2hmJOoBiYTC7hwx9E2A==", + "requires": { + "@emotion/memoize": "^0.7.4", + "@emotion/sheet": "^1.1.0", + "@emotion/utils": "^1.0.0", + "@emotion/weak-memoize": "^0.2.5", + "stylis": "4.0.13" + } + }, + "@emotion/hash": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.8.0.tgz", + "integrity": "sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow==" + }, + "@emotion/memoize": { + "version": "0.7.5", + "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.7.5.tgz", + "integrity": "sha512-igX9a37DR2ZPGYtV6suZ6whr8pTFtyHL3K/oLUotxpSVO2ASaprmAe2Dkq7tBo7CRY7MMDrAa9nuQP9/YG8FxQ==" + }, + "@emotion/react": { + "version": "11.7.1", + "resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.7.1.tgz", + "integrity": "sha512-DV2Xe3yhkF1yT4uAUoJcYL1AmrnO5SVsdfvu+fBuS7IbByDeTVx9+wFmvx9Idzv7/78+9Mgx2Hcmr7Fex3tIyw==", + "requires": { + "@babel/runtime": "^7.13.10", + "@emotion/cache": "^11.7.1", + "@emotion/serialize": "^1.0.2", + "@emotion/sheet": "^1.1.0", + "@emotion/utils": "^1.0.0", + "@emotion/weak-memoize": "^0.2.5", + "hoist-non-react-statics": "^3.3.1" + } + }, + "@emotion/serialize": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.0.2.tgz", + "integrity": "sha512-95MgNJ9+/ajxU7QIAruiOAdYNjxZX7G2mhgrtDWswA21VviYIRP1R5QilZ/bDY42xiKsaktP4egJb3QdYQZi1A==", + "requires": { + "@emotion/hash": "^0.8.0", + "@emotion/memoize": "^0.7.4", + "@emotion/unitless": "^0.7.5", + "@emotion/utils": "^1.0.0", + "csstype": "^3.0.2" + } + }, + "@emotion/sheet": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.1.0.tgz", + "integrity": "sha512-u0AX4aSo25sMAygCuQTzS+HsImZFuS8llY8O7b9MDRzbJM0kVJlAz6KNDqcG7pOuQZJmj/8X/rAW+66kMnMW+g==" + }, + "@emotion/unitless": { + "version": "0.7.5", + "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.7.5.tgz", + "integrity": "sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg==" + }, + "@emotion/utils": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.0.0.tgz", + "integrity": "sha512-mQC2b3XLDs6QCW+pDQDiyO/EdGZYOygE8s5N5rrzjSI4M3IejPE/JPndCBwRT9z982aqQNi6beWs1UeayrQxxA==" + }, + "@emotion/weak-memoize": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.2.5.tgz", + "integrity": "sha512-6U71C2Wp7r5XtFtQzYrW5iKFT67OixrSxjI4MptCHzdSVlgabczzqLe0ZSgnub/5Kp4hSbpDB1tMytZY9pwxxA==" + }, + "@eslint/eslintrc": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.0.tgz", + "integrity": "sha512-2ZPCc+uNbjV5ERJr+aKSPRwZgKd2z11x0EgLvb1PURmUrn9QNRXFqje0Ldq454PfAVyaJYyrDvvIKSFP4NnBog==", + "requires": { + "ajv": "^6.12.4", + "debug": "^4.1.1", + "espree": "^7.3.0", + "globals": "^12.1.0", + "ignore": "^4.0.6", + "import-fresh": "^3.2.1", + "js-yaml": "^3.13.1", + "minimatch": "^3.0.4", + "strip-json-comments": "^3.1.1" + }, + "dependencies": { + "globals": { + "version": "12.4.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-12.4.0.tgz", + "integrity": "sha512-BWICuzzDvDoH54NHKCseDanAhE3CeDorgDL5MT6LMXXj2WCnd9UC2szdk4AWLfjdgNBCXLUanXYcpBBKOSWGwg==", + "requires": { + "type-fest": "^0.8.1" + } + }, + "ignore": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", + "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==" + } + } + }, + "@fortawesome/fontawesome-common-types": { + "version": "0.2.36", + "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-0.2.36.tgz", + "integrity": "sha512-a/7BiSgobHAgBWeN7N0w+lAhInrGxksn13uK7231n2m8EDPE3BMCl9NZLTGrj9ZXfCmC6LM0QLqXidIizVQ6yg==" + }, + "@fortawesome/fontawesome-svg-core": { + "version": "1.2.36", + "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-1.2.36.tgz", + "integrity": "sha512-YUcsLQKYb6DmaJjIHdDWpBIGCcyE/W+p/LMGvjQem55Mm2XWVAP5kWTMKWLv9lwpCVjpLxPyOMOyUocP1GxrtA==", + "requires": { + "@fortawesome/fontawesome-common-types": "^0.2.36" + } + }, + "@fortawesome/free-solid-svg-icons": { + "version": "5.15.4", + "resolved": "https://registry.npmjs.org/@fortawesome/free-solid-svg-icons/-/free-solid-svg-icons-5.15.4.tgz", + "integrity": "sha512-JLmQfz6tdtwxoihXLg6lT78BorrFyCf59SAwBM6qV/0zXyVeDygJVb3fk+j5Qat+Yvcxp1buLTY5iDh1ZSAQ8w==", + "requires": { + "@fortawesome/fontawesome-common-types": "^0.2.36" + } + }, + "@fortawesome/react-fontawesome": { + "version": "0.1.15", + "resolved": "https://registry.npmjs.org/@fortawesome/react-fontawesome/-/react-fontawesome-0.1.15.tgz", + "integrity": "sha512-/HFHdcoLESxxMkqZAcZ6RXDJ69pVApwdwRos/B2kiMWxDSAX2dFK8Er2/+rG+RsrzWB/dsAyjefLmemgmfE18g==", + "requires": { + "prop-types": "^15.7.2" + } + }, + "@hapi/address": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@hapi/address/-/address-2.1.4.tgz", + "integrity": "sha512-QD1PhQk+s31P1ixsX0H0Suoupp3VMXzIVMSwobR3F3MSUO2YCV0B7xqLcUw/Bh8yuvd3LhpyqLQWTNcRmp6IdQ==" + }, + "@hapi/bourne": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@hapi/bourne/-/bourne-1.3.2.tgz", + "integrity": "sha512-1dVNHT76Uu5N3eJNTYcvxee+jzX4Z9lfciqRRHCU27ihbUcYi+iSc2iml5Ke1LXe1SyJCLA0+14Jh4tXJgOppA==" + }, + "@hapi/hoek": { + "version": "8.5.1", + "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-8.5.1.tgz", + "integrity": "sha512-yN7kbciD87WzLGc5539Tn0sApjyiGHAJgKvG9W8C7O+6c7qmoQMfVs0W4bX17eqz6C78QJqqFrtgdK5EWf6Qow==" + }, + "@hapi/joi": { + "version": "15.1.1", + "resolved": "https://registry.npmjs.org/@hapi/joi/-/joi-15.1.1.tgz", + "integrity": "sha512-entf8ZMOK8sc+8YfeOlM8pCfg3b5+WZIKBfUaaJT8UsjAAPjartzxIYm3TIbjvA4u+u++KbcXD38k682nVHDAQ==", + "requires": { + "@hapi/address": "2.x.x", + "@hapi/bourne": "1.x.x", + "@hapi/hoek": "8.x.x", + "@hapi/topo": "3.x.x" + } + }, + "@hapi/topo": { + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/@hapi/topo/-/topo-3.1.6.tgz", + "integrity": "sha512-tAag0jEcjwH+P2quUfipd7liWCNX2F8NvYjQp2wtInsZxnMlypdw0FtAOLxtvvkO+GSRRbmNi8m/5y42PQJYCQ==", + "requires": { + "@hapi/hoek": "^8.3.0" + } + }, + "@hypnosphi/create-react-context": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@hypnosphi/create-react-context/-/create-react-context-0.3.1.tgz", + "integrity": "sha512-V1klUed202XahrWJLLOT3EXNeCpFHCcJntdFGI15ntCwau+jfT386w7OFTMaCqOgXUH1fa0w/I1oZs+i/Rfr0A==", + "requires": { + "gud": "^1.0.0", + "warning": "^4.0.3" + } + }, + "@istanbuljs/load-nyc-config": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", + "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", + "requires": { + "camelcase": "^5.3.1", + "find-up": "^4.1.0", + "get-package-type": "^0.1.0", + "js-yaml": "^3.13.1", + "resolve-from": "^5.0.0" + }, + "dependencies": { + "camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==" + }, + "resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==" + } + } + }, + "@istanbuljs/schema": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", + "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==" + }, + "@jest/console": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-26.6.2.tgz", + "integrity": "sha512-IY1R2i2aLsLr7Id3S6p2BA82GNWryt4oSvEXLAKc+L2zdi89dSkE8xC1C+0kpATG4JhBJREnQOH7/zmccM2B0g==", + "requires": { + "@jest/types": "^26.6.2", + "@types/node": "*", + "chalk": "^4.0.0", + "jest-message-util": "^26.6.2", + "jest-util": "^26.6.2", + "slash": "^3.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", + "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "@jest/core": { + "version": "26.6.3", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-26.6.3.tgz", + "integrity": "sha512-xvV1kKbhfUqFVuZ8Cyo+JPpipAHHAV3kcDBftiduK8EICXmTFddryy3P7NfZt8Pv37rA9nEJBKCCkglCPt/Xjw==", + "requires": { + "@jest/console": "^26.6.2", + "@jest/reporters": "^26.6.2", + "@jest/test-result": "^26.6.2", + "@jest/transform": "^26.6.2", + "@jest/types": "^26.6.2", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.4", + "jest-changed-files": "^26.6.2", + "jest-config": "^26.6.3", + "jest-haste-map": "^26.6.2", + "jest-message-util": "^26.6.2", + "jest-regex-util": "^26.0.0", + "jest-resolve": "^26.6.2", + "jest-resolve-dependencies": "^26.6.3", + "jest-runner": "^26.6.3", + "jest-runtime": "^26.6.3", + "jest-snapshot": "^26.6.2", + "jest-util": "^26.6.2", + "jest-validate": "^26.6.2", + "jest-watcher": "^26.6.2", + "micromatch": "^4.0.2", + "p-each-series": "^2.1.0", + "rimraf": "^3.0.0", + "slash": "^3.0.0", + "strip-ansi": "^6.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", + "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" + }, + "jest-resolve": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-26.6.2.tgz", + "integrity": "sha512-sOxsZOq25mT1wRsfHcbtkInS+Ek7Q8jCHUB0ZUTP0tc/c41QHriU/NunqMfCUWsL4H3MHpvQD4QR9kSYhS7UvQ==", + "requires": { + "@jest/types": "^26.6.2", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.4", + "jest-pnp-resolver": "^1.2.2", + "jest-util": "^26.6.2", + "read-pkg-up": "^7.0.1", + "resolve": "^1.18.1", + "slash": "^3.0.0" + } + }, + "read-pkg": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", + "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", + "requires": { + "@types/normalize-package-data": "^2.4.0", + "normalize-package-data": "^2.5.0", + "parse-json": "^5.0.0", + "type-fest": "^0.6.0" + }, + "dependencies": { + "type-fest": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", + "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==" + } + } + }, + "read-pkg-up": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz", + "integrity": "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==", + "requires": { + "find-up": "^4.1.0", + "read-pkg": "^5.2.0", + "type-fest": "^0.8.1" + } + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "@jest/environment": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-26.6.2.tgz", + "integrity": "sha512-nFy+fHl28zUrRsCeMB61VDThV1pVTtlEokBRgqPrcT1JNq4yRNIyTHfyht6PqtUvY9IsuLGTrbG8kPXjSZIZwA==", + "requires": { + "@jest/fake-timers": "^26.6.2", + "@jest/types": "^26.6.2", + "@types/node": "*", + "jest-mock": "^26.6.2" + } + }, + "@jest/fake-timers": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-26.6.2.tgz", + "integrity": "sha512-14Uleatt7jdzefLPYM3KLcnUl1ZNikaKq34enpb5XG9i81JpppDb5muZvonvKyrl7ftEHkKS5L5/eB/kxJ+bvA==", + "requires": { + "@jest/types": "^26.6.2", + "@sinonjs/fake-timers": "^6.0.1", + "@types/node": "*", + "jest-message-util": "^26.6.2", + "jest-mock": "^26.6.2", + "jest-util": "^26.6.2" + } + }, + "@jest/globals": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-26.6.2.tgz", + "integrity": "sha512-85Ltnm7HlB/KesBUuALwQ68YTU72w9H2xW9FjZ1eL1U3lhtefjjl5c2MiUbpXt/i6LaPRvoOFJ22yCBSfQ0JIA==", + "requires": { + "@jest/environment": "^26.6.2", + "@jest/types": "^26.6.2", + "expect": "^26.6.2" + } + }, + "@jest/reporters": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-26.6.2.tgz", + "integrity": "sha512-h2bW53APG4HvkOnVMo8q3QXa6pcaNt1HkwVsOPMBV6LD/q9oSpxNSYZQYkAnjdMjrJ86UuYeLo+aEZClV6opnw==", + "requires": { + "@bcoe/v8-coverage": "^0.2.3", + "@jest/console": "^26.6.2", + "@jest/test-result": "^26.6.2", + "@jest/transform": "^26.6.2", + "@jest/types": "^26.6.2", + "chalk": "^4.0.0", + "collect-v8-coverage": "^1.0.0", + "exit": "^0.1.2", + "glob": "^7.1.2", + "graceful-fs": "^4.2.4", + "istanbul-lib-coverage": "^3.0.0", + "istanbul-lib-instrument": "^4.0.3", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^4.0.0", + "istanbul-reports": "^3.0.2", + "jest-haste-map": "^26.6.2", + "jest-resolve": "^26.6.2", + "jest-util": "^26.6.2", + "jest-worker": "^26.6.2", + "node-notifier": "^8.0.0", + "slash": "^3.0.0", + "source-map": "^0.6.0", + "string-length": "^4.0.1", + "terminal-link": "^2.0.0", + "v8-to-istanbul": "^7.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", + "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" + }, + "jest-resolve": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-26.6.2.tgz", + "integrity": "sha512-sOxsZOq25mT1wRsfHcbtkInS+Ek7Q8jCHUB0ZUTP0tc/c41QHriU/NunqMfCUWsL4H3MHpvQD4QR9kSYhS7UvQ==", + "requires": { + "@jest/types": "^26.6.2", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.4", + "jest-pnp-resolver": "^1.2.2", + "jest-util": "^26.6.2", + "read-pkg-up": "^7.0.1", + "resolve": "^1.18.1", + "slash": "^3.0.0" + } + }, + "read-pkg": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", + "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", + "requires": { + "@types/normalize-package-data": "^2.4.0", + "normalize-package-data": "^2.5.0", + "parse-json": "^5.0.0", + "type-fest": "^0.6.0" + }, + "dependencies": { + "type-fest": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", + "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==" + } + } + }, + "read-pkg-up": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz", + "integrity": "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==", + "requires": { + "find-up": "^4.1.0", + "read-pkg": "^5.2.0", + "type-fest": "^0.8.1" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "@jest/source-map": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-26.6.2.tgz", + "integrity": "sha512-YwYcCwAnNmOVsZ8mr3GfnzdXDAl4LaenZP5z+G0c8bzC9/dugL8zRmxZzdoTl4IaS3CryS1uWnROLPFmb6lVvA==", + "requires": { + "callsites": "^3.0.0", + "graceful-fs": "^4.2.4", + "source-map": "^0.6.0" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + } + } + }, + "@jest/test-result": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-26.6.2.tgz", + "integrity": "sha512-5O7H5c/7YlojphYNrK02LlDIV2GNPYisKwHm2QTKjNZeEzezCbwYs9swJySv2UfPMyZ0VdsmMv7jIlD/IKYQpQ==", + "requires": { + "@jest/console": "^26.6.2", + "@jest/types": "^26.6.2", + "@types/istanbul-lib-coverage": "^2.0.0", + "collect-v8-coverage": "^1.0.0" + } + }, + "@jest/test-sequencer": { + "version": "26.6.3", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-26.6.3.tgz", + "integrity": "sha512-YHlVIjP5nfEyjlrSr8t/YdNfU/1XEt7c5b4OxcXCjyRhjzLYu/rO69/WHPuYcbCWkz8kAeZVZp2N2+IOLLEPGw==", + "requires": { + "@jest/test-result": "^26.6.2", + "graceful-fs": "^4.2.4", + "jest-haste-map": "^26.6.2", + "jest-runner": "^26.6.3", + "jest-runtime": "^26.6.3" + } + }, + "@jest/transform": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-26.6.2.tgz", + "integrity": "sha512-E9JjhUgNzvuQ+vVAL21vlyfy12gP0GhazGgJC4h6qUt1jSdUXGWJ1wfu/X7Sd8etSgxV4ovT1pb9v5D6QW4XgA==", + "requires": { + "@babel/core": "^7.1.0", + "@jest/types": "^26.6.2", + "babel-plugin-istanbul": "^6.0.0", + "chalk": "^4.0.0", + "convert-source-map": "^1.4.0", + "fast-json-stable-stringify": "^2.0.0", + "graceful-fs": "^4.2.4", + "jest-haste-map": "^26.6.2", + "jest-regex-util": "^26.0.0", + "jest-util": "^26.6.2", + "micromatch": "^4.0.2", + "pirates": "^4.0.1", + "slash": "^3.0.0", + "source-map": "^0.6.1", + "write-file-atomic": "^3.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", + "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "@jest/types": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz", + "integrity": "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==", + "requires": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^15.0.0", + "chalk": "^4.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", + "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "@nodelib/fs.scandir": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.4.tgz", + "integrity": "sha512-33g3pMJk3bg5nXbL/+CY6I2eJDzZAni49PfJnL5fghPTggPvBd/pFNSgJsdAgWptuFu7qq/ERvOYFlhvsLTCKA==", + "requires": { + "@nodelib/fs.stat": "2.0.4", + "run-parallel": "^1.1.9" + } + }, + "@nodelib/fs.stat": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.4.tgz", + "integrity": "sha512-IYlHJA0clt2+Vg7bccq+TzRdJvv19c2INqBSsoOLp1je7xjtr7J26+WXR72MCdvU9q1qTzIWDfhMf+DRvQJK4Q==" + }, + "@nodelib/fs.walk": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.6.tgz", + "integrity": "sha512-8Broas6vTtW4GIXTAHDoE32hnN2M5ykgCpWGbuXHQ15vEMqr23pB76e/GZcYsZCHALv50ktd24qhEyKr6wBtow==", + "requires": { + "@nodelib/fs.scandir": "2.1.4", + "fastq": "^1.6.0" + } + }, + "@npmcli/move-file": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@npmcli/move-file/-/move-file-1.1.2.tgz", + "integrity": "sha512-1SUf/Cg2GzGDyaf15aR9St9TWlb+XvbZXWpDx8YKs7MLzMH/BCeopv+y9vzrzgkfykCGuWOlSu3mZhj2+FQcrg==", + "requires": { + "mkdirp": "^1.0.4", + "rimraf": "^3.0.2" + }, + "dependencies": { + "mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==" + } + } + }, + "@pmmmwh/react-refresh-webpack-plugin": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@pmmmwh/react-refresh-webpack-plugin/-/react-refresh-webpack-plugin-0.4.3.tgz", + "integrity": "sha512-br5Qwvh8D2OQqSXpd1g/xqXKnK0r+Jz6qVKBbWmpUcrbGOxUrf39V5oZ1876084CGn18uMdR5uvPqBv9UqtBjQ==", + "requires": { + "ansi-html": "^0.0.7", + "error-stack-parser": "^2.0.6", + "html-entities": "^1.2.1", + "native-url": "^0.2.6", + "schema-utils": "^2.6.5", + "source-map": "^0.7.3" + }, + "dependencies": { + "source-map": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", + "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==" + } + } + }, + "@rollup/plugin-node-resolve": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-7.1.3.tgz", + "integrity": "sha512-RxtSL3XmdTAE2byxekYLnx+98kEUOrPHF/KRVjLH+DEIHy6kjIw7YINQzn+NXiH/NTrQLAwYs0GWB+csWygA9Q==", + "requires": { + "@rollup/pluginutils": "^3.0.8", + "@types/resolve": "0.0.8", + "builtin-modules": "^3.1.0", + "is-module": "^1.0.0", + "resolve": "^1.14.2" + } + }, + "@rollup/plugin-replace": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/@rollup/plugin-replace/-/plugin-replace-2.4.2.tgz", + "integrity": "sha512-IGcu+cydlUMZ5En85jxHH4qj2hta/11BHq95iHEyb2sbgiN0eCdzvUcHw5gt9pBL5lTi4JDYJ1acCoMGpTvEZg==", + "requires": { + "@rollup/pluginutils": "^3.1.0", + "magic-string": "^0.25.7" + } + }, + "@rollup/pluginutils": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-3.1.0.tgz", + "integrity": "sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg==", + "requires": { + "@types/estree": "0.0.39", + "estree-walker": "^1.0.1", + "picomatch": "^2.2.2" + }, + "dependencies": { + "@types/estree": { + "version": "0.0.39", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.39.tgz", + "integrity": "sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==" + } + } + }, + "@rooks/use-outside-click": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/@rooks/use-outside-click/-/use-outside-click-3.6.0.tgz", + "integrity": "sha512-DDxdcD9bDDArV2tBmh5okaJNee/7EWaC5DsPrjTxIhhvXPpUatizcn2qYLcvX7y1vYpd64Wyqvkb87E6fsIfEQ==" + }, + "@sheerun/mutationobserver-shim": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@sheerun/mutationobserver-shim/-/mutationobserver-shim-0.3.3.tgz", + "integrity": "sha512-DetpxZw1fzPD5xUBrIAoplLChO2VB8DlL5Gg+I1IR9b2wPqYIca2WSUxL5g1vLeR4MsQq1NeWriXAVffV+U1Fw==", + "dev": true + }, + "@sinonjs/commons": { + "version": "1.8.3", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.3.tgz", + "integrity": "sha512-xkNcLAn/wZaX14RPlwizcKicDk9G3F8m2nU3L7Ukm5zBgTwiT0wsoFAHx9Jq56fJA1z/7uKGtCRu16sOUCLIHQ==", + "requires": { + "type-detect": "4.0.8" + } + }, + "@sinonjs/fake-timers": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-6.0.1.tgz", + "integrity": "sha512-MZPUxrmFubI36XS1DI3qmI0YdN1gks62JtFZvxR67ljjSNCeK6U08Zx4msEWOXuofgqUt6zPHSi1H9fbjR/NRA==", + "requires": { + "@sinonjs/commons": "^1.7.0" + } + }, + "@surma/rollup-plugin-off-main-thread": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/@surma/rollup-plugin-off-main-thread/-/rollup-plugin-off-main-thread-1.4.2.tgz", + "integrity": "sha512-yBMPqmd1yEJo/280PAMkychuaALyQ9Lkb5q1ck3mjJrFuEobIfhnQ4J3mbvBoISmR3SWMWV+cGB/I0lCQee79A==", + "requires": { + "ejs": "^2.6.1", + "magic-string": "^0.25.0" + } + }, + "@svgr/babel-plugin-add-jsx-attribute": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-5.4.0.tgz", + "integrity": "sha512-ZFf2gs/8/6B8PnSofI0inYXr2SDNTDScPXhN7k5EqD4aZ3gi6u+rbmZHVB8IM3wDyx8ntKACZbtXSm7oZGRqVg==" + }, + "@svgr/babel-plugin-remove-jsx-attribute": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-attribute/-/babel-plugin-remove-jsx-attribute-5.4.0.tgz", + "integrity": "sha512-yaS4o2PgUtwLFGTKbsiAy6D0o3ugcUhWK0Z45umJ66EPWunAz9fuFw2gJuje6wqQvQWOTJvIahUwndOXb7QCPg==" + }, + "@svgr/babel-plugin-remove-jsx-empty-expression": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-empty-expression/-/babel-plugin-remove-jsx-empty-expression-5.0.1.tgz", + "integrity": "sha512-LA72+88A11ND/yFIMzyuLRSMJ+tRKeYKeQ+mR3DcAZ5I4h5CPWN9AHyUzJbWSYp/u2u0xhmgOe0+E41+GjEueA==" + }, + "@svgr/babel-plugin-replace-jsx-attribute-value": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-replace-jsx-attribute-value/-/babel-plugin-replace-jsx-attribute-value-5.0.1.tgz", + "integrity": "sha512-PoiE6ZD2Eiy5mK+fjHqwGOS+IXX0wq/YDtNyIgOrc6ejFnxN4b13pRpiIPbtPwHEc+NT2KCjteAcq33/F1Y9KQ==" + }, + "@svgr/babel-plugin-svg-dynamic-title": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-dynamic-title/-/babel-plugin-svg-dynamic-title-5.4.0.tgz", + "integrity": "sha512-zSOZH8PdZOpuG1ZVx/cLVePB2ibo3WPpqo7gFIjLV9a0QsuQAzJiwwqmuEdTaW2pegyBE17Uu15mOgOcgabQZg==" + }, + "@svgr/babel-plugin-svg-em-dimensions": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-em-dimensions/-/babel-plugin-svg-em-dimensions-5.4.0.tgz", + "integrity": "sha512-cPzDbDA5oT/sPXDCUYoVXEmm3VIoAWAPT6mSPTJNbQaBNUuEKVKyGH93oDY4e42PYHRW67N5alJx/eEol20abw==" + }, + "@svgr/babel-plugin-transform-react-native-svg": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-react-native-svg/-/babel-plugin-transform-react-native-svg-5.4.0.tgz", + "integrity": "sha512-3eYP/SaopZ41GHwXma7Rmxcv9uRslRDTY1estspeB1w1ueZWd/tPlMfEOoccYpEMZU3jD4OU7YitnXcF5hLW2Q==" + }, + "@svgr/babel-plugin-transform-svg-component": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-svg-component/-/babel-plugin-transform-svg-component-5.5.0.tgz", + "integrity": "sha512-q4jSH1UUvbrsOtlo/tKcgSeiCHRSBdXoIoqX1pgcKK/aU3JD27wmMKwGtpB8qRYUYoyXvfGxUVKchLuR5pB3rQ==" + }, + "@svgr/babel-preset": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-preset/-/babel-preset-5.5.0.tgz", + "integrity": "sha512-4FiXBjvQ+z2j7yASeGPEi8VD/5rrGQk4Xrq3EdJmoZgz/tpqChpo5hgXDvmEauwtvOc52q8ghhZK4Oy7qph4ig==", + "requires": { + "@svgr/babel-plugin-add-jsx-attribute": "^5.4.0", + "@svgr/babel-plugin-remove-jsx-attribute": "^5.4.0", + "@svgr/babel-plugin-remove-jsx-empty-expression": "^5.0.1", + "@svgr/babel-plugin-replace-jsx-attribute-value": "^5.0.1", + "@svgr/babel-plugin-svg-dynamic-title": "^5.4.0", + "@svgr/babel-plugin-svg-em-dimensions": "^5.4.0", + "@svgr/babel-plugin-transform-react-native-svg": "^5.4.0", + "@svgr/babel-plugin-transform-svg-component": "^5.5.0" + } + }, + "@svgr/core": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@svgr/core/-/core-5.5.0.tgz", + "integrity": "sha512-q52VOcsJPvV3jO1wkPtzTuKlvX7Y3xIcWRpCMtBF3MrteZJtBfQw/+u0B1BHy5ColpQc1/YVTrPEtSYIMNZlrQ==", + "requires": { + "@svgr/plugin-jsx": "^5.5.0", + "camelcase": "^6.2.0", + "cosmiconfig": "^7.0.0" + } + }, + "@svgr/hast-util-to-babel-ast": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@svgr/hast-util-to-babel-ast/-/hast-util-to-babel-ast-5.5.0.tgz", + "integrity": "sha512-cAaR/CAiZRB8GP32N+1jocovUtvlj0+e65TB50/6Lcime+EA49m/8l+P2ko+XPJ4dw3xaPS3jOL4F2X4KWxoeQ==", + "requires": { + "@babel/types": "^7.12.6" + } + }, + "@svgr/plugin-jsx": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@svgr/plugin-jsx/-/plugin-jsx-5.5.0.tgz", + "integrity": "sha512-V/wVh33j12hGh05IDg8GpIUXbjAPnTdPTKuP4VNLggnwaHMPNQNae2pRnyTAILWCQdz5GyMqtO488g7CKM8CBA==", + "requires": { + "@babel/core": "^7.12.3", + "@svgr/babel-preset": "^5.5.0", + "@svgr/hast-util-to-babel-ast": "^5.5.0", + "svg-parser": "^2.0.2" + } + }, + "@svgr/plugin-svgo": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@svgr/plugin-svgo/-/plugin-svgo-5.5.0.tgz", + "integrity": "sha512-r5swKk46GuQl4RrVejVwpeeJaydoxkdwkM1mBKOgJLBUJPGaLci6ylg/IjhrRsREKDkr4kbMWdgOtbXEh0fyLQ==", + "requires": { + "cosmiconfig": "^7.0.0", + "deepmerge": "^4.2.2", + "svgo": "^1.2.2" + } + }, + "@svgr/webpack": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@svgr/webpack/-/webpack-5.5.0.tgz", + "integrity": "sha512-DOBOK255wfQxguUta2INKkzPj6AIS6iafZYiYmHn6W3pHlycSRRlvWKCfLDG10fXfLWqE3DJHgRUOyJYmARa7g==", + "requires": { + "@babel/core": "^7.12.3", + "@babel/plugin-transform-react-constant-elements": "^7.12.1", + "@babel/preset-env": "^7.12.1", + "@babel/preset-react": "^7.12.5", + "@svgr/core": "^5.5.0", + "@svgr/plugin-jsx": "^5.5.0", + "@svgr/plugin-svgo": "^5.5.0", + "loader-utils": "^2.0.0" + } + }, + "@testing-library/dom": { + "version": "6.16.0", + "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-6.16.0.tgz", + "integrity": "sha512-lBD88ssxqEfz0wFL6MeUyyWZfV/2cjEZZV3YRpb2IoJRej/4f1jB0TzqIOznTpfR1r34CNesrubxwIlAQ8zgPA==", + "dev": true, + "requires": { + "@babel/runtime": "^7.8.4", + "@sheerun/mutationobserver-shim": "^0.3.2", + "@types/testing-library__dom": "^6.12.1", + "aria-query": "^4.0.2", + "dom-accessibility-api": "^0.3.0", + "pretty-format": "^25.1.0", + "wait-for-expect": "^3.0.2" + }, + "dependencies": { + "@jest/types": { + "version": "25.5.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-25.5.0.tgz", + "integrity": "sha512-OXD0RgQ86Tu3MazKo8bnrkDRaDXXMGUqd+kTtLtK1Zb7CRzQcaSRPPPV37SvYTdevXEBVxe0HXylEjs8ibkmCw==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^1.1.1", + "@types/yargs": "^15.0.0", + "chalk": "^3.0.0" + } + }, + "@types/istanbul-reports": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-1.1.2.tgz", + "integrity": "sha512-P/W9yOX/3oPZSpaYOCQzGqgCQRXn0FFO/V8bWrCQs+wLmvVVxk6CRBXALEvNs9OHIatlnlFokfhuDo2ug01ciw==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "*", + "@types/istanbul-lib-report": "*" + } + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", + "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "pretty-format": { + "version": "25.5.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-25.5.0.tgz", + "integrity": "sha512-kbo/kq2LQ/A/is0PQwsEHM7Ca6//bGPPvU6UnsdDRSKTWxT/ru/xb88v4BJf6a69H+uTytOEsTusT9ksd/1iWQ==", + "dev": true, + "requires": { + "@jest/types": "^25.5.0", + "ansi-regex": "^5.0.0", + "ansi-styles": "^4.0.0", + "react-is": "^16.12.0" + } + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "@testing-library/jest-dom": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-4.2.4.tgz", + "integrity": "sha512-j31Bn0rQo12fhCWOUWy9fl7wtqkp7In/YP2p5ZFyRuiiB9Qs3g+hS4gAmDWONbAHcRmVooNJ5eOHQDCOmUFXHg==", + "dev": true, + "requires": { + "@babel/runtime": "^7.5.1", + "chalk": "^2.4.1", + "css": "^2.2.3", + "css.escape": "^1.5.1", + "jest-diff": "^24.0.0", + "jest-matcher-utils": "^24.0.0", + "lodash": "^4.17.11", + "pretty-format": "^24.0.0", + "redent": "^3.0.0" + }, + "dependencies": { + "@jest/types": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-24.9.0.tgz", + "integrity": "sha512-XKK7ze1apu5JWQ5eZjHITP66AX+QsLlbaJRBGYr8pNzwcAE2JVkwnf0yqjHTsDRcjR0mujy/NmZMXw5kl+kGBw==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^1.1.1", + "@types/yargs": "^13.0.0" + } + }, + "@types/istanbul-reports": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-1.1.2.tgz", + "integrity": "sha512-P/W9yOX/3oPZSpaYOCQzGqgCQRXn0FFO/V8bWrCQs+wLmvVVxk6CRBXALEvNs9OHIatlnlFokfhuDo2ug01ciw==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "*", + "@types/istanbul-lib-report": "*" + } + }, + "@types/yargs": { + "version": "13.0.11", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-13.0.11.tgz", + "integrity": "sha512-NRqD6T4gktUrDi1o1wLH3EKC1o2caCr7/wR87ODcbVITQF106OM3sFN92ysZ++wqelOd1CTzatnOBRDYYG6wGQ==", + "dev": true, + "requires": { + "@types/yargs-parser": "*" + } + }, + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "dev": true + }, + "diff-sequences": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-24.9.0.tgz", + "integrity": "sha512-Dj6Wk3tWyTE+Fo1rW8v0Xhwk80um6yFYKbuAxc9c3EZxIHFDYwbi34Uk42u1CdnIiVorvt4RmlSDjIPyzGC2ew==", + "dev": true + }, + "jest-diff": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-24.9.0.tgz", + "integrity": "sha512-qMfrTs8AdJE2iqrTp0hzh7kTd2PQWrsFyj9tORoKmu32xjPjeE4NyjVRDz8ybYwqS2ik8N4hsIpiVTyFeo2lBQ==", + "dev": true, + "requires": { + "chalk": "^2.0.1", + "diff-sequences": "^24.9.0", + "jest-get-type": "^24.9.0", + "pretty-format": "^24.9.0" + } + }, + "jest-get-type": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-24.9.0.tgz", + "integrity": "sha512-lUseMzAley4LhIcpSP9Jf+fTrQ4a1yHQwLNeeVa2cEmbCGeoZAtYPOIv8JaxLD/sUpKxetKGP+gsHl8f8TSj8Q==", + "dev": true + }, + "jest-matcher-utils": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-24.9.0.tgz", + "integrity": "sha512-OZz2IXsu6eaiMAwe67c1T+5tUAtQyQx27/EMEkbFAGiw52tB9em+uGbzpcgYVpA8wl0hlxKPZxrly4CXU/GjHA==", + "dev": true, + "requires": { + "chalk": "^2.0.1", + "jest-diff": "^24.9.0", + "jest-get-type": "^24.9.0", + "pretty-format": "^24.9.0" + } + }, + "pretty-format": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-24.9.0.tgz", + "integrity": "sha512-00ZMZUiHaJrNfk33guavqgvfJS30sLYf0f8+Srklv0AMPodGGHcoHgksZ3OThYnIvOd+8yMCn0YiEOogjlgsnA==", + "dev": true, + "requires": { + "@jest/types": "^24.9.0", + "ansi-regex": "^4.0.0", + "ansi-styles": "^3.2.0", + "react-is": "^16.8.4" + } + } + } + }, + "@testing-library/react": { + "version": "9.5.0", + "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-9.5.0.tgz", + "integrity": "sha512-di1b+D0p+rfeboHO5W7gTVeZDIK5+maEgstrZbWZSSvxDyfDRkkyBE1AJR5Psd6doNldluXlCWqXriUfqu/9Qg==", + "dev": true, + "requires": { + "@babel/runtime": "^7.8.4", + "@testing-library/dom": "^6.15.0", + "@types/testing-library__react": "^9.1.2" + } + }, + "@testing-library/user-event": { + "version": "7.2.1", + "resolved": "https://registry.npmjs.org/@testing-library/user-event/-/user-event-7.2.1.tgz", + "integrity": "sha512-oZ0Ib5I4Z2pUEcoo95cT1cr6slco9WY7yiPpG+RGNkj8YcYgJnM7pXmYmorNOReh8MIGcKSqXyeGjxnr8YiZbA==", + "dev": true + }, + "@types/anymatch": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@types/anymatch/-/anymatch-1.3.1.tgz", + "integrity": "sha512-/+CRPXpBDpo2RK9C68N3b2cOvO0Cf5B9aPijHsoDQTHivnGSObdOF2BRQOYjojWTDy6nQvMjmqRXIxH55VjxxA==" + }, + "@types/babel__core": { + "version": "7.1.14", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.14.tgz", + "integrity": "sha512-zGZJzzBUVDo/eV6KgbE0f0ZI7dInEYvo12Rb70uNQDshC3SkRMb67ja0GgRHZgAX3Za6rhaWlvbDO8rrGyAb1g==", + "requires": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "@types/babel__generator": { + "version": "7.6.2", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.2.tgz", + "integrity": "sha512-MdSJnBjl+bdwkLskZ3NGFp9YcXGx5ggLpQQPqtgakVhsWK0hTtNYhjpZLlWQTviGTvF8at+Bvli3jV7faPdgeQ==", + "requires": { + "@babel/types": "^7.0.0" + } + }, + "@types/babel__template": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.0.tgz", + "integrity": "sha512-NTPErx4/FiPCGScH7foPyr+/1Dkzkni+rHiYHHoTjvwou7AQzJkNeD60A9CXRy+ZEN2B1bggmkTMCDb+Mv5k+A==", + "requires": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "@types/babel__traverse": { + "version": "7.11.1", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.11.1.tgz", + "integrity": "sha512-Vs0hm0vPahPMYi9tDjtP66llufgO3ST16WXaSTtDGEl9cewAl3AibmxWw6TINOqHPT9z0uABKAYjT9jNSg4npw==", + "requires": { + "@babel/types": "^7.3.0" + } + }, + "@types/cheerio": { + "version": "0.22.28", + "resolved": "https://registry.npmjs.org/@types/cheerio/-/cheerio-0.22.28.tgz", + "integrity": "sha512-ehUMGSW5IeDxJjbru4awKYMlKGmo1wSSGUVqXtYwlgmUM8X1a0PZttEIm6yEY7vHsY/hh6iPnklF213G0UColw==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/debounce": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@types/debounce/-/debounce-1.2.0.tgz", + "integrity": "sha512-bWG5wapaWgbss9E238T0R6bfo5Fh3OkeoSt245CM7JJwVwpw6MEBCbIxLq5z8KzsE3uJhzcIuQkyiZmzV3M/Dw==", + "dev": true + }, + "@types/enzyme": { + "version": "3.10.8", + "resolved": "https://registry.npmjs.org/@types/enzyme/-/enzyme-3.10.8.tgz", + "integrity": "sha512-vlOuzqsTHxog6PV79+tvOHFb6hq4QZKMq1lLD9MaWD1oec2lHTKndn76XOpSwCA0oFTaIbKVPrgM3k78Jjd16g==", + "dev": true, + "requires": { + "@types/cheerio": "*", + "@types/react": "*" + } + }, + "@types/enzyme-adapter-react-16": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@types/enzyme-adapter-react-16/-/enzyme-adapter-react-16-1.0.6.tgz", + "integrity": "sha512-VonDkZ15jzqDWL8mPFIQnnLtjwebuL9YnDkqeCDYnB4IVgwUm0mwKkqhrxLL6mb05xm7qqa3IE95m8CZE9imCg==", + "dev": true, + "requires": { + "@types/enzyme": "*" + } + }, + "@types/eslint": { + "version": "7.2.10", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-7.2.10.tgz", + "integrity": "sha512-kUEPnMKrqbtpCq/KTaGFFKAcz6Ethm2EjCoKIDaCmfRBWLbFuTcOJfTlorwbnboXBzahqWLgUp1BQeKHiJzPUQ==", + "requires": { + "@types/estree": "*", + "@types/json-schema": "*" + } + }, + "@types/estree": { + "version": "0.0.47", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.47.tgz", + "integrity": "sha512-c5ciR06jK8u9BstrmJyO97m+klJrrhCf9u3rLu3DEAJBirxRqSCvDQoYKmxuYwQI5SZChAWu+tq9oVlGRuzPAg==" + }, + "@types/faker": { + "version": "4.1.12", + "resolved": "https://registry.npmjs.org/@types/faker/-/faker-4.1.12.tgz", + "integrity": "sha512-0MEyzJrLLs1WaOCx9ULK6FzdCSj2EuxdSP9kvuxxdBEGujZYUOZ4vkPXdgu3dhyg/pOdn7VCatelYX7k0YShlA==", + "dev": true + }, + "@types/file-saver": { + "version": "2.0.2", + "resolved": "https://landmarkcu.pkgs.visualstudio.com/_packaging/LCU-Internal/npm/registry/@types/file-saver/-/file-saver-2.0.2.tgz", + "integrity": "sha1-vVk8z67kL/lKXByDv2mum+g0k7k=", + "dev": true + }, + "@types/glob": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.1.3.tgz", + "integrity": "sha512-SEYeGAIQIQX8NN6LDKprLjbrd5dARM5EXsd8GI/A5l0apYI1fGMWgPHSe4ZKL4eozlAyI+doUE9XbYS4xCkQ1w==", + "requires": { + "@types/minimatch": "*", + "@types/node": "*" + } + }, + "@types/graceful-fs": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.5.tgz", + "integrity": "sha512-anKkLmZZ+xm4p8JWBf4hElkM4XR+EZeA2M9BAkkTldmcyDY4mbdIJnRghDJH3Ov5ooY7/UAoENtmdMSkaAd7Cw==", + "requires": { + "@types/node": "*" + } + }, + "@types/history": { + "version": "4.7.8", + "resolved": "https://registry.npmjs.org/@types/history/-/history-4.7.8.tgz", + "integrity": "sha512-S78QIYirQcUoo6UJZx9CSP0O2ix9IaeAXwQi26Rhr/+mg7qqPy8TzaxHSUut7eGjL8WmLccT7/MXf304WjqHcA==" + }, + "@types/hoist-non-react-statics": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz", + "integrity": "sha512-iMIqiko6ooLrTh1joXodJK5X9xeEALT1kM5G3ZLhD3hszxBdIEd5C75U834D9mLcINgD4OyZf5uQXjkuYydWvA==", + "requires": { + "@types/react": "*", + "hoist-non-react-statics": "^3.3.0" + } + }, + "@types/html-minifier-terser": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/@types/html-minifier-terser/-/html-minifier-terser-5.1.1.tgz", + "integrity": "sha512-giAlZwstKbmvMk1OO7WXSj4OZ0keXAcl2TQq4LWHiiPH2ByaH7WeUzng+Qej8UPxxv+8lRTuouo0iaNDBuzIBA==" + }, + "@types/istanbul-lib-coverage": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.3.tgz", + "integrity": "sha512-sz7iLqvVUg1gIedBOvlkxPlc8/uVzyS5OwGz1cKjXzkl3FpL3al0crU8YGU1WoHkxn0Wxbw5tyi6hvzJKNzFsw==" + }, + "@types/istanbul-lib-report": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", + "integrity": "sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg==", + "requires": { + "@types/istanbul-lib-coverage": "*" + } + }, + "@types/istanbul-reports": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.0.tgz", + "integrity": "sha512-nwKNbvnwJ2/mndE9ItP/zc2TCzw6uuodnF4EHYWD+gCQDVBuRQL5UzbZD0/ezy1iKsFU2ZQiDqg4M9dN4+wZgA==", + "requires": { + "@types/istanbul-lib-report": "*" + } + }, + "@types/jest": { + "version": "26.0.23", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-26.0.23.tgz", + "integrity": "sha512-ZHLmWMJ9jJ9PTiT58juykZpL7KjwJywFN3Rr2pTSkyQfydf/rk22yS7W8p5DaVUMQ2BQC7oYiU3FjbTM/mYrOA==", + "dev": true, + "requires": { + "jest-diff": "^26.0.0", + "pretty-format": "^26.0.0" + } + }, + "@types/json-schema": { + "version": "7.0.7", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.7.tgz", + "integrity": "sha512-cxWFQVseBm6O9Gbw1IWb8r6OS4OhSt3hPZLkFApLjM8TEXROBuQGLAH2i2gZpcXdLBIrpXuTDhH7Vbm1iXmNGA==" + }, + "@types/json5": { + "version": "0.0.29", + "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", + "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==" + }, + "@types/lodash": { + "version": "4.14.168", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.168.tgz", + "integrity": "sha512-oVfRvqHV/V6D1yifJbVRU3TMp8OT6o6BG+U9MkwuJ3U8/CsDHvalRpsxBqivn71ztOFZBTfJMvETbqHiaNSj7Q==", + "dev": true + }, + "@types/lodash.clonedeep": { + "version": "4.5.6", + "resolved": "https://registry.npmjs.org/@types/lodash.clonedeep/-/lodash.clonedeep-4.5.6.tgz", + "integrity": "sha512-cE1jYr2dEg1wBImvXlNtp0xDoS79rfEdGozQVgliDZj1uERH4k+rmEMTudP9b4VQ8O6nRb5gPqft0QzEQGMQgA==", + "dev": true, + "requires": { + "@types/lodash": "*" + } + }, + "@types/lodash.pick": { + "version": "4.4.6", + "resolved": "https://registry.npmjs.org/@types/lodash.pick/-/lodash.pick-4.4.6.tgz", + "integrity": "sha512-u8bzA16qQ+8dY280z3aK7PoWb3fzX5ATJ0rJB6F+uqchOX2VYF02Aqa+8aYiHiHgPzQiITqCgeimlyKFy4OA6g==", + "dev": true, + "requires": { + "@types/lodash": "*" + } + }, + "@types/lodash.throttle": { + "version": "4.1.6", + "resolved": "https://registry.npmjs.org/@types/lodash.throttle/-/lodash.throttle-4.1.6.tgz", + "integrity": "sha512-/UIH96i/sIRYGC60NoY72jGkCJtFN5KVPhEMMMTjol65effe1gPn0tycJqV5tlSwMTzX8FqzB5yAj0rfGHTPNg==", + "dev": true, + "requires": { + "@types/lodash": "*" + } + }, + "@types/minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-1z8k4wzFnNjVK/tlxvrWuK5WMt6mydWWP7+zvH5eFep4oj+UkrfiJTRtjCeBXNpwaA/FYqqtb4/QS4ianFpIRA==" + }, + "@types/minimist": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.1.tgz", + "integrity": "sha512-fZQQafSREFyuZcdWFAExYjBiCL7AUCdgsk80iO0q4yihYYdcIiH28CcuPTGFgLOCC8RlW49GSQxdHwZP+I7CNg==", + "dev": true + }, + "@types/moxios": { + "version": "0.4.11", + "resolved": "https://registry.npmjs.org/@types/moxios/-/moxios-0.4.11.tgz", + "integrity": "sha512-731cjysq/Z+8ImvUWrYS/FO6KOKnbqPbeBfe1HBCVTWBegJso62dyjLnnevz9VzSWx2tRAijyEorqv8/8+2n3A==", + "dev": true, + "requires": { + "axios": "^0.21.1" + }, + "dependencies": { + "axios": { + "version": "0.21.1", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.1.tgz", + "integrity": "sha512-dKQiRHxGD9PPRIUNIWvZhPTPpl1rf/OxTYKsqKUDjBwYylTvV7SjSHJb9ratfyzM6wCdLCOYLzs73qpg5c4iGA==", + "dev": true, + "requires": { + "follow-redirects": "^1.10.0" + } + } + } + }, + "@types/node": { + "version": "12.20.11", + "resolved": "https://registry.npmjs.org/@types/node/-/node-12.20.11.tgz", + "integrity": "sha512-gema+apZ6qLQK7k7F0dGkGCWQYsL0qqKORWOQO6tq46q+x+1C0vbOiOqOwRVlh4RAdbQwV/j/ryr3u5NOG1fPQ==" + }, + "@types/normalize-package-data": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.0.tgz", + "integrity": "sha512-f5j5b/Gf71L+dbqxIpQ4Z2WlmI/mPJ0fOkGGmFgtb6sAu97EPczzbS3/tJKxmcYDj55OX6ssqwDAWOHIYDRDGA==" + }, + "@types/parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==" + }, + "@types/prettier": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.2.3.tgz", + "integrity": "sha512-PijRCG/K3s3w1We6ynUKdxEc5AcuuH3NBmMDP8uvKVp6X43UY7NQlTzczakXP3DJR0F4dfNQIGjU2cUeRYs2AA==" + }, + "@types/prop-types": { + "version": "15.7.3", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.3.tgz", + "integrity": "sha512-KfRL3PuHmqQLOG+2tGpRO26Ctg+Cq1E01D2DMriKEATHgWLfeNDmq9e29Q9WIky0dQ3NPkd1mzYH8Lm936Z9qw==" + }, + "@types/q": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/@types/q/-/q-1.5.4.tgz", + "integrity": "sha512-1HcDas8SEj4z1Wc696tH56G8OlRaH/sqZOynNNB+HF0WOeXPaxTtbYzJY2oEfiUxjSKjhCKr+MvR7dCHcEelug==" + }, + "@types/react": { + "version": "16.14.5", + "resolved": "https://registry.npmjs.org/@types/react/-/react-16.14.5.tgz", + "integrity": "sha512-YRRv9DNZhaVTVRh9Wmmit7Y0UFhEVqXqCSw3uazRWMxa2x85hWQZ5BN24i7GXZbaclaLXEcodEeIHsjBA8eAMw==", + "requires": { + "@types/prop-types": "*", + "@types/scheduler": "*", + "csstype": "^3.0.2" + } + }, + "@types/react-datepicker": { + "version": "3.1.8", + "resolved": "https://registry.npmjs.org/@types/react-datepicker/-/react-datepicker-3.1.8.tgz", + "integrity": "sha512-RFEg7++xhosMq02i2lsuaUPEbZGn66U3dxtvw9LU/ZRqLkBGr9Ft2LTz6vbeYYVtaBdOr0NcQatOLnlfUaS8kw==", + "requires": { + "@types/react": "*", + "date-fns": "^2.0.1", + "popper.js": "^1.14.1" + } + }, + "@types/react-dom": { + "version": "16.9.12", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-16.9.12.tgz", + "integrity": "sha512-i7NPZZpPte3jtVOoW+eLB7G/jsX5OM6GqQnH+lC0nq0rqwlK0x8WcMEvYDgFWqWhWMlTltTimzdMax6wYfZssA==", + "requires": { + "@types/react": "^16" + } + }, + "@types/react-modal": { + "version": "3.12.0", + "resolved": "https://registry.npmjs.org/@types/react-modal/-/react-modal-3.12.0.tgz", + "integrity": "sha512-UnHu/YO73NZLwqFpju/c0tqiswR0UHIfeO16rkfLVJUIMfQsl7X0CBm99H5XXgBMe/PgtQ/Rkud72iuRBr1TeA==", + "dev": true, + "requires": { + "@types/react": "*" + } + }, + "@types/react-redux": { + "version": "7.1.16", + "resolved": "https://registry.npmjs.org/@types/react-redux/-/react-redux-7.1.16.tgz", + "integrity": "sha512-f/FKzIrZwZk7YEO9E1yoxIuDNRiDducxkFlkw/GNMGEnK9n4K8wJzlJBghpSuOVDgEUHoDkDF7Gi9lHNQR4siw==", + "requires": { + "@types/hoist-non-react-statics": "^3.3.0", + "@types/react": "*", + "hoist-non-react-statics": "^3.3.0", + "redux": "^4.0.0" + } + }, + "@types/react-router": { + "version": "5.1.13", + "resolved": "https://registry.npmjs.org/@types/react-router/-/react-router-5.1.13.tgz", + "integrity": "sha512-ZIuaO9Yrln54X6elg8q2Ivp6iK6p4syPsefEYAhRDAoqNh48C8VYUmB9RkXjKSQAJSJV0mbIFCX7I4vZDcHrjg==", + "requires": { + "@types/history": "*", + "@types/react": "*" + } + }, + "@types/react-router-dom": { + "version": "5.1.7", + "resolved": "https://registry.npmjs.org/@types/react-router-dom/-/react-router-dom-5.1.7.tgz", + "integrity": "sha512-D5mHD6TbdV/DNHYsnwBTv+y73ei+mMjrkGrla86HthE4/PVvL1J94Bu3qABU+COXzpL23T1EZapVVpwHuBXiUg==", + "requires": { + "@types/history": "*", + "@types/react": "*", + "@types/react-router": "*" + } + }, + "@types/react-toggle": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/react-toggle/-/react-toggle-4.0.2.tgz", + "integrity": "sha512-sHqfoKFnL0YU2+OC4meNEC8Ptx9FE8/+nFeFvNcdBa6ANA8KpAzj3R9JN8GtrvlLgjKDoYgI7iILgXYcTPo2IA==", + "requires": { + "@types/react": "*" + } + }, + "@types/react-transition-group": { + "version": "4.4.4", + "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.4.tgz", + "integrity": "sha512-7gAPz7anVK5xzbeQW9wFBDg7G++aPLAFY0QaSMOou9rJZpbuI58WAuJrgu+qR92l61grlnCUe7AFX8KGahAgug==", + "requires": { + "@types/react": "*" + } + }, + "@types/redux-thunk": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@types/redux-thunk/-/redux-thunk-2.1.0.tgz", + "integrity": "sha512-8M+kcrnZQYO3A40d8a1lieBFur/ab8EXHqa2dGMQqsmBaVCbNp3gd8ed/HuSjLoLA63ov/trXaXWlkiKVF4VUw==", + "dev": true, + "requires": { + "redux-thunk": "*" + } + }, + "@types/resolve": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-0.0.8.tgz", + "integrity": "sha512-auApPaJf3NPfe18hSoJkp8EbZzer2ISk7o8mCC3M9he/a04+gbMF97NkpD2S8riMGvm4BMRI59/SZQSaLTKpsQ==", + "requires": { + "@types/node": "*" + } + }, + "@types/scheduler": { + "version": "0.16.1", + "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.1.tgz", + "integrity": "sha512-EaCxbanVeyxDRTQBkdLb3Bvl/HK7PBK6UJjsSixB0iHKoWxE5uu2Q/DgtpOhPIojN0Zl1whvOd7PoHs2P0s5eA==" + }, + "@types/source-list-map": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/@types/source-list-map/-/source-list-map-0.1.2.tgz", + "integrity": "sha512-K5K+yml8LTo9bWJI/rECfIPrGgxdpeNbj+d53lwN4QjW1MCwlkhUms+gtdzigTeUyBr09+u8BwOIY3MXvHdcsA==" + }, + "@types/stack-utils": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.0.tgz", + "integrity": "sha512-RJJrrySY7A8havqpGObOB4W92QXKJo63/jFLLgpvOtsGUqbQZ9Sbgl35KMm1DjC6j7AvmmU2bIno+3IyEaemaw==" + }, + "@types/styled-components": { + "version": "5.1.26", + "resolved": "https://registry.npmjs.org/@types/styled-components/-/styled-components-5.1.26.tgz", + "integrity": "sha512-KuKJ9Z6xb93uJiIyxo/+ksS7yLjS1KzG6iv5i78dhVg/X3u5t1H7juRWqVmodIdz6wGVaIApo1u01kmFRdJHVw==", + "requires": { + "@types/hoist-non-react-statics": "*", + "@types/react": "*", + "csstype": "^3.0.2" + } + }, + "@types/tapable": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/@types/tapable/-/tapable-1.0.7.tgz", + "integrity": "sha512-0VBprVqfgFD7Ehb2vd8Lh9TG3jP98gvr8rgehQqzztZNI7o8zS8Ad4jyZneKELphpuE212D8J70LnSNQSyO6bQ==" + }, + "@types/testing-library__dom": { + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/@types/testing-library__dom/-/testing-library__dom-6.14.0.tgz", + "integrity": "sha512-sMl7OSv0AvMOqn1UJ6j1unPMIHRXen0Ita1ujnMX912rrOcawe4f7wu0Zt9GIQhBhJvH2BaibqFgQ3lP+Pj2hA==", + "dev": true, + "requires": { + "pretty-format": "^24.3.0" + }, + "dependencies": { + "@jest/types": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-24.9.0.tgz", + "integrity": "sha512-XKK7ze1apu5JWQ5eZjHITP66AX+QsLlbaJRBGYr8pNzwcAE2JVkwnf0yqjHTsDRcjR0mujy/NmZMXw5kl+kGBw==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^1.1.1", + "@types/yargs": "^13.0.0" + } + }, + "@types/istanbul-reports": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-1.1.2.tgz", + "integrity": "sha512-P/W9yOX/3oPZSpaYOCQzGqgCQRXn0FFO/V8bWrCQs+wLmvVVxk6CRBXALEvNs9OHIatlnlFokfhuDo2ug01ciw==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "*", + "@types/istanbul-lib-report": "*" + } + }, + "@types/yargs": { + "version": "13.0.11", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-13.0.11.tgz", + "integrity": "sha512-NRqD6T4gktUrDi1o1wLH3EKC1o2caCr7/wR87ODcbVITQF106OM3sFN92ysZ++wqelOd1CTzatnOBRDYYG6wGQ==", + "dev": true, + "requires": { + "@types/yargs-parser": "*" + } + }, + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "dev": true + }, + "pretty-format": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-24.9.0.tgz", + "integrity": "sha512-00ZMZUiHaJrNfk33guavqgvfJS30sLYf0f8+Srklv0AMPodGGHcoHgksZ3OThYnIvOd+8yMCn0YiEOogjlgsnA==", + "dev": true, + "requires": { + "@jest/types": "^24.9.0", + "ansi-regex": "^4.0.0", + "ansi-styles": "^3.2.0", + "react-is": "^16.8.4" + } + } + } + }, + "@types/testing-library__react": { + "version": "9.1.3", + "resolved": "https://registry.npmjs.org/@types/testing-library__react/-/testing-library__react-9.1.3.tgz", + "integrity": "sha512-iCdNPKU3IsYwRK9JieSYAiX0+aYDXOGAmrC/3/M7AqqSDKnWWVv07X+Zk1uFSL7cMTUYzv4lQRfohucEocn5/w==", + "dev": true, + "requires": { + "@types/react-dom": "*", + "@types/testing-library__dom": "*", + "pretty-format": "^25.1.0" + }, + "dependencies": { + "@jest/types": { + "version": "25.5.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-25.5.0.tgz", + "integrity": "sha512-OXD0RgQ86Tu3MazKo8bnrkDRaDXXMGUqd+kTtLtK1Zb7CRzQcaSRPPPV37SvYTdevXEBVxe0HXylEjs8ibkmCw==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^1.1.1", + "@types/yargs": "^15.0.0", + "chalk": "^3.0.0" + } + }, + "@types/istanbul-reports": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-1.1.2.tgz", + "integrity": "sha512-P/W9yOX/3oPZSpaYOCQzGqgCQRXn0FFO/V8bWrCQs+wLmvVVxk6CRBXALEvNs9OHIatlnlFokfhuDo2ug01ciw==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "*", + "@types/istanbul-lib-report": "*" + } + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", + "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "pretty-format": { + "version": "25.5.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-25.5.0.tgz", + "integrity": "sha512-kbo/kq2LQ/A/is0PQwsEHM7Ca6//bGPPvU6UnsdDRSKTWxT/ru/xb88v4BJf6a69H+uTytOEsTusT9ksd/1iWQ==", + "dev": true, + "requires": { + "@jest/types": "^25.5.0", + "ansi-regex": "^5.0.0", + "ansi-styles": "^4.0.0", + "react-is": "^16.12.0" + } + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "@types/uglify-js": { + "version": "3.13.0", + "resolved": "https://registry.npmjs.org/@types/uglify-js/-/uglify-js-3.13.0.tgz", + "integrity": "sha512-EGkrJD5Uy+Pg0NUR8uA4bJ5WMfljyad0G+784vLCNUkD+QwOJXUbBYExXfVGf7YtyzdQp3L/XMYcliB987kL5Q==", + "requires": { + "source-map": "^0.6.1" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + } + } + }, + "@types/webpack": { + "version": "4.41.27", + "resolved": "https://registry.npmjs.org/@types/webpack/-/webpack-4.41.27.tgz", + "integrity": "sha512-wK/oi5gcHi72VMTbOaQ70VcDxSQ1uX8S2tukBK9ARuGXrYM/+u4ou73roc7trXDNmCxCoerE8zruQqX/wuHszA==", + "requires": { + "@types/anymatch": "*", + "@types/node": "*", + "@types/tapable": "^1", + "@types/uglify-js": "*", + "@types/webpack-sources": "*", + "source-map": "^0.6.0" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + } + } + }, + "@types/webpack-sources": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@types/webpack-sources/-/webpack-sources-2.1.0.tgz", + "integrity": "sha512-LXn/oYIpBeucgP1EIJbKQ2/4ZmpvRl+dlrFdX7+94SKRUV3Evy3FsfMZY318vGhkWUS5MPhtOM3w1/hCOAOXcg==", + "requires": { + "@types/node": "*", + "@types/source-list-map": "*", + "source-map": "^0.7.3" + }, + "dependencies": { + "source-map": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", + "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==" + } + } + }, + "@types/xlsx": { + "version": "0.0.36", + "resolved": "https://registry.npmjs.org/@types/xlsx/-/xlsx-0.0.36.tgz", + "integrity": "sha512-mvfrKiKKMErQzLMF8ElYEH21qxWCZtN59pHhWGmWCWFJStYdMWjkDSAy6mGowFxHXaXZWe5/TW7pBUiWclIVOw==", + "requires": { + "xlsx": "*" + } + }, + "@types/yargs": { + "version": "15.0.13", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.13.tgz", + "integrity": "sha512-kQ5JNTrbDv3Rp5X2n/iUu37IJBDU2gsZ5R/g1/KHOOEc5IKfUFjXT6DENPGduh08I/pamwtEq4oul7gUqKTQDQ==", + "requires": { + "@types/yargs-parser": "*" + } + }, + "@types/yargs-parser": { + "version": "20.2.0", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-20.2.0.tgz", + "integrity": "sha512-37RSHht+gzzgYeobbG+KWryeAW8J33Nhr69cjTqSYymXVZEN9NbRYWoYlRtDhHKPVT1FyNKwaTPC1NynKZpzRA==" + }, + "@typescript-eslint/eslint-plugin": { + "version": "4.22.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.22.0.tgz", + "integrity": "sha512-U8SP9VOs275iDXaL08Ln1Fa/wLXfj5aTr/1c0t0j6CdbOnxh+TruXu1p4I0NAvdPBQgoPjHsgKn28mOi0FzfoA==", + "requires": { + "@typescript-eslint/experimental-utils": "4.22.0", + "@typescript-eslint/scope-manager": "4.22.0", + "debug": "^4.1.1", + "functional-red-black-tree": "^1.0.1", + "lodash": "^4.17.15", + "regexpp": "^3.0.0", + "semver": "^7.3.2", + "tsutils": "^3.17.1" + } + }, + "@typescript-eslint/experimental-utils": { + "version": "4.22.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-4.22.0.tgz", + "integrity": "sha512-xJXHHl6TuAxB5AWiVrGhvbGL8/hbiCQ8FiWwObO3r0fnvBdrbWEDy1hlvGQOAWc6qsCWuWMKdVWlLAEMpxnddg==", + "requires": { + "@types/json-schema": "^7.0.3", + "@typescript-eslint/scope-manager": "4.22.0", + "@typescript-eslint/types": "4.22.0", + "@typescript-eslint/typescript-estree": "4.22.0", + "eslint-scope": "^5.0.0", + "eslint-utils": "^2.0.0" + } + }, + "@typescript-eslint/parser": { + "version": "4.22.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-4.22.0.tgz", + "integrity": "sha512-z/bGdBJJZJN76nvAY9DkJANYgK3nlRstRRi74WHm3jjgf2I8AglrSY+6l7ogxOmn55YJ6oKZCLLy+6PW70z15Q==", + "requires": { + "@typescript-eslint/scope-manager": "4.22.0", + "@typescript-eslint/types": "4.22.0", + "@typescript-eslint/typescript-estree": "4.22.0", + "debug": "^4.1.1" + } + }, + "@typescript-eslint/scope-manager": { + "version": "4.22.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.22.0.tgz", + "integrity": "sha512-OcCO7LTdk6ukawUM40wo61WdeoA7NM/zaoq1/2cs13M7GyiF+T4rxuA4xM+6LeHWjWbss7hkGXjFDRcKD4O04Q==", + "requires": { + "@typescript-eslint/types": "4.22.0", + "@typescript-eslint/visitor-keys": "4.22.0" + } + }, + "@typescript-eslint/types": { + "version": "4.22.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.22.0.tgz", + "integrity": "sha512-sW/BiXmmyMqDPO2kpOhSy2Py5w6KvRRsKZnV0c4+0nr4GIcedJwXAq+RHNK4lLVEZAJYFltnnk1tJSlbeS9lYA==" + }, + "@typescript-eslint/typescript-estree": { + "version": "4.22.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.22.0.tgz", + "integrity": "sha512-TkIFeu5JEeSs5ze/4NID+PIcVjgoU3cUQUIZnH3Sb1cEn1lBo7StSV5bwPuJQuoxKXlzAObjYTilOEKRuhR5yg==", + "requires": { + "@typescript-eslint/types": "4.22.0", + "@typescript-eslint/visitor-keys": "4.22.0", + "debug": "^4.1.1", + "globby": "^11.0.1", + "is-glob": "^4.0.1", + "semver": "^7.3.2", + "tsutils": "^3.17.1" + } + }, + "@typescript-eslint/visitor-keys": { + "version": "4.22.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.22.0.tgz", + "integrity": "sha512-nnMu4F+s4o0sll6cBSsTeVsT4cwxB7zECK3dFxzEjPBii9xLpq4yqqsy/FU5zMfan6G60DKZSCXAa3sHJZrcYw==", + "requires": { + "@typescript-eslint/types": "4.22.0", + "eslint-visitor-keys": "^2.0.0" + } + }, + "@webassemblyjs/ast": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.9.0.tgz", + "integrity": "sha512-C6wW5L+b7ogSDVqymbkkvuW9kruN//YisMED04xzeBBqjHa2FYnmvOlS6Xj68xWQRgWvI9cIglsjFowH/RJyEA==", + "requires": { + "@webassemblyjs/helper-module-context": "1.9.0", + "@webassemblyjs/helper-wasm-bytecode": "1.9.0", + "@webassemblyjs/wast-parser": "1.9.0" + } + }, + "@webassemblyjs/floating-point-hex-parser": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.9.0.tgz", + "integrity": "sha512-TG5qcFsS8QB4g4MhrxK5TqfdNe7Ey/7YL/xN+36rRjl/BlGE/NcBvJcqsRgCP6Z92mRE+7N50pRIi8SmKUbcQA==" + }, + "@webassemblyjs/helper-api-error": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.9.0.tgz", + "integrity": "sha512-NcMLjoFMXpsASZFxJ5h2HZRcEhDkvnNFOAKneP5RbKRzaWJN36NC4jqQHKwStIhGXu5mUWlUUk7ygdtrO8lbmw==" + }, + "@webassemblyjs/helper-buffer": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.9.0.tgz", + "integrity": "sha512-qZol43oqhq6yBPx7YM3m9Bv7WMV9Eevj6kMi6InKOuZxhw+q9hOkvq5e/PpKSiLfyetpaBnogSbNCfBwyB00CA==" + }, + "@webassemblyjs/helper-code-frame": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-code-frame/-/helper-code-frame-1.9.0.tgz", + "integrity": "sha512-ERCYdJBkD9Vu4vtjUYe8LZruWuNIToYq/ME22igL+2vj2dQ2OOujIZr3MEFvfEaqKoVqpsFKAGsRdBSBjrIvZA==", + "requires": { + "@webassemblyjs/wast-printer": "1.9.0" + } + }, + "@webassemblyjs/helper-fsm": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-fsm/-/helper-fsm-1.9.0.tgz", + "integrity": "sha512-OPRowhGbshCb5PxJ8LocpdX9Kl0uB4XsAjl6jH/dWKlk/mzsANvhwbiULsaiqT5GZGT9qinTICdj6PLuM5gslw==" + }, + "@webassemblyjs/helper-module-context": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-module-context/-/helper-module-context-1.9.0.tgz", + "integrity": "sha512-MJCW8iGC08tMk2enck1aPW+BE5Cw8/7ph/VGZxwyvGbJwjktKkDK7vy7gAmMDx88D7mhDTCNKAW5tED+gZ0W8g==", + "requires": { + "@webassemblyjs/ast": "1.9.0" + } + }, + "@webassemblyjs/helper-wasm-bytecode": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.9.0.tgz", + "integrity": "sha512-R7FStIzyNcd7xKxCZH5lE0Bqy+hGTwS3LJjuv1ZVxd9O7eHCedSdrId/hMOd20I+v8wDXEn+bjfKDLzTepoaUw==" + }, + "@webassemblyjs/helper-wasm-section": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.9.0.tgz", + "integrity": "sha512-XnMB8l3ek4tvrKUUku+IVaXNHz2YsJyOOmz+MMkZvh8h1uSJpSen6vYnw3IoQ7WwEuAhL8Efjms1ZWjqh2agvw==", + "requires": { + "@webassemblyjs/ast": "1.9.0", + "@webassemblyjs/helper-buffer": "1.9.0", + "@webassemblyjs/helper-wasm-bytecode": "1.9.0", + "@webassemblyjs/wasm-gen": "1.9.0" + } + }, + "@webassemblyjs/ieee754": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.9.0.tgz", + "integrity": "sha512-dcX8JuYU/gvymzIHc9DgxTzUUTLexWwt8uCTWP3otys596io0L5aW02Gb1RjYpx2+0Jus1h4ZFqjla7umFniTg==", + "requires": { + "@xtuc/ieee754": "^1.2.0" + } + }, + "@webassemblyjs/leb128": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.9.0.tgz", + "integrity": "sha512-ENVzM5VwV1ojs9jam6vPys97B/S65YQtv/aanqnU7D8aSoHFX8GyhGg0CMfyKNIHBuAVjy3tlzd5QMMINa7wpw==", + "requires": { + "@xtuc/long": "4.2.2" + } + }, + "@webassemblyjs/utf8": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.9.0.tgz", + "integrity": "sha512-GZbQlWtopBTP0u7cHrEx+73yZKrQoBMpwkGEIqlacljhXCkVM1kMQge/Mf+csMJAjEdSwhOyLAS0AoR3AG5P8w==" + }, + "@webassemblyjs/wasm-edit": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.9.0.tgz", + "integrity": "sha512-FgHzBm80uwz5M8WKnMTn6j/sVbqilPdQXTWraSjBwFXSYGirpkSWE2R9Qvz9tNiTKQvoKILpCuTjBKzOIm0nxw==", + "requires": { + "@webassemblyjs/ast": "1.9.0", + "@webassemblyjs/helper-buffer": "1.9.0", + "@webassemblyjs/helper-wasm-bytecode": "1.9.0", + "@webassemblyjs/helper-wasm-section": "1.9.0", + "@webassemblyjs/wasm-gen": "1.9.0", + "@webassemblyjs/wasm-opt": "1.9.0", + "@webassemblyjs/wasm-parser": "1.9.0", + "@webassemblyjs/wast-printer": "1.9.0" + } + }, + "@webassemblyjs/wasm-gen": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.9.0.tgz", + "integrity": "sha512-cPE3o44YzOOHvlsb4+E9qSqjc9Qf9Na1OO/BHFy4OI91XDE14MjFN4lTMezzaIWdPqHnsTodGGNP+iRSYfGkjA==", + "requires": { + "@webassemblyjs/ast": "1.9.0", + "@webassemblyjs/helper-wasm-bytecode": "1.9.0", + "@webassemblyjs/ieee754": "1.9.0", + "@webassemblyjs/leb128": "1.9.0", + "@webassemblyjs/utf8": "1.9.0" + } + }, + "@webassemblyjs/wasm-opt": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.9.0.tgz", + "integrity": "sha512-Qkjgm6Anhm+OMbIL0iokO7meajkzQD71ioelnfPEj6r4eOFuqm4YC3VBPqXjFyyNwowzbMD+hizmprP/Fwkl2A==", + "requires": { + "@webassemblyjs/ast": "1.9.0", + "@webassemblyjs/helper-buffer": "1.9.0", + "@webassemblyjs/wasm-gen": "1.9.0", + "@webassemblyjs/wasm-parser": "1.9.0" + } + }, + "@webassemblyjs/wasm-parser": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.9.0.tgz", + "integrity": "sha512-9+wkMowR2AmdSWQzsPEjFU7njh8HTO5MqO8vjwEHuM+AMHioNqSBONRdr0NQQ3dVQrzp0s8lTcYqzUdb7YgELA==", + "requires": { + "@webassemblyjs/ast": "1.9.0", + "@webassemblyjs/helper-api-error": "1.9.0", + "@webassemblyjs/helper-wasm-bytecode": "1.9.0", + "@webassemblyjs/ieee754": "1.9.0", + "@webassemblyjs/leb128": "1.9.0", + "@webassemblyjs/utf8": "1.9.0" + } + }, + "@webassemblyjs/wast-parser": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-parser/-/wast-parser-1.9.0.tgz", + "integrity": "sha512-qsqSAP3QQ3LyZjNC/0jBJ/ToSxfYJ8kYyuiGvtn/8MK89VrNEfwj7BPQzJVHi0jGTRK2dGdJ5PRqhtjzoww+bw==", + "requires": { + "@webassemblyjs/ast": "1.9.0", + "@webassemblyjs/floating-point-hex-parser": "1.9.0", + "@webassemblyjs/helper-api-error": "1.9.0", + "@webassemblyjs/helper-code-frame": "1.9.0", + "@webassemblyjs/helper-fsm": "1.9.0", + "@xtuc/long": "4.2.2" + } + }, + "@webassemblyjs/wast-printer": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.9.0.tgz", + "integrity": "sha512-2J0nE95rHXHyQ24cWjMKJ1tqB/ds8z/cyeOZxJhcb+rW+SQASVjuznUSmdz5GpVJTzU8JkhYut0D3siFDD6wsA==", + "requires": { + "@webassemblyjs/ast": "1.9.0", + "@webassemblyjs/wast-parser": "1.9.0", + "@xtuc/long": "4.2.2" + } + }, + "@xtuc/ieee754": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", + "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==" + }, + "@xtuc/long": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", + "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==" + }, + "JSONStream": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.5.tgz", + "integrity": "sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==", + "dev": true, + "requires": { + "jsonparse": "^1.2.0", + "through": ">=2.2.7 <3" + } + }, + "abab": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.5.tgz", + "integrity": "sha512-9IK9EadsbHo6jLWIpxpR6pL0sazTXV6+SQv25ZB+F7Bj9mJNaOc4nCRabwd5M/JwmUa8idz6Eci6eKfJryPs6Q==" + }, + "abbrev": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", + "dev": true + }, + "accepts": { + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", + "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", + "requires": { + "mime-types": "~2.1.24", + "negotiator": "0.6.2" + } + }, + "acorn": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", + "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==" + }, + "acorn-globals": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-6.0.0.tgz", + "integrity": "sha512-ZQl7LOWaF5ePqqcX4hLuv/bLXYQNfNWw2c0/yX/TsPRKamzHcTGQnlCjHT3TsmkOUVEPS3crCxiPfdzE/Trlhg==", + "requires": { + "acorn": "^7.1.1", + "acorn-walk": "^7.1.1" + } + }, + "acorn-jsx": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.1.tgz", + "integrity": "sha512-K0Ptm/47OKfQRpNQ2J/oIN/3QYiK6FwW+eJbILhsdxh2WTLdl+30o8aGdTbm5JbffpFFAg/g+zi1E+jvJha5ng==" + }, + "acorn-walk": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.2.0.tgz", + "integrity": "sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==" + }, + "add-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/add-stream/-/add-stream-1.0.0.tgz", + "integrity": "sha512-qQLMr+8o0WC4FZGQTcJiKBVC59JylcPSrTtk6usvmIDFUOCKegapy1VHQwRbFMOFyb/inzUVqHs+eMYKDM1YeQ==", + "dev": true + }, + "address": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/address/-/address-1.1.2.tgz", + "integrity": "sha512-aT6camzM4xEA54YVJYSqxz1kv4IHnQZRtThJJHhUMRExaU5spC7jX5ugSwTaTgJliIgs4VhZOk7htClvQ/LmRA==" + }, + "adjust-sourcemap-loader": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/adjust-sourcemap-loader/-/adjust-sourcemap-loader-3.0.0.tgz", + "integrity": "sha512-YBrGyT2/uVQ/c6Rr+t6ZJXniY03YtHGMJQYal368burRGYKqhx9qGTWqcBU5s1CwYY9E/ri63RYyG1IacMZtqw==", + "requires": { + "loader-utils": "^2.0.0", + "regex-parser": "^2.2.11" + } + }, + "adler-32": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/adler-32/-/adler-32-1.2.0.tgz", + "integrity": "sha512-/vUqU/UY4MVeFsg+SsK6c+/05RZXIHZMGJA+PX5JyWI0ZRcBpupnRuPLU/NXXoFwMYCPCoxIfElM2eS+DUXCqQ==", + "requires": { + "exit-on-epipe": "~1.0.1", + "printj": "~1.1.0" + } + }, + "aggregate-error": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", + "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", + "requires": { + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" + }, + "dependencies": { + "indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==" + } + } + }, + "airbnb-prop-types": { + "version": "2.16.0", + "resolved": "https://registry.npmjs.org/airbnb-prop-types/-/airbnb-prop-types-2.16.0.tgz", + "integrity": "sha512-7WHOFolP/6cS96PhKNrslCLMYAI8yB1Pp6u6XmxozQOiZbsI5ycglZr5cHhBFfuRcQQjzCMith5ZPZdYiJCxUg==", + "dev": true, + "requires": { + "array.prototype.find": "^2.1.1", + "function.prototype.name": "^1.1.2", + "is-regex": "^1.1.0", + "object-is": "^1.1.2", + "object.assign": "^4.1.0", + "object.entries": "^1.1.2", + "prop-types": "^15.7.2", + "prop-types-exact": "^1.2.0", + "react-is": "^16.13.1" + } + }, + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "ajv-errors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ajv-errors/-/ajv-errors-1.0.1.tgz", + "integrity": "sha512-DCRfO/4nQ+89p/RK43i8Ezd41EqdGIU4ld7nGF8OQ14oc/we5rEntLCUa7+jrn3nn83BosfwZA0wb4pon2o8iQ==" + }, + "ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==" + }, + "alphanum-sort": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/alphanum-sort/-/alphanum-sort-1.0.2.tgz", + "integrity": "sha512-0FcBfdcmaumGPQ0qPn7Q5qTgz/ooXgIyp1rf8ik5bGX8mpE2YHjC0P/eyQvxu1GURYQgq9ozf2mteQ5ZD9YiyQ==" + }, + "amdefine": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz", + "integrity": "sha512-S2Hw0TtNkMJhIabBwIojKL9YHO5T0n5eNqWJ7Lrlel/zDbftQpxpapi8tZs3X1HWa+u+QeydGmzzNU0m09+Rcg==", + "dev": true + }, + "ansi-colors": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", + "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==" + }, + "ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "requires": { + "type-fest": "^0.21.3" + }, + "dependencies": { + "type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==" + } + } + }, + "ansi-html": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/ansi-html/-/ansi-html-0.0.7.tgz", + "integrity": "sha512-JoAxEa1DfP9m2xfB/y2r/aKcwXNlltr4+0QSBC4TrLfcxyvepX2Pv0t/xpgGV5bGsDzCYV8SzjWgyCW0T9yYbA==" + }, + "ansi-regex": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==" + }, + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "requires": { + "color-convert": "^1.9.0" + } + }, + "anymatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", + "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", + "requires": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + } + }, + "aproba": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", + "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==" + }, + "are-we-there-yet": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz", + "integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==", + "dev": true, + "requires": { + "delegates": "^1.0.0", + "readable-stream": "^2.0.6" + } + }, + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "requires": { + "sprintf-js": "~1.0.2" + } + }, + "aria-query": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-4.2.2.tgz", + "integrity": "sha512-o/HelwhuKpTj/frsOsbNLNgnNGVIFsVP/SW2BSF14gVl7kAfMOJ6/8wUAUvG1R1NHKrfG+2sHZTu0yauT1qBrA==", + "requires": { + "@babel/runtime": "^7.10.2", + "@babel/runtime-corejs3": "^7.10.2" + } + }, + "arity-n": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/arity-n/-/arity-n-1.0.4.tgz", + "integrity": "sha512-fExL2kFDC1Q2DUOx3whE/9KoN66IzkY4b4zUHUBFM1ojEYjZZYDcUW3bek/ufGionX9giIKDC5redH2IlGqcQQ==" + }, + "arr-diff": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", + "integrity": "sha512-YVIQ82gZPGBebQV/a8dar4AitzCQs0jjXwMPZllpXMaGjXPYVUawSxQrRsjhjupyVxEvbHgUmIhKVlND+j02kA==" + }, + "arr-flatten": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", + "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==" + }, + "arr-union": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", + "integrity": "sha512-sKpyeERZ02v1FeCZT8lrfJq5u6goHCtpTAzPwJYe7c8SPFOboNjNg1vz2L4VTn9T4PQxEx13TbXLmYUcS6Ug7Q==" + }, + "array-differ": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/array-differ/-/array-differ-3.0.0.tgz", + "integrity": "sha512-THtfYS6KtME/yIAhKjZ2ul7XI96lQGHRputJQHO80LAWQnuGP4iCIN8vdMRboGbIEYBwU33q8Tch1os2+X0kMg==", + "dev": true + }, + "array-filter": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/array-filter/-/array-filter-1.0.0.tgz", + "integrity": "sha1-uveeYubvTCpMC4MSMtr/7CUfnYM=", + "dev": true + }, + "array-find-index": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz", + "integrity": "sha512-M1HQyIXcBGtVywBt8WVdim+lrNaK7VHp99Qt5pSNziXznKHViIBbXWtfRTpEFpF/c4FdfxNAsCCwPp5phBYJtw==", + "dev": true + }, + "array-flatten": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-2.1.2.tgz", + "integrity": "sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ==" + }, + "array-ify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/array-ify/-/array-ify-1.0.0.tgz", + "integrity": "sha512-c5AMf34bKdvPhQ7tBGhqkgKNUzMr4WUs+WDtC2ZUGOUncbxKMTvqxYctiseW3+L4bA8ec+GcZ6/A/FW4m8ukng==", + "dev": true + }, + "array-includes": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.3.tgz", + "integrity": "sha512-gcem1KlBU7c9rB+Rq8/3PPKsK2kjqeEBa3bD5kkQo4nYlOHQCJqIJFqBXDEfwaRuYTT4E+FxA9xez7Gf/e3Q7A==", + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.18.0-next.2", + "get-intrinsic": "^1.1.1", + "is-string": "^1.0.5" + } + }, + "array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==" + }, + "array-uniq": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", + "integrity": "sha512-MNha4BWQ6JbwhFhj03YK552f7cb3AzoE8SzeljgChvL1dl3IcvggXVz1DilzySZkCja+CXuZbdW7yATchWn8/Q==" + }, + "array-unique": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", + "integrity": "sha512-SleRWjh9JUud2wH1hPs9rZBZ33H6T9HOiL0uwGnGx9FpE6wKGyfWugmbkEOIs6qWrZhg0LWeLziLrEwQJhs5mQ==" + }, + "array.prototype.find": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/array.prototype.find/-/array.prototype.find-2.1.1.tgz", + "integrity": "sha512-mi+MYNJYLTx2eNYy+Yh6raoQacCsNeeMUaspFPh9Y141lFSsWxxB8V9mM2ye+eqiRs917J6/pJ4M9ZPzenWckA==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.4" + } + }, + "array.prototype.flat": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.2.4.tgz", + "integrity": "sha512-4470Xi3GAPAjZqFcljX2xzckv1qeKPizoNkiS0+O4IoPR2ZNpcjE0pkhdihlDouK+x6QOast26B4Q/O9DJnwSg==", + "requires": { + "call-bind": "^1.0.0", + "define-properties": "^1.1.3", + "es-abstract": "^1.18.0-next.1" + } + }, + "array.prototype.flatmap": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.2.4.tgz", + "integrity": "sha512-r9Z0zYoxqHz60vvQbWEdXIEtCwHF0yxaWfno9qzXeNHvfyl3BZqygmGzb84dsubyaXLH4husF+NFgMSdpZhk2Q==", + "requires": { + "call-bind": "^1.0.0", + "define-properties": "^1.1.3", + "es-abstract": "^1.18.0-next.1", + "function-bind": "^1.1.1" + } + }, + "arrify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/arrify/-/arrify-2.0.1.tgz", + "integrity": "sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug==" + }, + "asap": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", + "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==" + }, + "asn1": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", + "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", + "requires": { + "safer-buffer": "~2.1.0" + } + }, + "asn1.js": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-5.4.1.tgz", + "integrity": "sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA==", + "requires": { + "bn.js": "^4.0.0", + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0", + "safer-buffer": "^2.1.0" + }, + "dependencies": { + "bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" + } + } + }, + "assert": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/assert/-/assert-1.5.0.tgz", + "integrity": "sha512-EDsgawzwoun2CZkCgtxJbv392v4nbk9XDD06zI+kQYoBM/3RBWLlEyJARDOmhAAosBjWACEkKL6S+lIZtcAubA==", + "requires": { + "object-assign": "^4.1.1", + "util": "0.10.3" + }, + "dependencies": { + "inherits": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz", + "integrity": "sha512-8nWq2nLTAwd02jTqJExUYFSD/fKq6VH9Y/oG2accc/kdI0V98Bag8d5a4gi3XHz73rDWa2PvTtvcWYquKqSENA==" + }, + "util": { + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/util/-/util-0.10.3.tgz", + "integrity": "sha512-5KiHfsmkqacuKjkRkdV7SsfDJ2EGiPsK92s2MhNSY0craxjTdKTtqKsJaCWp4LW33ZZ0OPUv1WO/TFvNQRiQxQ==", + "requires": { + "inherits": "2.0.1" + } + } + } + }, + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==" + }, + "assign-symbols": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", + "integrity": "sha512-Q+JC7Whu8HhmTdBph/Tq59IoRtoy6KAm5zzPv00WdujX82lbAL8K7WVjne7vdCsAmbF4AYaDOPyO3k0kl8qIrw==" + }, + "ast-types-flow": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.7.tgz", + "integrity": "sha512-eBvWn1lvIApYMhzQMsu9ciLfkBY499mFZlNqG+/9WR7PVlroQw0vG30cOQQbaKz3sCEc44TAOu2ykzqXSNnwag==" + }, + "astral-regex": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", + "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==" + }, + "async": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz", + "integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==", + "requires": { + "lodash": "^4.17.14" + } + }, + "async-each": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.3.tgz", + "integrity": "sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ==" + }, + "async-foreach": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/async-foreach/-/async-foreach-0.1.3.tgz", + "integrity": "sha512-VUeSMD8nEGBWaZK4lizI1sf3yEC7pnAQ/mrI7pC2fBz2s/tq5jWWEngTwaf0Gruu/OoXRGLGg1XFqpYBiGTYJA==", + "dev": true + }, + "async-limiter": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz", + "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==" + }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + }, + "at-least-node": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", + "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==" + }, + "atob": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", + "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==" + }, + "autoprefixer": { + "version": "9.8.6", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-9.8.6.tgz", + "integrity": "sha512-XrvP4VVHdRBCdX1S3WXVD8+RyG9qeb1D5Sn1DeLiG2xfSpzellk5k54xbUERJ3M5DggQxes39UGOTP8CFrEGbg==", + "requires": { + "browserslist": "^4.12.0", + "caniuse-lite": "^1.0.30001109", + "colorette": "^1.2.1", + "normalize-range": "^0.1.2", + "num2fraction": "^1.2.2", + "postcss": "^7.0.32", + "postcss-value-parser": "^4.1.0" + } + }, + "aws-sign2": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", + "integrity": "sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA==" + }, + "aws4": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.11.0.tgz", + "integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==" + }, + "axe-core": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.2.0.tgz", + "integrity": "sha512-1uIESzroqpaTzt9uX48HO+6gfnKu3RwvWdCcWSrX4csMInJfCo1yvKPNXCwXFRpJqRW25tiASb6No0YH57PXqg==" + }, + "axios": { + "version": "0.21.1", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.1.tgz", + "integrity": "sha512-dKQiRHxGD9PPRIUNIWvZhPTPpl1rf/OxTYKsqKUDjBwYylTvV7SjSHJb9ratfyzM6wCdLCOYLzs73qpg5c4iGA==", + "requires": { + "follow-redirects": "^1.10.0" + } + }, + "axobject-query": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-2.2.0.tgz", + "integrity": "sha512-Td525n+iPOOyUQIeBfcASuG6uJsDOITl7Mds5gFyerkWiX7qhUTdYUBlSgNMyVqtSJqwpt1kXGLdUt6SykLMRA==" + }, + "babel-eslint": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/babel-eslint/-/babel-eslint-10.1.0.tgz", + "integrity": "sha512-ifWaTHQ0ce+448CYop8AdrQiBsGrnC+bMgfyKFdi6EsPLTAWG+QfyDeM6OH+FmWnKvEq5NnBMLvlBUPKQZoDSg==", + "requires": { + "@babel/code-frame": "^7.0.0", + "@babel/parser": "^7.7.0", + "@babel/traverse": "^7.7.0", + "@babel/types": "^7.7.0", + "eslint-visitor-keys": "^1.0.0", + "resolve": "^1.12.0" + }, + "dependencies": { + "eslint-visitor-keys": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", + "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==" + } + } + }, + "babel-extract-comments": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/babel-extract-comments/-/babel-extract-comments-1.0.0.tgz", + "integrity": "sha512-qWWzi4TlddohA91bFwgt6zO/J0X+io7Qp184Fw0m2JYRSTZnJbFR8+07KmzudHCZgOiKRCrjhylwv9Xd8gfhVQ==", + "requires": { + "babylon": "^6.18.0" + } + }, + "babel-jest": { + "version": "26.6.3", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-26.6.3.tgz", + "integrity": "sha512-pl4Q+GAVOHwvjrck6jKjvmGhnO3jHX/xuB9d27f+EJZ/6k+6nMuPjorrYp7s++bKKdANwzElBWnLWaObvTnaZA==", + "requires": { + "@jest/transform": "^26.6.2", + "@jest/types": "^26.6.2", + "@types/babel__core": "^7.1.7", + "babel-plugin-istanbul": "^6.0.0", + "babel-preset-jest": "^26.6.2", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.4", + "slash": "^3.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", + "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "babel-loader": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.1.0.tgz", + "integrity": "sha512-7q7nC1tYOrqvUrN3LQK4GwSk/TQorZSOlO9C+RZDZpODgyN4ZlCqE5q9cDsyWOliN+aU9B4JX01xK9eJXowJLw==", + "requires": { + "find-cache-dir": "^2.1.0", + "loader-utils": "^1.4.0", + "mkdirp": "^0.5.3", + "pify": "^4.0.1", + "schema-utils": "^2.6.5" + }, + "dependencies": { + "json5": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", + "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", + "requires": { + "minimist": "^1.2.0" + } + }, + "loader-utils": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz", + "integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==", + "requires": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^1.0.1" + } + }, + "pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==" + } + } + }, + "babel-plugin-dynamic-import-node": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.3.tgz", + "integrity": "sha512-jZVI+s9Zg3IqA/kdi0i6UDCybUI3aSBLnglhYbSSjKlV7yF1F/5LWv8MakQmvYpnbJDS6fcBL2KzHSxNCMtWSQ==", + "requires": { + "object.assign": "^4.1.0" + } + }, + "babel-plugin-istanbul": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.0.0.tgz", + "integrity": "sha512-AF55rZXpe7trmEylbaE1Gv54wn6rwU03aptvRoVIGP8YykoSxqdVLV1TfwflBCE/QtHmqtP8SWlTENqbK8GCSQ==", + "requires": { + "@babel/helper-plugin-utils": "^7.0.0", + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-instrument": "^4.0.0", + "test-exclude": "^6.0.0" + } + }, + "babel-plugin-jest-hoist": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-26.6.2.tgz", + "integrity": "sha512-PO9t0697lNTmcEHH69mdtYiOIkkOlj9fySqfO3K1eCcdISevLAE0xY59VLLUj0SoiPiTX/JU2CYFpILydUa5Lw==", + "requires": { + "@babel/template": "^7.3.3", + "@babel/types": "^7.3.3", + "@types/babel__core": "^7.0.0", + "@types/babel__traverse": "^7.0.6" + } + }, + "babel-plugin-macros": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-2.8.0.tgz", + "integrity": "sha512-SEP5kJpfGYqYKpBrj5XU3ahw5p5GOHJ0U5ssOSQ/WBVdwkD2Dzlce95exQTs3jOVWPPKLBN2rlEWkCK7dSmLvg==", + "requires": { + "@babel/runtime": "^7.7.2", + "cosmiconfig": "^6.0.0", + "resolve": "^1.12.0" + }, + "dependencies": { + "cosmiconfig": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-6.0.0.tgz", + "integrity": "sha512-xb3ZL6+L8b9JLLCx3ZdoZy4+2ECphCMo2PwqgP1tlfVq6M6YReyzBJtvWWtbDSpNr9hn96pkCiZqUcFEc+54Qg==", + "requires": { + "@types/parse-json": "^4.0.0", + "import-fresh": "^3.1.0", + "parse-json": "^5.0.0", + "path-type": "^4.0.0", + "yaml": "^1.7.2" + } + } + } + }, + "babel-plugin-named-asset-import": { + "version": "0.3.7", + "resolved": "https://registry.npmjs.org/babel-plugin-named-asset-import/-/babel-plugin-named-asset-import-0.3.7.tgz", + "integrity": "sha512-squySRkf+6JGnvjoUtDEjSREJEBirnXi9NqP6rjSYsylxQxqBTz+pkmf395i9E2zsvmYUaI40BHo6SqZUdydlw==" + }, + "babel-plugin-polyfill-corejs2": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.2.0.tgz", + "integrity": "sha512-9bNwiR0dS881c5SHnzCmmGlMkJLl0OUZvxrxHo9w/iNoRuqaPjqlvBf4HrovXtQs/au5yKkpcdgfT1cC5PAZwg==", + "requires": { + "@babel/compat-data": "^7.13.11", + "@babel/helper-define-polyfill-provider": "^0.2.0", + "semver": "^6.1.1" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" + } + } + }, + "babel-plugin-polyfill-corejs3": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.2.0.tgz", + "integrity": "sha512-zZyi7p3BCUyzNxLx8KV61zTINkkV65zVkDAFNZmrTCRVhjo1jAS+YLvDJ9Jgd/w2tsAviCwFHReYfxO3Iql8Yg==", + "requires": { + "@babel/helper-define-polyfill-provider": "^0.2.0", + "core-js-compat": "^3.9.1" + } + }, + "babel-plugin-polyfill-regenerator": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.2.0.tgz", + "integrity": "sha512-J7vKbCuD2Xi/eEHxquHN14bXAW9CXtecwuLrOIDJtcZzTaPzV1VdEfoUf9AzcRBMolKUQKM9/GVojeh0hFiqMg==", + "requires": { + "@babel/helper-define-polyfill-provider": "^0.2.0" + } + }, + "babel-plugin-syntax-object-rest-spread": { + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/babel-plugin-syntax-object-rest-spread/-/babel-plugin-syntax-object-rest-spread-6.13.0.tgz", + "integrity": "sha512-C4Aq+GaAj83pRQ0EFgTvw5YO6T3Qz2KGrNRwIj9mSoNHVvdZY4KO2uA6HNtNXCw993iSZnckY1aLW8nOi8i4+w==" + }, + "babel-plugin-transform-object-rest-spread": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-object-rest-spread/-/babel-plugin-transform-object-rest-spread-6.26.0.tgz", + "integrity": "sha512-ocgA9VJvyxwt+qJB0ncxV8kb/CjfTcECUY4tQ5VT7nP6Aohzobm8CDFaQ5FHdvZQzLmf0sgDxB8iRXZXxwZcyA==", + "requires": { + "babel-plugin-syntax-object-rest-spread": "^6.8.0", + "babel-runtime": "^6.26.0" + } + }, + "babel-plugin-transform-react-remove-prop-types": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-react-remove-prop-types/-/babel-plugin-transform-react-remove-prop-types-0.4.24.tgz", + "integrity": "sha512-eqj0hVcJUR57/Ug2zE1Yswsw4LhuqqHhD+8v120T1cl3kjg76QwtyBrdIk4WVwK+lAhBJVYCd/v+4nc4y+8JsA==" + }, + "babel-preset-current-node-syntax": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz", + "integrity": "sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==", + "requires": { + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/plugin-syntax-bigint": "^7.8.3", + "@babel/plugin-syntax-class-properties": "^7.8.3", + "@babel/plugin-syntax-import-meta": "^7.8.3", + "@babel/plugin-syntax-json-strings": "^7.8.3", + "@babel/plugin-syntax-logical-assignment-operators": "^7.8.3", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", + "@babel/plugin-syntax-numeric-separator": "^7.8.3", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", + "@babel/plugin-syntax-optional-chaining": "^7.8.3", + "@babel/plugin-syntax-top-level-await": "^7.8.3" + } + }, + "babel-preset-jest": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-26.6.2.tgz", + "integrity": "sha512-YvdtlVm9t3k777c5NPQIv6cxFFFapys25HiUmuSgHwIZhfifweR5c5Sf5nwE3MAbfu327CYSvps8Yx6ANLyleQ==", + "requires": { + "babel-plugin-jest-hoist": "^26.6.2", + "babel-preset-current-node-syntax": "^1.0.0" + } + }, + "babel-preset-react-app": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/babel-preset-react-app/-/babel-preset-react-app-10.0.0.tgz", + "integrity": "sha512-itL2z8v16khpuKutx5IH8UdCdSTuzrOhRFTEdIhveZ2i1iBKDrVE0ATa4sFVy+02GLucZNVBWtoarXBy0Msdpg==", + "requires": { + "@babel/core": "7.12.3", + "@babel/plugin-proposal-class-properties": "7.12.1", + "@babel/plugin-proposal-decorators": "7.12.1", + "@babel/plugin-proposal-nullish-coalescing-operator": "7.12.1", + "@babel/plugin-proposal-numeric-separator": "7.12.1", + "@babel/plugin-proposal-optional-chaining": "7.12.1", + "@babel/plugin-transform-flow-strip-types": "7.12.1", + "@babel/plugin-transform-react-display-name": "7.12.1", + "@babel/plugin-transform-runtime": "7.12.1", + "@babel/preset-env": "7.12.1", + "@babel/preset-react": "7.12.1", + "@babel/preset-typescript": "7.12.1", + "@babel/runtime": "7.12.1", + "babel-plugin-macros": "2.8.0", + "babel-plugin-transform-react-remove-prop-types": "0.4.24" + }, + "dependencies": { + "@babel/plugin-proposal-class-properties": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.12.1.tgz", + "integrity": "sha512-cKp3dlQsFsEs5CWKnN7BnSHOd0EOW8EKpEjkoz1pO2E5KzIDNV9Ros1b0CnmbVgAGXJubOYVBOGCT1OmJwOI7w==", + "requires": { + "@babel/helper-create-class-features-plugin": "^7.12.1", + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-proposal-nullish-coalescing-operator": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.12.1.tgz", + "integrity": "sha512-nZY0ESiaQDI1y96+jk6VxMOaL4LPo/QDHBqL+SF3/vl6dHkTwHlOI8L4ZwuRBHgakRBw5zsVylel7QPbbGuYgg==", + "requires": { + "@babel/helper-plugin-utils": "^7.10.4", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.0" + } + }, + "@babel/plugin-proposal-numeric-separator": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.12.1.tgz", + "integrity": "sha512-MR7Ok+Af3OhNTCxYVjJZHS0t97ydnJZt/DbR4WISO39iDnhiD8XHrY12xuSJ90FFEGjir0Fzyyn7g/zY6hxbxA==", + "requires": { + "@babel/helper-plugin-utils": "^7.10.4", + "@babel/plugin-syntax-numeric-separator": "^7.10.4" + } + }, + "@babel/plugin-proposal-optional-chaining": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.12.1.tgz", + "integrity": "sha512-c2uRpY6WzaVDzynVY9liyykS+kVU+WRZPMPYpkelXH8KBt1oXoI89kPbZKKG/jDT5UK92FTW2fZkZaJhdiBabw==", + "requires": { + "@babel/helper-plugin-utils": "^7.10.4", + "@babel/helper-skip-transparent-expression-wrappers": "^7.12.1", + "@babel/plugin-syntax-optional-chaining": "^7.8.0" + } + }, + "@babel/plugin-transform-react-display-name": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.12.1.tgz", + "integrity": "sha512-cAzB+UzBIrekfYxyLlFqf/OagTvHLcVBb5vpouzkYkBclRPraiygVnafvAoipErZLI8ANv8Ecn6E/m5qPXD26w==", + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/preset-env": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.12.1.tgz", + "integrity": "sha512-H8kxXmtPaAGT7TyBvSSkoSTUK6RHh61So05SyEbpmr0MCZrsNYn7mGMzzeYoOUCdHzww61k8XBft2TaES+xPLg==", + "requires": { + "@babel/compat-data": "^7.12.1", + "@babel/helper-compilation-targets": "^7.12.1", + "@babel/helper-module-imports": "^7.12.1", + "@babel/helper-plugin-utils": "^7.10.4", + "@babel/helper-validator-option": "^7.12.1", + "@babel/plugin-proposal-async-generator-functions": "^7.12.1", + "@babel/plugin-proposal-class-properties": "^7.12.1", + "@babel/plugin-proposal-dynamic-import": "^7.12.1", + "@babel/plugin-proposal-export-namespace-from": "^7.12.1", + "@babel/plugin-proposal-json-strings": "^7.12.1", + "@babel/plugin-proposal-logical-assignment-operators": "^7.12.1", + "@babel/plugin-proposal-nullish-coalescing-operator": "^7.12.1", + "@babel/plugin-proposal-numeric-separator": "^7.12.1", + "@babel/plugin-proposal-object-rest-spread": "^7.12.1", + "@babel/plugin-proposal-optional-catch-binding": "^7.12.1", + "@babel/plugin-proposal-optional-chaining": "^7.12.1", + "@babel/plugin-proposal-private-methods": "^7.12.1", + "@babel/plugin-proposal-unicode-property-regex": "^7.12.1", + "@babel/plugin-syntax-async-generators": "^7.8.0", + "@babel/plugin-syntax-class-properties": "^7.12.1", + "@babel/plugin-syntax-dynamic-import": "^7.8.0", + "@babel/plugin-syntax-export-namespace-from": "^7.8.3", + "@babel/plugin-syntax-json-strings": "^7.8.0", + "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.0", + "@babel/plugin-syntax-numeric-separator": "^7.10.4", + "@babel/plugin-syntax-object-rest-spread": "^7.8.0", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.0", + "@babel/plugin-syntax-optional-chaining": "^7.8.0", + "@babel/plugin-syntax-top-level-await": "^7.12.1", + "@babel/plugin-transform-arrow-functions": "^7.12.1", + "@babel/plugin-transform-async-to-generator": "^7.12.1", + "@babel/plugin-transform-block-scoped-functions": "^7.12.1", + "@babel/plugin-transform-block-scoping": "^7.12.1", + "@babel/plugin-transform-classes": "^7.12.1", + "@babel/plugin-transform-computed-properties": "^7.12.1", + "@babel/plugin-transform-destructuring": "^7.12.1", + "@babel/plugin-transform-dotall-regex": "^7.12.1", + "@babel/plugin-transform-duplicate-keys": "^7.12.1", + "@babel/plugin-transform-exponentiation-operator": "^7.12.1", + "@babel/plugin-transform-for-of": "^7.12.1", + "@babel/plugin-transform-function-name": "^7.12.1", + "@babel/plugin-transform-literals": "^7.12.1", + "@babel/plugin-transform-member-expression-literals": "^7.12.1", + "@babel/plugin-transform-modules-amd": "^7.12.1", + "@babel/plugin-transform-modules-commonjs": "^7.12.1", + "@babel/plugin-transform-modules-systemjs": "^7.12.1", + "@babel/plugin-transform-modules-umd": "^7.12.1", + "@babel/plugin-transform-named-capturing-groups-regex": "^7.12.1", + "@babel/plugin-transform-new-target": "^7.12.1", + "@babel/plugin-transform-object-super": "^7.12.1", + "@babel/plugin-transform-parameters": "^7.12.1", + "@babel/plugin-transform-property-literals": "^7.12.1", + "@babel/plugin-transform-regenerator": "^7.12.1", + "@babel/plugin-transform-reserved-words": "^7.12.1", + "@babel/plugin-transform-shorthand-properties": "^7.12.1", + "@babel/plugin-transform-spread": "^7.12.1", + "@babel/plugin-transform-sticky-regex": "^7.12.1", + "@babel/plugin-transform-template-literals": "^7.12.1", + "@babel/plugin-transform-typeof-symbol": "^7.12.1", + "@babel/plugin-transform-unicode-escapes": "^7.12.1", + "@babel/plugin-transform-unicode-regex": "^7.12.1", + "@babel/preset-modules": "^0.1.3", + "@babel/types": "^7.12.1", + "core-js-compat": "^3.6.2", + "semver": "^5.5.0" + } + }, + "@babel/preset-react": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/preset-react/-/preset-react-7.12.1.tgz", + "integrity": "sha512-euCExymHCi0qB9u5fKw7rvlw7AZSjw/NaB9h7EkdTt5+yHRrXdiRTh7fkG3uBPpJg82CqLfp1LHLqWGSCrab+g==", + "requires": { + "@babel/helper-plugin-utils": "^7.10.4", + "@babel/plugin-transform-react-display-name": "^7.12.1", + "@babel/plugin-transform-react-jsx": "^7.12.1", + "@babel/plugin-transform-react-jsx-development": "^7.12.1", + "@babel/plugin-transform-react-jsx-self": "^7.12.1", + "@babel/plugin-transform-react-jsx-source": "^7.12.1", + "@babel/plugin-transform-react-pure-annotations": "^7.12.1" + } + }, + "@babel/runtime": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.12.1.tgz", + "integrity": "sha512-J5AIf3vPj3UwXaAzb5j1xM4WAQDX3EMgemF8rjCP3SoW09LfRKAXQKt6CoVYl230P6iWdRcBbnLDDdnqWxZSCA==", + "requires": { + "regenerator-runtime": "^0.13.4" + } + }, + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" + } + } + }, + "babel-runtime": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz", + "integrity": "sha512-ITKNuq2wKlW1fJg9sSW52eepoYgZBggvOAHC0u/CYu/qxQ9EVzThCgR69BnSXLHjy2f7SY5zaQ4yt7H9ZVxY2g==", + "requires": { + "core-js": "^2.4.0", + "regenerator-runtime": "^0.11.0" + }, + "dependencies": { + "core-js": { + "version": "2.6.12", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.12.tgz", + "integrity": "sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ==" + }, + "regenerator-runtime": { + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz", + "integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==" + } + } + }, + "babylon": { + "version": "6.18.0", + "resolved": "https://registry.npmjs.org/babylon/-/babylon-6.18.0.tgz", + "integrity": "sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ==" + }, + "balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" + }, + "base": { + "version": "0.11.2", + "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", + "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", + "requires": { + "cache-base": "^1.0.1", + "class-utils": "^0.3.5", + "component-emitter": "^1.2.1", + "define-property": "^1.0.0", + "isobject": "^3.0.1", + "mixin-deep": "^1.2.0", + "pascalcase": "^0.1.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha512-cZTYKFWspt9jZsMscWo8sc/5lbPC9Q0N5nBLgb+Yd915iL3udB1uFgS3B8YCx66UVHq018DAVFoee7x+gxggeA==", + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } + } + }, + "base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==" + }, + "batch": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz", + "integrity": "sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw==" + }, + "bcrypt-pbkdf": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", + "integrity": "sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==", + "requires": { + "tweetnacl": "^0.14.3" + } + }, + "bfj": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/bfj/-/bfj-7.0.2.tgz", + "integrity": "sha512-+e/UqUzwmzJamNF50tBV6tZPTORow7gQ96iFow+8b562OdMpEK0BcJEq2OSPEDmAbSMBQ7PKZ87ubFkgxpYWgw==", + "requires": { + "bluebird": "^3.5.5", + "check-types": "^11.1.1", + "hoopy": "^0.1.4", + "tryer": "^1.0.1" + } + }, + "big.js": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", + "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==" + }, + "binary-extensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "optional": true + }, + "bindings": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", + "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", + "optional": true, + "requires": { + "file-uri-to-path": "1.0.0" + } + }, + "block-stream": { + "version": "0.0.9", + "resolved": "https://registry.npmjs.org/block-stream/-/block-stream-0.0.9.tgz", + "integrity": "sha512-OorbnJVPII4DuUKbjARAe8u8EfqOmkEEaSFIyoQ7OjTHn6kafxWl0wLgoZ2rXaYd7MyLcDaU4TmhfxtwgcccMQ==", + "dev": true, + "requires": { + "inherits": "~2.0.0" + } + }, + "bluebird": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", + "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==" + }, + "bn.js": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.0.tgz", + "integrity": "sha512-D7iWRBvnZE8ecXiLj/9wbxH7Tk79fAh8IHaTNq1RWRixsS02W+5qS+iE9yq6RYl0asXx5tw0bLhmT5pIfbSquw==" + }, + "body-parser": { + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", + "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==", + "requires": { + "bytes": "3.1.0", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "~1.1.2", + "http-errors": "1.7.2", + "iconv-lite": "0.4.24", + "on-finished": "~2.3.0", + "qs": "6.7.0", + "raw-body": "2.4.0", + "type-is": "~1.6.17" + }, + "dependencies": { + "bytes": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", + "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==" + }, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + }, + "qs": { + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", + "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==" + } + } + }, + "bonjour": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/bonjour/-/bonjour-3.5.0.tgz", + "integrity": "sha512-RaVTblr+OnEli0r/ud8InrU7D+G0y6aJhlxaLa6Pwty4+xoxboF1BsUI45tujvRpbj9dQVoglChqonGAsjEBYg==", + "requires": { + "array-flatten": "^2.1.0", + "deep-equal": "^1.0.1", + "dns-equal": "^1.0.0", + "dns-txt": "^2.0.2", + "multicast-dns": "^6.0.1", + "multicast-dns-service-types": "^1.1.0" + } + }, + "boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==" + }, + "bootstrap": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-4.6.0.tgz", + "integrity": "sha512-Io55IuQY3kydzHtbGvQya3H+KorS/M9rSNyfCGCg9WZ4pyT/lCxIlpJgG1GXW/PswzC84Tr2fBYi+7+jFVQQBw==" + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "requires": { + "fill-range": "^7.0.1" + } + }, + "brorand": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", + "integrity": "sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w==" + }, + "browser-process-hrtime": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz", + "integrity": "sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow==" + }, + "browserify-aes": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", + "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==", + "requires": { + "buffer-xor": "^1.0.3", + "cipher-base": "^1.0.0", + "create-hash": "^1.1.0", + "evp_bytestokey": "^1.0.3", + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "browserify-cipher": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.1.tgz", + "integrity": "sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==", + "requires": { + "browserify-aes": "^1.0.4", + "browserify-des": "^1.0.0", + "evp_bytestokey": "^1.0.0" + } + }, + "browserify-des": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.2.tgz", + "integrity": "sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A==", + "requires": { + "cipher-base": "^1.0.1", + "des.js": "^1.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "browserify-rsa": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.1.0.tgz", + "integrity": "sha512-AdEER0Hkspgno2aR97SAf6vi0y0k8NuOpGnVH3O99rcA5Q6sh8QxcngtHuJ6uXwnfAXNM4Gn1Gb7/MV1+Ymbog==", + "requires": { + "bn.js": "^5.0.0", + "randombytes": "^2.0.1" + } + }, + "browserify-sign": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.2.1.tgz", + "integrity": "sha512-/vrA5fguVAKKAVTNJjgSm1tRQDHUU6DbwO9IROu/0WAzC8PKhucDSh18J0RMvVeHAn5puMd+QHC2erPRNf8lmg==", + "requires": { + "bn.js": "^5.1.1", + "browserify-rsa": "^4.0.1", + "create-hash": "^1.2.0", + "create-hmac": "^1.1.7", + "elliptic": "^6.5.3", + "inherits": "^2.0.4", + "parse-asn1": "^5.1.5", + "readable-stream": "^3.6.0", + "safe-buffer": "^5.2.0" + }, + "dependencies": { + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + }, + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" + } + } + }, + "browserify-zlib": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.2.0.tgz", + "integrity": "sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==", + "requires": { + "pako": "~1.0.5" + } + }, + "browserslist": { + "version": "4.16.5", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.16.5.tgz", + "integrity": "sha512-C2HAjrM1AI/djrpAUU/tr4pml1DqLIzJKSLDBXBrNErl9ZCCTXdhwxdJjYc16953+mBWf7Lw+uUJgpgb8cN71A==", + "requires": { + "caniuse-lite": "^1.0.30001214", + "colorette": "^1.2.2", + "electron-to-chromium": "^1.3.719", + "escalade": "^3.1.1", + "node-releases": "^1.1.71" + } + }, + "bs-logger": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/bs-logger/-/bs-logger-0.2.6.tgz", + "integrity": "sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==", + "dev": true, + "requires": { + "fast-json-stable-stringify": "2.x" + } + }, + "bser": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", + "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", + "requires": { + "node-int64": "^0.4.0" + } + }, + "buffer": { + "version": "4.9.2", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.2.tgz", + "integrity": "sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==", + "requires": { + "base64-js": "^1.0.2", + "ieee754": "^1.1.4", + "isarray": "^1.0.0" + } + }, + "buffer-from": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", + "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==" + }, + "buffer-indexof": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/buffer-indexof/-/buffer-indexof-1.1.1.tgz", + "integrity": "sha512-4/rOEg86jivtPTeOUUT61jJO1Ya1TrR/OkqCSZDyq84WJh3LuuiphBYJN+fm5xufIk4XAFcEwte/8WzC8If/1g==" + }, + "buffer-xor": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", + "integrity": "sha512-571s0T7nZWK6vB67HI5dyUF7wXiNcfaPPPTl6zYCNApANjIvYJTg7hlud/+cJpdAhS7dVzqMLmfhfHR3rAcOjQ==" + }, + "builtin-modules": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.2.0.tgz", + "integrity": "sha512-lGzLKcioL90C7wMczpkY0n/oART3MbBa8R9OFGE1rJxoVI86u4WAGfEk8Wjv10eKSyTHVGkSo3bvBylCEtk7LA==" + }, + "builtin-status-codes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz", + "integrity": "sha512-HpGFw18DgFWlncDfjTa2rcQ4W88O1mC8e8yZ2AvQY5KDaktSTwo+KRf6nHK6FRI5FyRyb/5T6+TSxfP7QyGsmQ==" + }, + "bytes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", + "integrity": "sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw==" + }, + "cacache": { + "version": "15.0.6", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-15.0.6.tgz", + "integrity": "sha512-g1WYDMct/jzW+JdWEyjaX2zoBkZ6ZT9VpOyp2I/VMtDsNLffNat3kqPFfi1eDRSK9/SuKGyORDHcQMcPF8sQ/w==", + "requires": { + "@npmcli/move-file": "^1.0.1", + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "glob": "^7.1.4", + "infer-owner": "^1.0.4", + "lru-cache": "^6.0.0", + "minipass": "^3.1.1", + "minipass-collect": "^1.0.2", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.2", + "mkdirp": "^1.0.3", + "p-map": "^4.0.0", + "promise-inflight": "^1.0.1", + "rimraf": "^3.0.2", + "ssri": "^8.0.1", + "tar": "^6.0.2", + "unique-filename": "^1.1.1" + }, + "dependencies": { + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "requires": { + "yallist": "^4.0.0" + } + }, + "mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==" + }, + "tar": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.0.tgz", + "integrity": "sha512-DUCttfhsnLCjwoDoFcI+B2iJgYa93vBnDUATYEeRx6sntCTdN01VnqsIuTlALXla/LWooNg0yEGeB+Y8WdFxGA==", + "requires": { + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^3.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" + } + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + } + } + }, + "cache-base": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", + "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", + "requires": { + "collection-visit": "^1.0.0", + "component-emitter": "^1.2.1", + "get-value": "^2.0.6", + "has-value": "^1.0.0", + "isobject": "^3.0.1", + "set-value": "^2.0.0", + "to-object-path": "^0.3.0", + "union-value": "^1.0.0", + "unset-value": "^1.0.0" + } + }, + "cachedir": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/cachedir/-/cachedir-2.2.0.tgz", + "integrity": "sha512-VvxA0xhNqIIfg0V9AmJkDg91DaJwryutH5rVEZAhcNi4iJFj9f+QxmAjgK1LT9I8OgToX27fypX6/MeCXVbBjQ==", + "dev": true + }, + "call-bind": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", + "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "requires": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.2" + } + }, + "caller-callsite": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/caller-callsite/-/caller-callsite-2.0.0.tgz", + "integrity": "sha512-JuG3qI4QOftFsZyOn1qq87fq5grLIyk1JYd5lJmdA+fG7aQ9pA/i3JIJGcO3q0MrRcHlOt1U+ZeHW8Dq9axALQ==", + "requires": { + "callsites": "^2.0.0" + }, + "dependencies": { + "callsites": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-2.0.0.tgz", + "integrity": "sha512-ksWePWBloaWPxJYQ8TL0JHvtci6G5QTKwQ95RcWAa/lzoAKuAOflGdAK92hpHXjkwb8zLxoLNUoNYZgVsaJzvQ==" + } + } + }, + "caller-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/caller-path/-/caller-path-2.0.0.tgz", + "integrity": "sha512-MCL3sf6nCSXOwCTzvPKhN18TU7AHTvdtam8DAogxcrJ8Rjfbbg7Lgng64H9Iy+vUV6VGFClN/TyxBkAebLRR4A==", + "requires": { + "caller-callsite": "^2.0.0" + } + }, + "callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==" + }, + "camel-case": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-4.1.2.tgz", + "integrity": "sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==", + "requires": { + "pascal-case": "^3.1.2", + "tslib": "^2.0.3" + }, + "dependencies": { + "tslib": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.2.0.tgz", + "integrity": "sha512-gS9GVHRU+RGn5KQM2rllAlR3dU6m7AcpJKdtH8gFvQiC4Otgk98XnmMU+nZenHt/+VhnBPWwgrJsyrdcw6i23w==" + } + } + }, + "camelcase": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.2.0.tgz", + "integrity": "sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg==" + }, + "camelcase-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-2.1.0.tgz", + "integrity": "sha512-bA/Z/DERHKqoEOrp+qeGKw1QlvEQkGZSc0XaY6VnTxZr+Kv1G5zFwttpjv8qxZ/sBPT4nthwZaAcsAZTJlSKXQ==", + "dev": true, + "requires": { + "camelcase": "^2.0.0", + "map-obj": "^1.0.0" + }, + "dependencies": { + "camelcase": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz", + "integrity": "sha512-DLIsRzJVBQu72meAKPkWQOLcujdXT32hwdfnkI1frSiSRMK1MofjKHf+MEx0SB6fjEFXL8fBDv1dKymBlOp4Qw==", + "dev": true + } + } + }, + "caniuse-api": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-3.0.0.tgz", + "integrity": "sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==", + "requires": { + "browserslist": "^4.0.0", + "caniuse-lite": "^1.0.0", + "lodash.memoize": "^4.1.2", + "lodash.uniq": "^4.5.0" + } + }, + "caniuse-lite": { + "version": "1.0.30001219", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001219.tgz", + "integrity": "sha512-c0yixVG4v9KBc/tQ2rlbB3A/bgBFRvl8h8M4IeUbqCca4gsiCfvtaheUssbnux/Mb66Vjz7x8yYjDgYcNQOhyQ==" + }, + "capture-exit": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/capture-exit/-/capture-exit-2.0.0.tgz", + "integrity": "sha512-PiT/hQmTonHhl/HFGN+Lx3JJUznrVYJ3+AQsnthneZbvW7x+f08Tk7yLJTLEOUvBTbduLeeBkxEaYXUOUrRq6g==", + "requires": { + "rsvp": "^4.8.4" + } + }, + "case-sensitive-paths-webpack-plugin": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/case-sensitive-paths-webpack-plugin/-/case-sensitive-paths-webpack-plugin-2.3.0.tgz", + "integrity": "sha512-/4YgnZS8y1UXXmC02xD5rRrBEu6T5ub+mQHLNRj0fzTRbgdBYhsNo2V5EqwgqrExjxsjtF/OpAKAMkKsxbD5XQ==" + }, + "caseless": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==" + }, + "cfb": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/cfb/-/cfb-1.2.1.tgz", + "integrity": "sha512-wT2ScPAFGSVy7CY+aauMezZBnNrfnaLSrxHUHdea+Td/86vrk6ZquggV+ssBR88zNs0OnBkL2+lf9q0K+zVGzQ==", + "requires": { + "adler-32": "~1.3.0", + "crc-32": "~1.2.0", + "printj": "~1.3.0" + }, + "dependencies": { + "adler-32": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/adler-32/-/adler-32-1.3.0.tgz", + "integrity": "sha512-f5nltvjl+PRUh6YNfUstRaXwJxtfnKEWhAWWlmKvh+Y3J2+98a0KKVYDEhz6NdKGqswLhjNGznxfSsZGOvOd9g==", + "requires": { + "printj": "~1.2.2" + }, + "dependencies": { + "printj": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/printj/-/printj-1.2.3.tgz", + "integrity": "sha512-sanczS6xOJOg7IKDvi4sGOUOe7c1tsEzjwlLFH/zgwx/uyImVM9/rgBkc8AfiQa/Vg54nRd8mkm9yI7WV/O+WA==" + } + } + }, + "printj": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/printj/-/printj-1.3.0.tgz", + "integrity": "sha512-017o8YIaz8gLhaNxRB9eBv2mWXI2CtzhPJALnQTP+OPpuUfP0RMWqr/mHCzqVeu1AQxfzSfAtAq66vKB8y7Lzg==" + } + } + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "char-regex": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", + "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==" + }, + "chardet": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", + "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", + "dev": true + }, + "check-types": { + "version": "11.1.2", + "resolved": "https://registry.npmjs.org/check-types/-/check-types-11.1.2.tgz", + "integrity": "sha512-tzWzvgePgLORb9/3a0YenggReLKAIb2owL03H2Xdoe5pKcUyWRSEQ8xfCar8t2SIAuEDwtmx2da1YB52YuHQMQ==" + }, + "cheerio": { + "version": "1.0.0-rc.6", + "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0-rc.6.tgz", + "integrity": "sha512-hjx1XE1M/D5pAtMgvWwE21QClmAEeGHOIDfycgmndisdNgI6PE1cGRQkMGBcsbUbmEQyWu5PJLUcAOjtQS8DWw==", + "dev": true, + "requires": { + "cheerio-select": "^1.3.0", + "dom-serializer": "^1.3.1", + "domhandler": "^4.1.0", + "htmlparser2": "^6.1.0", + "parse5": "^6.0.1", + "parse5-htmlparser2-tree-adapter": "^6.0.1" + }, + "dependencies": { + "dom-serializer": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.3.1.tgz", + "integrity": "sha512-Pv2ZluG5ife96udGgEDovOOOA5UELkltfJpnIExPrAk1LTvecolUGn6lIaoLh86d83GiB86CjzciMd9BuRB71Q==", + "dev": true, + "requires": { + "domelementtype": "^2.0.1", + "domhandler": "^4.0.0", + "entities": "^2.0.0" + } + }, + "domelementtype": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.2.0.tgz", + "integrity": "sha512-DtBMo82pv1dFtUmHyr48beiuq792Sxohr+8Hm9zoxklYPfa6n0Z3Byjj2IV7bmr2IyqClnqEQhfgHJJ5QF0R5A==", + "dev": true + }, + "domhandler": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.2.0.tgz", + "integrity": "sha512-zk7sgt970kzPks2Bf+dwT/PLzghLnsivb9CcxkvR8Mzr66Olr0Ofd8neSbglHJHaHa2MadfoSdNlKYAaafmWfA==", + "dev": true, + "requires": { + "domelementtype": "^2.2.0" + } + }, + "domutils": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.6.0.tgz", + "integrity": "sha512-y0BezHuy4MDYxh6OvolXYsH+1EMGmFbwv5FKW7ovwMG6zTPWqNPq3WF9ayZssFq+UlKdffGLbOEaghNdaOm1WA==", + "dev": true, + "requires": { + "dom-serializer": "^1.0.1", + "domelementtype": "^2.2.0", + "domhandler": "^4.2.0" + } + }, + "htmlparser2": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-6.1.0.tgz", + "integrity": "sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A==", + "dev": true, + "requires": { + "domelementtype": "^2.0.1", + "domhandler": "^4.0.0", + "domutils": "^2.5.2", + "entities": "^2.0.0" + } + } + } + }, + "cheerio-select": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/cheerio-select/-/cheerio-select-1.4.0.tgz", + "integrity": "sha512-sobR3Yqz27L553Qa7cK6rtJlMDbiKPdNywtR95Sj/YgfpLfy0u6CGJuaBKe5YE/vTc23SCRKxWSdlon/w6I/Ew==", + "dev": true, + "requires": { + "css-select": "^4.1.2", + "css-what": "^5.0.0", + "domelementtype": "^2.2.0", + "domhandler": "^4.2.0", + "domutils": "^2.6.0" + }, + "dependencies": { + "css-select": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-4.1.2.tgz", + "integrity": "sha512-nu5ye2Hg/4ISq4XqdLY2bEatAcLIdt3OYGFc9Tm9n7VSlFBcfRv0gBNksHRgSdUDQGtN3XrZ94ztW+NfzkFSUw==", + "dev": true, + "requires": { + "boolbase": "^1.0.0", + "css-what": "^5.0.0", + "domhandler": "^4.2.0", + "domutils": "^2.6.0", + "nth-check": "^2.0.0" + } + }, + "css-what": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-5.0.0.tgz", + "integrity": "sha512-qxyKHQvgKwzwDWC/rGbT821eJalfupxYW2qbSJSAtdSTimsr/MlaGONoNLllaUPZWf8QnbcKM/kPVYUQuEKAFA==", + "dev": true + }, + "dom-serializer": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.3.1.tgz", + "integrity": "sha512-Pv2ZluG5ife96udGgEDovOOOA5UELkltfJpnIExPrAk1LTvecolUGn6lIaoLh86d83GiB86CjzciMd9BuRB71Q==", + "dev": true, + "requires": { + "domelementtype": "^2.0.1", + "domhandler": "^4.0.0", + "entities": "^2.0.0" + } + }, + "domelementtype": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.2.0.tgz", + "integrity": "sha512-DtBMo82pv1dFtUmHyr48beiuq792Sxohr+8Hm9zoxklYPfa6n0Z3Byjj2IV7bmr2IyqClnqEQhfgHJJ5QF0R5A==", + "dev": true + }, + "domhandler": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.2.0.tgz", + "integrity": "sha512-zk7sgt970kzPks2Bf+dwT/PLzghLnsivb9CcxkvR8Mzr66Olr0Ofd8neSbglHJHaHa2MadfoSdNlKYAaafmWfA==", + "dev": true, + "requires": { + "domelementtype": "^2.2.0" + } + }, + "domutils": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.6.0.tgz", + "integrity": "sha512-y0BezHuy4MDYxh6OvolXYsH+1EMGmFbwv5FKW7ovwMG6zTPWqNPq3WF9ayZssFq+UlKdffGLbOEaghNdaOm1WA==", + "dev": true, + "requires": { + "dom-serializer": "^1.0.1", + "domelementtype": "^2.2.0", + "domhandler": "^4.2.0" + } + }, + "nth-check": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.0.0.tgz", + "integrity": "sha512-i4sc/Kj8htBrAiH1viZ0TgU8Y5XqCaV/FziYK6TBczxmeKm3AEFWqqF3195yKudrarqy7Zu80Ra5dobFjn9X/Q==", + "dev": true, + "requires": { + "boolbase": "^1.0.0" + } + } + } + }, + "chokidar": { + "version": "3.5.1", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.1.tgz", + "integrity": "sha512-9+s+Od+W0VJJzawDma/gvBNQqkTiqYTWLuZoyAsivsI4AaWTCzHG06/TMjsf1cYe9Cb97UCEhjz7HvnPk2p/tw==", + "optional": true, + "requires": { + "anymatch": "~3.1.1", + "braces": "~3.0.2", + "fsevents": "~2.3.1", + "glob-parent": "~5.1.0", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.5.0" + } + }, + "chownr": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", + "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==" + }, + "chrome-trace-event": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz", + "integrity": "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==" + }, + "ci-info": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", + "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==" + }, + "cipher-base": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", + "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==", + "requires": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "cjs-module-lexer": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-0.6.0.tgz", + "integrity": "sha512-uc2Vix1frTfnuzxxu1Hp4ktSvM3QaI4oXl4ZUqL1wjTu/BGki9TrCWoqLTg/drR1KwAEarXuRFCG2Svr1GxPFw==" + }, + "class-utils": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", + "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==", + "requires": { + "arr-union": "^3.1.0", + "define-property": "^0.2.5", + "isobject": "^3.0.0", + "static-extend": "^0.1.1" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==", + "requires": { + "is-descriptor": "^0.1.0" + } + } + } + }, + "classnames": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.3.1.tgz", + "integrity": "sha512-OlQdbZ7gLfGarSqxesMesDa5uz7KFbID8Kpq/SxIoNGDqY8lSYs0D+hhtBXhcdB3rcbXArFr7vlHheLk1voeNA==" + }, + "clean-css": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-4.2.3.tgz", + "integrity": "sha512-VcMWDN54ZN/DS+g58HYL5/n4Zrqe8vHJpGA8KdgUXFU4fuP/aHNw8eld9SyEIyabIMJX/0RaY/fplOo5hYLSFA==", + "requires": { + "source-map": "~0.6.0" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + } + } + }, + "clean-stack": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", + "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==" + }, + "cli-cursor": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz", + "integrity": "sha512-8lgKz8LmCRYZZQDpRyT2m5rKJ08TnU4tR9FFFW2rxpxR1FzWi4PQ/NfyODchAatHaUgnSPVcx/R5w6NuTBzFiw==", + "dev": true, + "requires": { + "restore-cursor": "^2.0.0" + } + }, + "cli-width": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.1.tgz", + "integrity": "sha512-GRMWDxpOB6Dgk2E5Uo+3eEBvtOOlimMmpbFiKuLFnQzYDavtLFY3K5ona41jgN/WdRZtG7utuVSVTL4HbZHGkw==", + "dev": true + }, + "cliui": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", + "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", + "requires": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^6.2.0" + } + }, + "clsx": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-1.1.1.tgz", + "integrity": "sha512-6/bPho624p3S2pMyvP5kKBPXnI3ufHLObBFCfgx+LkeR5lg2XYy2hqZqUf45ypD8COn2bhgGJSUE+l5dhNBieA==" + }, + "co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==" + }, + "coa": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/coa/-/coa-2.0.2.tgz", + "integrity": "sha512-q5/jG+YQnSy4nRTV4F7lPepBJZ8qBNJJDBuJdoejDyLXgmL7IEo+Le2JDZudFTFt7mrCqIRaSjws4ygRCTCAXA==", + "requires": { + "@types/q": "^1.5.1", + "chalk": "^2.4.1", + "q": "^1.1.2" + } + }, + "code-point-at": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", + "integrity": "sha512-RpAVKQA5T63xEj6/giIbUEtZwJ4UFIc3ZtvEkiaUERylqe8xb5IvqcgOurZLahv93CLKfxcw5YI+DZcUBRyLXA==", + "dev": true + }, + "codepage": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/codepage/-/codepage-1.15.0.tgz", + "integrity": "sha512-3g6NUTPd/YtuuGrhMnOMRjFc+LJw/bnMp3+0r/Wcz3IXUuCosKRJvMphm5+Q+bvTVGcJJuRvVLuYba+WojaFaA==" + }, + "collect-v8-coverage": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz", + "integrity": "sha512-iBPtljfCNcTKNAto0KEtDfZ3qzjJvqE3aTGZsbhjSBlorqpXJlaWWtPO35D+ZImoC3KWejX64o+yPGxhWSTzfg==" + }, + "collection-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", + "integrity": "sha512-lNkKvzEeMBBjUGHZ+q6z9pSJla0KWAQPvtzhEV9+iGyQYG+pBpl7xKDhxoNSOZH2hhv0v5k0y2yAM4o4SjoSkw==", + "requires": { + "map-visit": "^1.0.0", + "object-visit": "^1.0.0" + } + }, + "color": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/color/-/color-3.1.3.tgz", + "integrity": "sha512-xgXAcTHa2HeFCGLE9Xs/R82hujGtu9Jd9x4NW3T34+OMs7VoPsjwzRczKHvTAHeJwWFwX5j15+MgAppE8ztObQ==", + "requires": { + "color-convert": "^1.9.1", + "color-string": "^1.5.4" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" + }, + "color-string": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.5.5.tgz", + "integrity": "sha512-jgIoum0OfQfq9Whcfc2z/VhCNcmQjWbey6qBX0vqt7YICflUmBCh9E9CiQD5GSJ+Uehixm3NUwHVhqUAWRivZg==", + "requires": { + "color-name": "^1.0.0", + "simple-swizzle": "^0.2.2" + } + }, + "colorette": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.2.2.tgz", + "integrity": "sha512-MKGMzyfeuutC/ZJ1cba9NqcNpfeqMUcYmyF1ZFY6/Cn7CNSAKx6a+s48sqLqyAiZuaP2TcqMhoo+dlwFnVxT9w==" + }, + "colors": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/colors/-/colors-0.6.2.tgz", + "integrity": "sha512-OsSVtHK8Ir8r3+Fxw/b4jS1ZLPXkV6ZxDRJQzeD7qo0SqMXWrHDM71DgYzPMHY8SFJ0Ao+nNU2p1MmwdzKqPrw==", + "dev": true + }, + "combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "requires": { + "delayed-stream": "~1.0.0" + } + }, + "commander": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", + "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==" + }, + "commitizen": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/commitizen/-/commitizen-4.2.3.tgz", + "integrity": "sha512-pYlYEng7XMV2TW4xtjDKBGqeJ0Teq2zyRSx2S3Ml1XAplHSlJZK8vm1KdGclpMEZuGafbS5TeHXIVnHk8RWIzQ==", + "dev": true, + "requires": { + "cachedir": "2.2.0", + "cz-conventional-changelog": "3.2.0", + "dedent": "0.7.0", + "detect-indent": "6.0.0", + "find-node-modules": "2.0.0", + "find-root": "1.1.0", + "fs-extra": "8.1.0", + "glob": "7.1.4", + "inquirer": "6.5.2", + "is-utf8": "^0.2.1", + "lodash": "^4.17.20", + "minimist": "1.2.5", + "strip-bom": "4.0.0", + "strip-json-comments": "3.0.1" + }, + "dependencies": { + "cz-conventional-changelog": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/cz-conventional-changelog/-/cz-conventional-changelog-3.2.0.tgz", + "integrity": "sha512-yAYxeGpVi27hqIilG1nh4A9Bnx4J3Ov+eXy4koL3drrR+IO9GaWPsKjik20ht608Asqi8TQPf0mczhEeyAtMzg==", + "dev": true, + "requires": { + "@commitlint/load": ">6.1.1", + "chalk": "^2.4.1", + "commitizen": "^4.0.3", + "conventional-commit-types": "^3.0.0", + "lodash.map": "^4.5.1", + "longest": "^2.0.1", + "word-wrap": "^1.0.3" + } + }, + "fs-extra": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", + "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", + "dev": true, + "requires": { + "graceful-fs": "^4.2.0", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + } + }, + "glob": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.4.tgz", + "integrity": "sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.6" + } + }, + "strip-bom": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", + "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", + "dev": true + }, + "strip-json-comments": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.0.1.tgz", + "integrity": "sha512-VTyMAUfdm047mwKl+u79WIdrZxtFtn+nBxHeb844XBQ9uMNTuTHdx2hc5RiAJYqwTj3wc/xe5HLSdJSkJ+WfZw==", + "dev": true + }, + "universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "dev": true + } + } + }, + "common-tags": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/common-tags/-/common-tags-1.8.0.tgz", + "integrity": "sha512-6P6g0uetGpW/sdyUy/iQQCbFF0kWVMSIVSyYz7Zgjcgh8mgw8PQzDNZeyZ5DQ2gM7LBoZPHmnjz8rUthkBG5tw==" + }, + "commondir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", + "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==" + }, + "compare-func": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/compare-func/-/compare-func-2.0.0.tgz", + "integrity": "sha512-zHig5N+tPWARooBnb0Zx1MFcdfpyJrfTJ3Y5L+IFvUm8rM74hHz66z0gw0x4tijh5CorKkKUCnW82R2vmpeCRA==", + "dev": true, + "requires": { + "array-ify": "^1.0.0", + "dot-prop": "^5.1.0" + } + }, + "component-emitter": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", + "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==" + }, + "compose-function": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/compose-function/-/compose-function-3.0.3.tgz", + "integrity": "sha512-xzhzTJ5eC+gmIzvZq+C3kCJHsp9os6tJkrigDRZclyGtOKINbZtE8n1Tzmeh32jW+BUDPbvZpibwvJHBLGMVwg==", + "requires": { + "arity-n": "^1.0.4" + } + }, + "compressible": { + "version": "2.0.18", + "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", + "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==", + "requires": { + "mime-db": ">= 1.43.0 < 2" + } + }, + "compression": { + "version": "1.7.4", + "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.4.tgz", + "integrity": "sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==", + "requires": { + "accepts": "~1.3.5", + "bytes": "3.0.0", + "compressible": "~2.0.16", + "debug": "2.6.9", + "on-headers": "~1.0.2", + "safe-buffer": "5.1.2", + "vary": "~1.1.2" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + } + } + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" + }, + "concat-stream": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", + "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", + "requires": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^2.2.2", + "typedarray": "^0.0.6" + } + }, + "confusing-browser-globals": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/confusing-browser-globals/-/confusing-browser-globals-1.0.10.tgz", + "integrity": "sha512-gNld/3lySHwuhaVluJUKLePYirM3QNCKzVxqAdhJII9/WXKVX5PURzMVJspS1jTslSqjeuG4KMVTSouit5YPHA==" + }, + "connect-history-api-fallback": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-1.6.0.tgz", + "integrity": "sha512-e54B99q/OUoH64zYYRf3HBP5z24G38h5D3qXu23JGRoigpX5Ss4r9ZnDk3g0Z8uQC2x2lPaJ+UlWBc1ZWBWdLg==" + }, + "connected-react-router": { + "version": "6.9.1", + "resolved": "https://registry.npmjs.org/connected-react-router/-/connected-react-router-6.9.1.tgz", + "integrity": "sha512-BbtB6t0iqAwGwygDenJl9zmlk7vpKWIRSycULmkAOn2RUaF6+bqETprl0qcIqQmY5CTqSwKanaxkLXYWiffAfQ==", + "requires": { + "immutable": "^3.8.1 || ^4.0.0-rc.1", + "lodash.isequalwith": "^4.4.0", + "prop-types": "^15.7.2", + "seamless-immutable": "^7.1.3" + } + }, + "console-browserify": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.2.0.tgz", + "integrity": "sha512-ZMkYO/LkF17QvCPqM0gxw8yUzigAOZOSWSHg91FH6orS7vcEj5dVZTidN2fQ14yBSdg97RqhSNwLUXInd52OTA==" + }, + "console-control-strings": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", + "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==", + "dev": true + }, + "constants-browserify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz", + "integrity": "sha512-xFxOwqIzR/e1k1gLiWEophSCMqXcwVHIH7akf7b/vxcUeGunlj3hvZaaqxwHsTgn+IndtkQJgSztIDWeumWJDQ==" + }, + "contains-path": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/contains-path/-/contains-path-0.1.0.tgz", + "integrity": "sha512-OKZnPGeMQy2RPaUIBPFFd71iNf4791H12MCRuVQDnzGRwCYNYmTDy5pdafo2SLAcEMKzTOQnLWG4QdcjeJUMEg==" + }, + "content-disposition": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", + "integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==", + "requires": { + "safe-buffer": "5.1.2" + } + }, + "content-type": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", + "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" + }, + "conventional-changelog": { + "version": "3.1.24", + "resolved": "https://registry.npmjs.org/conventional-changelog/-/conventional-changelog-3.1.24.tgz", + "integrity": "sha512-ed6k8PO00UVvhExYohroVPXcOJ/K1N0/drJHx/faTH37OIZthlecuLIRX/T6uOp682CAoVoFpu+sSEaeuH6Asg==", + "dev": true, + "requires": { + "conventional-changelog-angular": "^5.0.12", + "conventional-changelog-atom": "^2.0.8", + "conventional-changelog-codemirror": "^2.0.8", + "conventional-changelog-conventionalcommits": "^4.5.0", + "conventional-changelog-core": "^4.2.1", + "conventional-changelog-ember": "^2.0.9", + "conventional-changelog-eslint": "^3.0.9", + "conventional-changelog-express": "^2.0.6", + "conventional-changelog-jquery": "^3.0.11", + "conventional-changelog-jshint": "^2.0.9", + "conventional-changelog-preset-loader": "^2.3.4" + } + }, + "conventional-changelog-angular": { + "version": "5.0.12", + "resolved": "https://registry.npmjs.org/conventional-changelog-angular/-/conventional-changelog-angular-5.0.12.tgz", + "integrity": "sha512-5GLsbnkR/7A89RyHLvvoExbiGbd9xKdKqDTrArnPbOqBqG/2wIosu0fHwpeIRI8Tl94MhVNBXcLJZl92ZQ5USw==", + "dev": true, + "requires": { + "compare-func": "^2.0.0", + "q": "^1.5.1" + } + }, + "conventional-changelog-atom": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/conventional-changelog-atom/-/conventional-changelog-atom-2.0.8.tgz", + "integrity": "sha512-xo6v46icsFTK3bb7dY/8m2qvc8sZemRgdqLb/bjpBsH2UyOS8rKNTgcb5025Hri6IpANPApbXMg15QLb1LJpBw==", + "dev": true, + "requires": { + "q": "^1.5.1" + } + }, + "conventional-changelog-codemirror": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/conventional-changelog-codemirror/-/conventional-changelog-codemirror-2.0.8.tgz", + "integrity": "sha512-z5DAsn3uj1Vfp7po3gpt2Boc+Bdwmw2++ZHa5Ak9k0UKsYAO5mH1UBTN0qSCuJZREIhX6WU4E1p3IW2oRCNzQw==", + "dev": true, + "requires": { + "q": "^1.5.1" + } + }, + "conventional-changelog-config-spec": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/conventional-changelog-config-spec/-/conventional-changelog-config-spec-2.1.0.tgz", + "integrity": "sha512-IpVePh16EbbB02V+UA+HQnnPIohgXvJRxHcS5+Uwk4AT5LjzCZJm5sp/yqs5C6KZJ1jMsV4paEV13BN1pvDuxQ==", + "dev": true + }, + "conventional-changelog-conventionalcommits": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/conventional-changelog-conventionalcommits/-/conventional-changelog-conventionalcommits-4.5.0.tgz", + "integrity": "sha512-buge9xDvjjOxJlyxUnar/+6i/aVEVGA7EEh4OafBCXPlLUQPGbRUBhBUveWRxzvR8TEjhKEP4BdepnpG2FSZXw==", + "dev": true, + "requires": { + "compare-func": "^2.0.0", + "lodash": "^4.17.15", + "q": "^1.5.1" + } + }, + "conventional-changelog-core": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/conventional-changelog-core/-/conventional-changelog-core-4.2.2.tgz", + "integrity": "sha512-7pDpRUiobQDNkwHyJG7k9f6maPo9tfPzkSWbRq97GGiZqisElhnvUZSvyQH20ogfOjntB5aadvv6NNcKL1sReg==", + "dev": true, + "requires": { + "add-stream": "^1.0.0", + "conventional-changelog-writer": "^4.0.18", + "conventional-commits-parser": "^3.2.0", + "dateformat": "^3.0.0", + "get-pkg-repo": "^1.0.0", + "git-raw-commits": "^2.0.8", + "git-remote-origin-url": "^2.0.0", + "git-semver-tags": "^4.1.1", + "lodash": "^4.17.15", + "normalize-package-data": "^3.0.0", + "q": "^1.5.1", + "read-pkg": "^3.0.0", + "read-pkg-up": "^3.0.0", + "shelljs": "^0.8.3", + "through2": "^4.0.0" + }, + "dependencies": { + "find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", + "dev": true, + "requires": { + "locate-path": "^2.0.0" + } + }, + "hosted-git-info": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.0.2.tgz", + "integrity": "sha512-c9OGXbZ3guC/xOlCg1Ci/VgWlwsqDv1yMQL1CWqXDL0hDjXuNcq0zuR4xqPSuasI3kqFDhqSyTjREz5gzq0fXg==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "load-json-file": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", + "integrity": "sha1-L19Fq5HjMhYjT9U62rZo607AmTs=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "parse-json": "^4.0.0", + "pify": "^3.0.0", + "strip-bom": "^3.0.0" + } + }, + "locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", + "dev": true, + "requires": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + } + }, + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "requires": { + "yallist": "^4.0.0" + } + }, + "normalize-package-data": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-3.0.2.tgz", + "integrity": "sha512-6CdZocmfGaKnIHPVFhJJZ3GuR8SsLKvDANFp47Jmy51aKIr8akjAWTSxtpI+MBgBFdSMRyo4hMpDlT6dTffgZg==", + "dev": true, + "requires": { + "hosted-git-info": "^4.0.1", + "resolve": "^1.20.0", + "semver": "^7.3.4", + "validate-npm-package-license": "^3.0.1" + } + }, + "p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "dev": true, + "requires": { + "p-try": "^1.0.0" + } + }, + "p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", + "dev": true, + "requires": { + "p-limit": "^1.1.0" + } + }, + "p-try": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", + "dev": true + }, + "parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", + "dev": true, + "requires": { + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" + } + }, + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", + "dev": true + }, + "path-type": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", + "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", + "dev": true, + "requires": { + "pify": "^3.0.0" + } + }, + "pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", + "dev": true + }, + "read-pkg": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", + "integrity": "sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k=", + "dev": true, + "requires": { + "load-json-file": "^4.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^3.0.0" + }, + "dependencies": { + "hosted-git-info": { + "version": "2.8.9", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", + "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", + "dev": true + }, + "normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "dev": true, + "requires": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + } + } + }, + "read-pkg-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-3.0.0.tgz", + "integrity": "sha1-PtSWaF26D4/hGNBpHcUfSh/5bwc=", + "dev": true, + "requires": { + "find-up": "^2.0.0", + "read-pkg": "^3.0.0" + } + }, + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + }, + "resolve": { + "version": "1.20.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz", + "integrity": "sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==", + "dev": true, + "requires": { + "is-core-module": "^2.2.0", + "path-parse": "^1.0.6" + } + }, + "semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "through2": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/through2/-/through2-4.0.2.tgz", + "integrity": "sha512-iOqSav00cVxEEICeD7TjLB1sueEL+81Wpzp2bY17uZjZN0pWZPuo4suZ/61VujxmqSGFfgOcNuTZ85QJwNZQpw==", + "dev": true, + "requires": { + "readable-stream": "3" + } + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + } + } + }, + "conventional-changelog-ember": { + "version": "2.0.9", + "resolved": "https://registry.npmjs.org/conventional-changelog-ember/-/conventional-changelog-ember-2.0.9.tgz", + "integrity": "sha512-ulzIReoZEvZCBDhcNYfDIsLTHzYHc7awh+eI44ZtV5cx6LVxLlVtEmcO+2/kGIHGtw+qVabJYjdI5cJOQgXh1A==", + "dev": true, + "requires": { + "q": "^1.5.1" + } + }, + "conventional-changelog-eslint": { + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/conventional-changelog-eslint/-/conventional-changelog-eslint-3.0.9.tgz", + "integrity": "sha512-6NpUCMgU8qmWmyAMSZO5NrRd7rTgErjrm4VASam2u5jrZS0n38V7Y9CzTtLT2qwz5xEChDR4BduoWIr8TfwvXA==", + "dev": true, + "requires": { + "q": "^1.5.1" + } + }, + "conventional-changelog-express": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/conventional-changelog-express/-/conventional-changelog-express-2.0.6.tgz", + "integrity": "sha512-SDez2f3iVJw6V563O3pRtNwXtQaSmEfTCaTBPCqn0oG0mfkq0rX4hHBq5P7De2MncoRixrALj3u3oQsNK+Q0pQ==", + "dev": true, + "requires": { + "q": "^1.5.1" + } + }, + "conventional-changelog-jquery": { + "version": "3.0.11", + "resolved": "https://registry.npmjs.org/conventional-changelog-jquery/-/conventional-changelog-jquery-3.0.11.tgz", + "integrity": "sha512-x8AWz5/Td55F7+o/9LQ6cQIPwrCjfJQ5Zmfqi8thwUEKHstEn4kTIofXub7plf1xvFA2TqhZlq7fy5OmV6BOMw==", + "dev": true, + "requires": { + "q": "^1.5.1" + } + }, + "conventional-changelog-jshint": { + "version": "2.0.9", + "resolved": "https://registry.npmjs.org/conventional-changelog-jshint/-/conventional-changelog-jshint-2.0.9.tgz", + "integrity": "sha512-wMLdaIzq6TNnMHMy31hql02OEQ8nCQfExw1SE0hYL5KvU+JCTuPaDO+7JiogGT2gJAxiUGATdtYYfh+nT+6riA==", + "dev": true, + "requires": { + "compare-func": "^2.0.0", + "q": "^1.5.1" + } + }, + "conventional-changelog-preset-loader": { + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/conventional-changelog-preset-loader/-/conventional-changelog-preset-loader-2.3.4.tgz", + "integrity": "sha512-GEKRWkrSAZeTq5+YjUZOYxdHq+ci4dNwHvpaBC3+ENalzFWuCWa9EZXSuZBpkr72sMdKB+1fyDV4takK1Lf58g==", + "dev": true + }, + "conventional-changelog-writer": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/conventional-changelog-writer/-/conventional-changelog-writer-4.1.0.tgz", + "integrity": "sha512-WwKcUp7WyXYGQmkLsX4QmU42AZ1lqlvRW9mqoyiQzdD+rJWbTepdWoKJuwXTS+yq79XKnQNa93/roViPQrAQgw==", + "dev": true, + "requires": { + "compare-func": "^2.0.0", + "conventional-commits-filter": "^2.0.7", + "dateformat": "^3.0.0", + "handlebars": "^4.7.6", + "json-stringify-safe": "^5.0.1", + "lodash": "^4.17.15", + "meow": "^8.0.0", + "semver": "^6.0.0", + "split": "^1.0.0", + "through2": "^4.0.0" + }, + "dependencies": { + "camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true + }, + "camelcase-keys": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-6.2.2.tgz", + "integrity": "sha512-YrwaA0vEKazPBkn0ipTiMpSajYDSe+KjQfrjhcBMxJt/znbvlHd8Pw/Vamaz5EB4Wfhs3SUR3Z9mwRu/P3s3Yg==", + "dev": true, + "requires": { + "camelcase": "^5.3.1", + "map-obj": "^4.0.0", + "quick-lru": "^4.0.1" + } + }, + "hosted-git-info": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.0.2.tgz", + "integrity": "sha512-c9OGXbZ3guC/xOlCg1Ci/VgWlwsqDv1yMQL1CWqXDL0hDjXuNcq0zuR4xqPSuasI3kqFDhqSyTjREz5gzq0fXg==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "requires": { + "yallist": "^4.0.0" + } + }, + "map-obj": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-4.2.1.tgz", + "integrity": "sha512-+WA2/1sPmDj1dlvvJmB5G6JKfY9dpn7EVBUL06+y6PoljPkh+6V1QihwxNkbcGxCRjt2b0F9K0taiCuo7MbdFQ==", + "dev": true + }, + "meow": { + "version": "8.1.2", + "resolved": "https://registry.npmjs.org/meow/-/meow-8.1.2.tgz", + "integrity": "sha512-r85E3NdZ+mpYk1C6RjPFEMSE+s1iZMuHtsHAqY0DT3jZczl0diWUZ8g6oU7h0M9cD2EL+PzaYghhCLzR0ZNn5Q==", + "dev": true, + "requires": { + "@types/minimist": "^1.2.0", + "camelcase-keys": "^6.2.2", + "decamelize-keys": "^1.1.0", + "hard-rejection": "^2.1.0", + "minimist-options": "4.1.0", + "normalize-package-data": "^3.0.0", + "read-pkg-up": "^7.0.1", + "redent": "^3.0.0", + "trim-newlines": "^3.0.0", + "type-fest": "^0.18.0", + "yargs-parser": "^20.2.3" + } + }, + "normalize-package-data": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-3.0.2.tgz", + "integrity": "sha512-6CdZocmfGaKnIHPVFhJJZ3GuR8SsLKvDANFp47Jmy51aKIr8akjAWTSxtpI+MBgBFdSMRyo4hMpDlT6dTffgZg==", + "dev": true, + "requires": { + "hosted-git-info": "^4.0.1", + "resolve": "^1.20.0", + "semver": "^7.3.4", + "validate-npm-package-license": "^3.0.1" + }, + "dependencies": { + "semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + } + } + }, + "read-pkg": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", + "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", + "dev": true, + "requires": { + "@types/normalize-package-data": "^2.4.0", + "normalize-package-data": "^2.5.0", + "parse-json": "^5.0.0", + "type-fest": "^0.6.0" + }, + "dependencies": { + "hosted-git-info": { + "version": "2.8.9", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", + "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", + "dev": true + }, + "normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "dev": true, + "requires": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + }, + "type-fest": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", + "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", + "dev": true + } + } + }, + "read-pkg-up": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz", + "integrity": "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==", + "dev": true, + "requires": { + "find-up": "^4.1.0", + "read-pkg": "^5.2.0", + "type-fest": "^0.8.1" + }, + "dependencies": { + "type-fest": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "dev": true + } + } + }, + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + }, + "resolve": { + "version": "1.20.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz", + "integrity": "sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==", + "dev": true, + "requires": { + "is-core-module": "^2.2.0", + "path-parse": "^1.0.6" + } + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + }, + "through2": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/through2/-/through2-4.0.2.tgz", + "integrity": "sha512-iOqSav00cVxEEICeD7TjLB1sueEL+81Wpzp2bY17uZjZN0pWZPuo4suZ/61VujxmqSGFfgOcNuTZ85QJwNZQpw==", + "dev": true, + "requires": { + "readable-stream": "3" + } + }, + "trim-newlines": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-3.0.0.tgz", + "integrity": "sha512-C4+gOpvmxaSMKuEf9Qc134F1ZuOHVXKRbtEflf4NTtuuJDEIJ9p5PXsalL8SkeRw+qit1Mo+yuvMPAKwWg/1hA==", + "dev": true + }, + "type-fest": { + "version": "0.18.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.18.1.tgz", + "integrity": "sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw==", + "dev": true + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "yargs-parser": { + "version": "20.2.7", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.7.tgz", + "integrity": "sha512-FiNkvbeHzB/syOjIUxFDCnhSfzAL8R5vs40MgLFBorXACCOAEaWu0gRZl14vG8MR9AOJIZbmkjhusqBYZ3HTHw==", + "dev": true + } + } + }, + "conventional-commit-types": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/conventional-commit-types/-/conventional-commit-types-3.0.0.tgz", + "integrity": "sha512-SmmCYnOniSsAa9GqWOeLqc179lfr5TRu5b4QFDkbsrJ5TZjPJx85wtOr3zn+1dbeNiXDKGPbZ72IKbPhLXh/Lg==", + "dev": true + }, + "conventional-commits-filter": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/conventional-commits-filter/-/conventional-commits-filter-2.0.7.tgz", + "integrity": "sha512-ASS9SamOP4TbCClsRHxIHXRfcGCnIoQqkvAzCSbZzTFLfcTqJVugB0agRgsEELsqaeWgsXv513eS116wnlSSPA==", + "dev": true, + "requires": { + "lodash.ismatch": "^4.4.0", + "modify-values": "^1.0.0" + } + }, + "conventional-commits-parser": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/conventional-commits-parser/-/conventional-commits-parser-3.2.1.tgz", + "integrity": "sha512-OG9kQtmMZBJD/32NEw5IhN5+HnBqVjy03eC+I71I0oQRFA5rOgA4OtPOYG7mz1GkCfCNxn3gKIX8EiHJYuf1cA==", + "dev": true, + "requires": { + "JSONStream": "^1.0.4", + "is-text-path": "^1.0.1", + "lodash": "^4.17.15", + "meow": "^8.0.0", + "split2": "^3.0.0", + "through2": "^4.0.0", + "trim-off-newlines": "^1.0.0" + }, + "dependencies": { + "camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true + }, + "camelcase-keys": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-6.2.2.tgz", + "integrity": "sha512-YrwaA0vEKazPBkn0ipTiMpSajYDSe+KjQfrjhcBMxJt/znbvlHd8Pw/Vamaz5EB4Wfhs3SUR3Z9mwRu/P3s3Yg==", + "dev": true, + "requires": { + "camelcase": "^5.3.1", + "map-obj": "^4.0.0", + "quick-lru": "^4.0.1" + } + }, + "hosted-git-info": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.0.2.tgz", + "integrity": "sha512-c9OGXbZ3guC/xOlCg1Ci/VgWlwsqDv1yMQL1CWqXDL0hDjXuNcq0zuR4xqPSuasI3kqFDhqSyTjREz5gzq0fXg==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "requires": { + "yallist": "^4.0.0" + } + }, + "map-obj": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-4.2.1.tgz", + "integrity": "sha512-+WA2/1sPmDj1dlvvJmB5G6JKfY9dpn7EVBUL06+y6PoljPkh+6V1QihwxNkbcGxCRjt2b0F9K0taiCuo7MbdFQ==", + "dev": true + }, + "meow": { + "version": "8.1.2", + "resolved": "https://registry.npmjs.org/meow/-/meow-8.1.2.tgz", + "integrity": "sha512-r85E3NdZ+mpYk1C6RjPFEMSE+s1iZMuHtsHAqY0DT3jZczl0diWUZ8g6oU7h0M9cD2EL+PzaYghhCLzR0ZNn5Q==", + "dev": true, + "requires": { + "@types/minimist": "^1.2.0", + "camelcase-keys": "^6.2.2", + "decamelize-keys": "^1.1.0", + "hard-rejection": "^2.1.0", + "minimist-options": "4.1.0", + "normalize-package-data": "^3.0.0", + "read-pkg-up": "^7.0.1", + "redent": "^3.0.0", + "trim-newlines": "^3.0.0", + "type-fest": "^0.18.0", + "yargs-parser": "^20.2.3" + } + }, + "normalize-package-data": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-3.0.2.tgz", + "integrity": "sha512-6CdZocmfGaKnIHPVFhJJZ3GuR8SsLKvDANFp47Jmy51aKIr8akjAWTSxtpI+MBgBFdSMRyo4hMpDlT6dTffgZg==", + "dev": true, + "requires": { + "hosted-git-info": "^4.0.1", + "resolve": "^1.20.0", + "semver": "^7.3.4", + "validate-npm-package-license": "^3.0.1" + } + }, + "read-pkg": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", + "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", + "dev": true, + "requires": { + "@types/normalize-package-data": "^2.4.0", + "normalize-package-data": "^2.5.0", + "parse-json": "^5.0.0", + "type-fest": "^0.6.0" + }, + "dependencies": { + "hosted-git-info": { + "version": "2.8.9", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", + "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", + "dev": true + }, + "normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "dev": true, + "requires": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + }, + "type-fest": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", + "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", + "dev": true + } + } + }, + "read-pkg-up": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz", + "integrity": "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==", + "dev": true, + "requires": { + "find-up": "^4.1.0", + "read-pkg": "^5.2.0", + "type-fest": "^0.8.1" + }, + "dependencies": { + "type-fest": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "dev": true + } + } + }, + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + }, + "resolve": { + "version": "1.20.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz", + "integrity": "sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==", + "dev": true, + "requires": { + "is-core-module": "^2.2.0", + "path-parse": "^1.0.6" + } + }, + "semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "through2": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/through2/-/through2-4.0.2.tgz", + "integrity": "sha512-iOqSav00cVxEEICeD7TjLB1sueEL+81Wpzp2bY17uZjZN0pWZPuo4suZ/61VujxmqSGFfgOcNuTZ85QJwNZQpw==", + "dev": true, + "requires": { + "readable-stream": "3" + } + }, + "trim-newlines": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-3.0.0.tgz", + "integrity": "sha512-C4+gOpvmxaSMKuEf9Qc134F1ZuOHVXKRbtEflf4NTtuuJDEIJ9p5PXsalL8SkeRw+qit1Mo+yuvMPAKwWg/1hA==", + "dev": true + }, + "type-fest": { + "version": "0.18.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.18.1.tgz", + "integrity": "sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw==", + "dev": true + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "yargs-parser": { + "version": "20.2.7", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.7.tgz", + "integrity": "sha512-FiNkvbeHzB/syOjIUxFDCnhSfzAL8R5vs40MgLFBorXACCOAEaWu0gRZl14vG8MR9AOJIZbmkjhusqBYZ3HTHw==", + "dev": true + } + } + }, + "conventional-recommended-bump": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/conventional-recommended-bump/-/conventional-recommended-bump-6.1.0.tgz", + "integrity": "sha512-uiApbSiNGM/kkdL9GTOLAqC4hbptObFo4wW2QRyHsKciGAfQuLU1ShZ1BIVI/+K2BE/W1AWYQMCXAsv4dyKPaw==", + "dev": true, + "requires": { + "concat-stream": "^2.0.0", + "conventional-changelog-preset-loader": "^2.3.4", + "conventional-commits-filter": "^2.0.7", + "conventional-commits-parser": "^3.2.0", + "git-raw-commits": "^2.0.8", + "git-semver-tags": "^4.1.1", + "meow": "^8.0.0", + "q": "^1.5.1" + }, + "dependencies": { + "camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true + }, + "camelcase-keys": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-6.2.2.tgz", + "integrity": "sha512-YrwaA0vEKazPBkn0ipTiMpSajYDSe+KjQfrjhcBMxJt/znbvlHd8Pw/Vamaz5EB4Wfhs3SUR3Z9mwRu/P3s3Yg==", + "dev": true, + "requires": { + "camelcase": "^5.3.1", + "map-obj": "^4.0.0", + "quick-lru": "^4.0.1" + } + }, + "concat-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-2.0.0.tgz", + "integrity": "sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==", + "dev": true, + "requires": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.0.2", + "typedarray": "^0.0.6" + } + }, + "hosted-git-info": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.0.2.tgz", + "integrity": "sha512-c9OGXbZ3guC/xOlCg1Ci/VgWlwsqDv1yMQL1CWqXDL0hDjXuNcq0zuR4xqPSuasI3kqFDhqSyTjREz5gzq0fXg==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "requires": { + "yallist": "^4.0.0" + } + }, + "map-obj": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-4.2.1.tgz", + "integrity": "sha512-+WA2/1sPmDj1dlvvJmB5G6JKfY9dpn7EVBUL06+y6PoljPkh+6V1QihwxNkbcGxCRjt2b0F9K0taiCuo7MbdFQ==", + "dev": true + }, + "meow": { + "version": "8.1.2", + "resolved": "https://registry.npmjs.org/meow/-/meow-8.1.2.tgz", + "integrity": "sha512-r85E3NdZ+mpYk1C6RjPFEMSE+s1iZMuHtsHAqY0DT3jZczl0diWUZ8g6oU7h0M9cD2EL+PzaYghhCLzR0ZNn5Q==", + "dev": true, + "requires": { + "@types/minimist": "^1.2.0", + "camelcase-keys": "^6.2.2", + "decamelize-keys": "^1.1.0", + "hard-rejection": "^2.1.0", + "minimist-options": "4.1.0", + "normalize-package-data": "^3.0.0", + "read-pkg-up": "^7.0.1", + "redent": "^3.0.0", + "trim-newlines": "^3.0.0", + "type-fest": "^0.18.0", + "yargs-parser": "^20.2.3" + } + }, + "normalize-package-data": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-3.0.2.tgz", + "integrity": "sha512-6CdZocmfGaKnIHPVFhJJZ3GuR8SsLKvDANFp47Jmy51aKIr8akjAWTSxtpI+MBgBFdSMRyo4hMpDlT6dTffgZg==", + "dev": true, + "requires": { + "hosted-git-info": "^4.0.1", + "resolve": "^1.20.0", + "semver": "^7.3.4", + "validate-npm-package-license": "^3.0.1" + } + }, + "read-pkg": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", + "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", + "dev": true, + "requires": { + "@types/normalize-package-data": "^2.4.0", + "normalize-package-data": "^2.5.0", + "parse-json": "^5.0.0", + "type-fest": "^0.6.0" + }, + "dependencies": { + "hosted-git-info": { + "version": "2.8.9", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", + "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", + "dev": true + }, + "normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "dev": true, + "requires": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + }, + "type-fest": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", + "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", + "dev": true + } + } + }, + "read-pkg-up": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz", + "integrity": "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==", + "dev": true, + "requires": { + "find-up": "^4.1.0", + "read-pkg": "^5.2.0", + "type-fest": "^0.8.1" + }, + "dependencies": { + "type-fest": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "dev": true + } + } + }, + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + }, + "resolve": { + "version": "1.20.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz", + "integrity": "sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==", + "dev": true, + "requires": { + "is-core-module": "^2.2.0", + "path-parse": "^1.0.6" + } + }, + "semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "trim-newlines": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-3.0.0.tgz", + "integrity": "sha512-C4+gOpvmxaSMKuEf9Qc134F1ZuOHVXKRbtEflf4NTtuuJDEIJ9p5PXsalL8SkeRw+qit1Mo+yuvMPAKwWg/1hA==", + "dev": true + }, + "type-fest": { + "version": "0.18.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.18.1.tgz", + "integrity": "sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw==", + "dev": true + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "yargs-parser": { + "version": "20.2.7", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.7.tgz", + "integrity": "sha512-FiNkvbeHzB/syOjIUxFDCnhSfzAL8R5vs40MgLFBorXACCOAEaWu0gRZl14vG8MR9AOJIZbmkjhusqBYZ3HTHw==", + "dev": true + } + } + }, + "convert-source-map": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.7.0.tgz", + "integrity": "sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA==", + "requires": { + "safe-buffer": "~5.1.1" + } + }, + "cookie": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz", + "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==" + }, + "cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" + }, + "copy-concurrently": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/copy-concurrently/-/copy-concurrently-1.0.5.tgz", + "integrity": "sha512-f2domd9fsVDFtaFcbaRZuYXwtdmnzqbADSwhSWYxYB/Q8zsdUUFMXVRwXGDMWmbEzAn1kdRrtI1T/KTFOL4X2A==", + "requires": { + "aproba": "^1.1.1", + "fs-write-stream-atomic": "^1.0.8", + "iferr": "^0.1.5", + "mkdirp": "^0.5.1", + "rimraf": "^2.5.4", + "run-queue": "^1.0.0" + }, + "dependencies": { + "rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "requires": { + "glob": "^7.1.3" + } + } + } + }, + "copy-descriptor": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", + "integrity": "sha512-XgZ0pFcakEUlbwQEVNg3+QAis1FyTL3Qel9FYy8pSkQqoG3PNoT0bOCQtOXcOkur21r2Eq2kI+IE+gsmAEVlYw==" + }, + "core-js": { + "version": "3.11.0", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.11.0.tgz", + "integrity": "sha512-bd79DPpx+1Ilh9+30aT5O1sgpQd4Ttg8oqkqi51ZzhedMM1omD2e6IOF48Z/DzDCZ2svp49tN/3vneTK6ZBkXw==" + }, + "core-js-compat": { + "version": "3.11.0", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.11.0.tgz", + "integrity": "sha512-3wsN9YZJohOSDCjVB0GequOyHax8zFiogSX3XWLE28M1Ew7dTU57tgHjIylSBKSIouwmLBp3g61sKMz/q3xEGA==", + "requires": { + "browserslist": "^4.16.4", + "semver": "7.0.0" + }, + "dependencies": { + "semver": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.0.0.tgz", + "integrity": "sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A==" + } + } + }, + "core-js-pure": { + "version": "3.11.0", + "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.11.0.tgz", + "integrity": "sha512-PxEiQGjzC+5qbvE7ZIs5Zn6BynNeZO9zHhrrWmkRff2SZLq0CE/H5LuZOJHhmOQ8L38+eMzEHAmPYWrUtDfuDQ==" + }, + "core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==" + }, + "cosmiconfig": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.0.0.tgz", + "integrity": "sha512-pondGvTuVYDk++upghXJabWzL6Kxu6f26ljFw64Swq9v6sQPUL3EUlVDV56diOjpCayKihL6hVe8exIACU4XcA==", + "requires": { + "@types/parse-json": "^4.0.0", + "import-fresh": "^3.2.1", + "parse-json": "^5.0.0", + "path-type": "^4.0.0", + "yaml": "^1.10.0" + } + }, + "crc-32": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.0.tgz", + "integrity": "sha512-1uBwHxF+Y/4yF5G48fwnKq6QsIXheor3ZLPT80yGBV1oEUwpPojlEhQbWKVw1VwcTQyMGHK1/XMmTjmlsmTTGA==", + "requires": { + "exit-on-epipe": "~1.0.1", + "printj": "~1.1.0" + } + }, + "create-ecdh": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.4.tgz", + "integrity": "sha512-mf+TCx8wWc9VpuxfP2ht0iSISLZnt0JgWlrOKZiNqyUZWnjIaCIVNQArMHnCZKfEYRg6IM7A+NeJoN8gf/Ws0A==", + "requires": { + "bn.js": "^4.1.0", + "elliptic": "^6.5.3" + }, + "dependencies": { + "bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" + } + } + }, + "create-hash": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", + "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", + "requires": { + "cipher-base": "^1.0.1", + "inherits": "^2.0.1", + "md5.js": "^1.3.4", + "ripemd160": "^2.0.1", + "sha.js": "^2.4.0" + } + }, + "create-hmac": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", + "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", + "requires": { + "cipher-base": "^1.0.3", + "create-hash": "^1.1.0", + "inherits": "^2.0.1", + "ripemd160": "^2.0.0", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + } + }, + "cross-spawn": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", + "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "requires": { + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + }, + "dependencies": { + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" + } + } + }, + "crypto-browserify": { + "version": "3.12.0", + "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz", + "integrity": "sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==", + "requires": { + "browserify-cipher": "^1.0.0", + "browserify-sign": "^4.0.0", + "create-ecdh": "^4.0.0", + "create-hash": "^1.1.0", + "create-hmac": "^1.1.0", + "diffie-hellman": "^5.0.0", + "inherits": "^2.0.1", + "pbkdf2": "^3.0.3", + "public-encrypt": "^4.0.0", + "randombytes": "^2.0.0", + "randomfill": "^1.0.3" + } + }, + "crypto-random-string": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-1.0.0.tgz", + "integrity": "sha512-GsVpkFPlycH7/fRR7Dhcmnoii54gV1nz7y4CWyeFS14N+JVBBhY+r8amRHE4BwSYal7BPTDp8isvAlCxyFt3Hg==" + }, + "css": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/css/-/css-2.2.4.tgz", + "integrity": "sha512-oUnjmWpy0niI3x/mPL8dVEI1l7MnG3+HHyRPHf+YFSbK+svOhXpmSOcDURUh2aOCgl2grzrOPt1nHLuCVFULLw==", + "requires": { + "inherits": "^2.0.3", + "source-map": "^0.6.1", + "source-map-resolve": "^0.5.2", + "urix": "^0.1.0" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + } + } + }, + "css-blank-pseudo": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/css-blank-pseudo/-/css-blank-pseudo-0.1.4.tgz", + "integrity": "sha512-LHz35Hr83dnFeipc7oqFDmsjHdljj3TQtxGGiNWSOsTLIAubSm4TEz8qCaKFpk7idaQ1GfWscF4E6mgpBysA1w==", + "requires": { + "postcss": "^7.0.5" + } + }, + "css-color-names": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/css-color-names/-/css-color-names-0.0.4.tgz", + "integrity": "sha512-zj5D7X1U2h2zsXOAM8EyUREBnnts6H+Jm+d1M2DbiQQcUtnqgQsMrdo8JW9R80YFUmIdBZeMu5wvYM7hcgWP/Q==" + }, + "css-declaration-sorter": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-4.0.1.tgz", + "integrity": "sha512-BcxQSKTSEEQUftYpBVnsH4SF05NTuBokb19/sBt6asXGKZ/6VP7PLG1CBCkFDYOnhXhPh0jMhO6xZ71oYHXHBA==", + "requires": { + "postcss": "^7.0.1", + "timsort": "^0.3.0" + } + }, + "css-has-pseudo": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/css-has-pseudo/-/css-has-pseudo-0.10.0.tgz", + "integrity": "sha512-Z8hnfsZu4o/kt+AuFzeGpLVhFOGO9mluyHBaA2bA8aCGTwah5sT3WV/fTHH8UNZUytOIImuGPrl/prlb4oX4qQ==", + "requires": { + "postcss": "^7.0.6", + "postcss-selector-parser": "^5.0.0-rc.4" + }, + "dependencies": { + "cssesc": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-2.0.0.tgz", + "integrity": "sha512-MsCAG1z9lPdoO/IUMLSBWBSVxVtJ1395VGIQ+Fc2gNdkQ1hNDnQdw3YhA71WJCBW1vdwA0cAnk/DnW6bqoEUYg==" + }, + "postcss-selector-parser": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-5.0.0.tgz", + "integrity": "sha512-w+zLE5Jhg6Liz8+rQOWEAwtwkyqpfnmsinXjXg6cY7YIONZZtgvE0v2O0uhQBs0peNomOJwWRKt6JBfTdTd3OQ==", + "requires": { + "cssesc": "^2.0.0", + "indexes-of": "^1.0.1", + "uniq": "^1.0.1" + } + } + } + }, + "css-loader": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-4.3.0.tgz", + "integrity": "sha512-rdezjCjScIrsL8BSYszgT4s476IcNKt6yX69t0pHjJVnPUTDpn4WfIpDQTN3wCJvUvfsz/mFjuGOekf3PY3NUg==", + "requires": { + "camelcase": "^6.0.0", + "cssesc": "^3.0.0", + "icss-utils": "^4.1.1", + "loader-utils": "^2.0.0", + "postcss": "^7.0.32", + "postcss-modules-extract-imports": "^2.0.0", + "postcss-modules-local-by-default": "^3.0.3", + "postcss-modules-scope": "^2.2.0", + "postcss-modules-values": "^3.0.0", + "postcss-value-parser": "^4.1.0", + "schema-utils": "^2.7.1", + "semver": "^7.3.2" + } + }, + "css-prefers-color-scheme": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/css-prefers-color-scheme/-/css-prefers-color-scheme-3.1.1.tgz", + "integrity": "sha512-MTu6+tMs9S3EUqzmqLXEcgNRbNkkD/TGFvowpeoWJn5Vfq7FMgsmRQs9X5NXAURiOBmOxm/lLjsDNXDE6k9bhg==", + "requires": { + "postcss": "^7.0.5" + } + }, + "css-select": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-2.1.0.tgz", + "integrity": "sha512-Dqk7LQKpwLoH3VovzZnkzegqNSuAziQyNZUcrdDM401iY+R5NkGBXGmtO05/yaXQziALuPogeG0b7UAgjnTJTQ==", + "requires": { + "boolbase": "^1.0.0", + "css-what": "^3.2.1", + "domutils": "^1.7.0", + "nth-check": "^1.0.2" + } + }, + "css-select-base-adapter": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/css-select-base-adapter/-/css-select-base-adapter-0.1.1.tgz", + "integrity": "sha512-jQVeeRG70QI08vSTwf1jHxp74JoZsr2XSgETae8/xC8ovSnL2WF87GTLO86Sbwdt2lK4Umg4HnnwMO4YF3Ce7w==" + }, + "css-tree": { + "version": "1.0.0-alpha.37", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.0.0-alpha.37.tgz", + "integrity": "sha512-DMxWJg0rnz7UgxKT0Q1HU/L9BeJI0M6ksor0OgqOnF+aRCDWg/N2641HmVyU9KVIu0OVVWOb2IpC9A+BJRnejg==", + "requires": { + "mdn-data": "2.0.4", + "source-map": "^0.6.1" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + } + } + }, + "css-what": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-3.4.2.tgz", + "integrity": "sha512-ACUm3L0/jiZTqfzRM3Hi9Q8eZqd6IK37mMWPLz9PJxkLWllYeRf+EHUSHYEtFop2Eqytaq1FizFVh7XfBnXCDQ==" + }, + "css.escape": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/css.escape/-/css.escape-1.5.1.tgz", + "integrity": "sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==", + "dev": true + }, + "cssdb": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/cssdb/-/cssdb-4.4.0.tgz", + "integrity": "sha512-LsTAR1JPEM9TpGhl/0p3nQecC2LJ0kD8X5YARu1hk/9I1gril5vDtMZyNxcEpxxDj34YNck/ucjuoUd66K03oQ==" + }, + "cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==" + }, + "cssnano": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-4.1.11.tgz", + "integrity": "sha512-6gZm2htn7xIPJOHY824ERgj8cNPgPxyCSnkXc4v7YvNW+TdVfzgngHcEhy/8D11kUWRUMbke+tC+AUcUsnMz2g==", + "requires": { + "cosmiconfig": "^5.0.0", + "cssnano-preset-default": "^4.0.8", + "is-resolvable": "^1.0.0", + "postcss": "^7.0.0" + }, + "dependencies": { + "cosmiconfig": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-5.2.1.tgz", + "integrity": "sha512-H65gsXo1SKjf8zmrJ67eJk8aIRKV5ff2D4uKZIBZShbhGSpEmsQOPW/SKMKYhSTrqR7ufy6RP69rPogdaPh/kA==", + "requires": { + "import-fresh": "^2.0.0", + "is-directory": "^0.3.1", + "js-yaml": "^3.13.1", + "parse-json": "^4.0.0" + } + }, + "import-fresh": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-2.0.0.tgz", + "integrity": "sha512-eZ5H8rcgYazHbKC3PG4ClHNykCSxtAhxSSEM+2mb+7evD2CKF5V7c0dNum7AdpDh0ZdICwZY9sRSn8f+KH96sg==", + "requires": { + "caller-path": "^2.0.0", + "resolve-from": "^3.0.0" + } + }, + "parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==", + "requires": { + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" + } + }, + "resolve-from": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz", + "integrity": "sha512-GnlH6vxLymXJNMBo7XP1fJIzBFbdYt49CuTwmB/6N53t+kMPRMFKz783LlQ4tv28XoQfMWinAJX6WCGf2IlaIw==" + } + } + }, + "cssnano-preset-default": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/cssnano-preset-default/-/cssnano-preset-default-4.0.8.tgz", + "integrity": "sha512-LdAyHuq+VRyeVREFmuxUZR1TXjQm8QQU/ktoo/x7bz+SdOge1YKc5eMN6pRW7YWBmyq59CqYba1dJ5cUukEjLQ==", + "requires": { + "css-declaration-sorter": "^4.0.1", + "cssnano-util-raw-cache": "^4.0.1", + "postcss": "^7.0.0", + "postcss-calc": "^7.0.1", + "postcss-colormin": "^4.0.3", + "postcss-convert-values": "^4.0.1", + "postcss-discard-comments": "^4.0.2", + "postcss-discard-duplicates": "^4.0.2", + "postcss-discard-empty": "^4.0.1", + "postcss-discard-overridden": "^4.0.1", + "postcss-merge-longhand": "^4.0.11", + "postcss-merge-rules": "^4.0.3", + "postcss-minify-font-values": "^4.0.2", + "postcss-minify-gradients": "^4.0.2", + "postcss-minify-params": "^4.0.2", + "postcss-minify-selectors": "^4.0.2", + "postcss-normalize-charset": "^4.0.1", + "postcss-normalize-display-values": "^4.0.2", + "postcss-normalize-positions": "^4.0.2", + "postcss-normalize-repeat-style": "^4.0.2", + "postcss-normalize-string": "^4.0.2", + "postcss-normalize-timing-functions": "^4.0.2", + "postcss-normalize-unicode": "^4.0.1", + "postcss-normalize-url": "^4.0.1", + "postcss-normalize-whitespace": "^4.0.2", + "postcss-ordered-values": "^4.1.2", + "postcss-reduce-initial": "^4.0.3", + "postcss-reduce-transforms": "^4.0.2", + "postcss-svgo": "^4.0.3", + "postcss-unique-selectors": "^4.0.1" + } + }, + "cssnano-util-get-arguments": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/cssnano-util-get-arguments/-/cssnano-util-get-arguments-4.0.0.tgz", + "integrity": "sha512-6RIcwmV3/cBMG8Aj5gucQRsJb4vv4I4rn6YjPbVWd5+Pn/fuG+YseGvXGk00XLkoZkaj31QOD7vMUpNPC4FIuw==" + }, + "cssnano-util-get-match": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/cssnano-util-get-match/-/cssnano-util-get-match-4.0.0.tgz", + "integrity": "sha512-JPMZ1TSMRUPVIqEalIBNoBtAYbi8okvcFns4O0YIhcdGebeYZK7dMyHJiQ6GqNBA9kE0Hym4Aqym5rPdsV/4Cw==" + }, + "cssnano-util-raw-cache": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/cssnano-util-raw-cache/-/cssnano-util-raw-cache-4.0.1.tgz", + "integrity": "sha512-qLuYtWK2b2Dy55I8ZX3ky1Z16WYsx544Q0UWViebptpwn/xDBmog2TLg4f+DBMg1rJ6JDWtn96WHbOKDWt1WQA==", + "requires": { + "postcss": "^7.0.0" + } + }, + "cssnano-util-same-parent": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/cssnano-util-same-parent/-/cssnano-util-same-parent-4.0.1.tgz", + "integrity": "sha512-WcKx5OY+KoSIAxBW6UBBRay1U6vkYheCdjyVNDm85zt5K9mHoGOfsOsqIszfAqrQQFIIKgjh2+FDgIj/zsl21Q==" + }, + "csso": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/csso/-/csso-4.2.0.tgz", + "integrity": "sha512-wvlcdIbf6pwKEk7vHj8/Bkc0B4ylXZruLvOgs9doS5eOsOpuodOV2zJChSpkp+pRpYQLQMeF04nr3Z68Sta9jA==", + "requires": { + "css-tree": "^1.1.2" + }, + "dependencies": { + "css-tree": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.1.3.tgz", + "integrity": "sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==", + "requires": { + "mdn-data": "2.0.14", + "source-map": "^0.6.1" + } + }, + "mdn-data": { + "version": "2.0.14", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.14.tgz", + "integrity": "sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==" + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + } + } + }, + "cssom": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.4.4.tgz", + "integrity": "sha512-p3pvU7r1MyyqbTk+WbNJIgJjG2VmTIaB10rI93LzVPrmDJKkzKYMtxxyAvQXR/NS6otuzveI7+7BBq3SjBS2mw==" + }, + "cssstyle": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-2.3.0.tgz", + "integrity": "sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A==", + "requires": { + "cssom": "~0.3.6" + }, + "dependencies": { + "cssom": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz", + "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==" + } + } + }, + "csstype": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.8.tgz", + "integrity": "sha512-jXKhWqXPmlUeoQnF/EhTtTl4C9SnrxSH/jZUih3jmO6lBKr99rP3/+FmrMj4EFpOXzMtXHAZkd3x0E6h6Fgflw==" + }, + "currently-unhandled": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz", + "integrity": "sha512-/fITjgjGU50vjQ4FH6eUoYu+iUoUKIXws2hL15JJpIR+BbTxaXQsMuuyjtNh2WqsSBS5nsaZHFsFecyw5CCAng==", + "dev": true, + "requires": { + "array-find-index": "^1.0.1" + } + }, + "cyclist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/cyclist/-/cyclist-1.0.1.tgz", + "integrity": "sha512-NJGVKPS81XejHcLhaLJS7plab0fK3slPh11mESeeDq2W4ZI5kUKK/LRRdVDvjJseojbPB7ZwjnyOybg3Igea/A==" + }, + "cz-conventional-changelog": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cz-conventional-changelog/-/cz-conventional-changelog-2.1.0.tgz", + "integrity": "sha512-TMjkSrvju5fPQV+Ho8TIioAgXkly8h3vJ/txiczJrlUaLpgMGA6ssnwquLMWzNZZyCsJK5r4kPgwdohC4UAGmQ==", + "dev": true, + "requires": { + "conventional-commit-types": "^2.0.0", + "lodash.map": "^4.5.1", + "longest": "^1.0.1", + "right-pad": "^1.0.1", + "word-wrap": "^1.0.3" + }, + "dependencies": { + "conventional-commit-types": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/conventional-commit-types/-/conventional-commit-types-2.3.0.tgz", + "integrity": "sha512-6iB39PrcGYdz0n3z31kj6/Km6mK9hm9oMRhwcLnKxE7WNoeRKZbTAobliKrbYZ5jqyCvtcVEfjCiaEzhL3AVmQ==", + "dev": true + }, + "longest": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz", + "integrity": "sha512-k+yt5n3l48JU4k8ftnKG6V7u32wyH2NfKzeMto9F/QRE0amxy/LayxwlvjjkZEIzqR+19IrtFO8p5kB9QaYUFg==", + "dev": true + } + } + }, + "d": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz", + "integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==", + "requires": { + "es5-ext": "^0.10.50", + "type": "^1.0.1" + } + }, + "damerau-levenshtein": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.6.tgz", + "integrity": "sha512-JVrozIeElnj3QzfUIt8tB8YMluBJom4Vw9qTPpjGYQ9fYlB3D/rb6OordUxf3xeFB35LKWs0xqcO5U6ySvBtug==" + }, + "dargs": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/dargs/-/dargs-7.0.0.tgz", + "integrity": "sha512-2iy1EkLdlBzQGvbweYRFxmFath8+K7+AKB0TlhHWkNuH+TmovaMH/Wp7V7R4u7f4SnX3OgLsU9t1NI9ioDnUpg==", + "dev": true + }, + "dashdash": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "integrity": "sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g==", + "requires": { + "assert-plus": "^1.0.0" + } + }, + "data-urls": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-2.0.0.tgz", + "integrity": "sha512-X5eWTSXO/BJmpdIKCRuKUgSCgAN0OwliVK3yPKbwIWU1Tdw5BRajxlzMidvh+gwko9AfQ9zIj52pzF91Q3YAvQ==", + "requires": { + "abab": "^2.0.3", + "whatwg-mimetype": "^2.3.0", + "whatwg-url": "^8.0.0" + } + }, + "date-fns": { + "version": "2.21.1", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.21.1.tgz", + "integrity": "sha512-m1WR0xGiC6j6jNFAyW4Nvh4WxAi4JF4w9jRJwSI8nBmNcyZXPcP9VUQG+6gHQXAmqaGEKDKhOqAtENDC941UkA==" + }, + "dateformat": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-3.0.3.tgz", + "integrity": "sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q==", + "dev": true + }, + "debounce": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/debounce/-/debounce-1.2.1.tgz", + "integrity": "sha512-XRRe6Glud4rd/ZGQfiV1ruXSfbvfJedlV9Y6zOlP+2K04vBYiJEte6stfFkCP03aMnY5tsipamumUjL14fofug==" + }, + "debug": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "requires": { + "ms": "2.1.2" + } + }, + "decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==" + }, + "decamelize-keys": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/decamelize-keys/-/decamelize-keys-1.1.0.tgz", + "integrity": "sha512-ocLWuYzRPoS9bfiSdDd3cxvrzovVMZnRDVEzAs+hWIVXGDbHxWMECij2OBuyB/An0FFW/nLuq6Kv1i/YC5Qfzg==", + "dev": true, + "requires": { + "decamelize": "^1.1.0", + "map-obj": "^1.0.0" + } + }, + "decimal.js": { + "version": "10.2.1", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.2.1.tgz", + "integrity": "sha512-KaL7+6Fw6i5A2XSnsbhm/6B+NuEA7TZ4vqxnd5tXz9sbKtrN9Srj8ab4vKVdK8YAqZO9P1kg45Y6YLoduPf+kw==" + }, + "decode-uri-component": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", + "integrity": "sha512-hjf+xovcEn31w/EUYdTXQh/8smFL/dzYjohQGEIgjyNavaJfBY2p5F527Bo1VPATxv0VYTUC2bOcXvqFwk78Og==" + }, + "dedent": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz", + "integrity": "sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA==" + }, + "deep-equal": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.1.1.tgz", + "integrity": "sha512-yd9c5AdiqVcR+JjcwUQb9DkhJc8ngNr0MahEBGvDiJw8puWab2yZlh+nkasOnZP+EGTAP6rRp2JzJhJZzvNF8g==", + "requires": { + "is-arguments": "^1.0.4", + "is-date-object": "^1.0.1", + "is-regex": "^1.0.4", + "object-is": "^1.0.1", + "object-keys": "^1.1.1", + "regexp.prototype.flags": "^1.2.0" + } + }, + "deep-is": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", + "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=" + }, + "deepmerge": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz", + "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==" + }, + "default-gateway": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/default-gateway/-/default-gateway-4.2.0.tgz", + "integrity": "sha512-h6sMrVB1VMWVrW13mSc6ia/DwYYw5MN6+exNu1OaJeFac5aSAvwM7lZ0NVfTABuSkQelr4h5oebg3KB1XPdjgA==", + "requires": { + "execa": "^1.0.0", + "ip-regex": "^2.1.0" + } + }, + "define-properties": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", + "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", + "requires": { + "object-keys": "^1.0.12" + } + }, + "define-property": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", + "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", + "requires": { + "is-descriptor": "^1.0.2", + "isobject": "^3.0.1" + }, + "dependencies": { + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } + } + }, + "del": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/del/-/del-4.1.1.tgz", + "integrity": "sha512-QwGuEUouP2kVwQenAsOof5Fv8K9t3D8Ca8NxcXKrIpEHjTXK5J2nXLdP+ALI1cgv8wj7KuwBhTwBkOZSJKM5XQ==", + "requires": { + "@types/glob": "^7.1.1", + "globby": "^6.1.0", + "is-path-cwd": "^2.0.0", + "is-path-in-cwd": "^2.0.0", + "p-map": "^2.0.0", + "pify": "^4.0.1", + "rimraf": "^2.6.3" + }, + "dependencies": { + "array-union": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", + "integrity": "sha512-Dxr6QJj/RdU/hCaBjOfxW+q6lyuVE6JFWIrAUpuOOhoJJoQ99cUn3igRaHVB5P9WrgFVN0FfArM3x0cueOU8ng==", + "requires": { + "array-uniq": "^1.0.1" + } + }, + "globby": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz", + "integrity": "sha512-KVbFv2TQtbzCoxAnfD6JcHZTYCzyliEaaeM/gH8qQdkKr5s0OP9scEgvdcngyk7AVdY6YVW/TJHd+lQ/Df3Daw==", + "requires": { + "array-union": "^1.0.1", + "glob": "^7.0.3", + "object-assign": "^4.0.1", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" + }, + "dependencies": { + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==" + } + } + }, + "p-map": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-2.1.0.tgz", + "integrity": "sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==" + }, + "pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==" + }, + "rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "requires": { + "glob": "^7.1.3" + } + } + } + }, + "delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==" + }, + "delegates": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==", + "dev": true + }, + "depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=" + }, + "des.js": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.1.tgz", + "integrity": "sha512-Q0I4pfFrv2VPd34/vfLrFOoRmlYj3OV50i7fskps1jZWK1kApMWWT9G6RRUeYedLcBDIhnSDaUvJMb3AhUlaEA==", + "requires": { + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0" + } + }, + "destroy": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", + "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" + }, + "detect-file": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/detect-file/-/detect-file-1.0.0.tgz", + "integrity": "sha512-DtCOLG98P007x7wiiOmfI0fi3eIKyWiLTGJ2MDnVi/E04lWGbf+JzrRHMm0rgIIZJGtHpKpbVgLWHrv8xXpc3Q==", + "dev": true + }, + "detect-indent": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-6.0.0.tgz", + "integrity": "sha512-oSyFlqaTHCItVRGK5RmrmjB+CmaMOW7IaNA/kdxqhoa6d17j/5ce9O9eWXmV/KEdRwqpQA+Vqe8a8Bsybu4YnA==", + "dev": true + }, + "detect-newline": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", + "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==" + }, + "detect-node": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.0.5.tgz", + "integrity": "sha512-qi86tE6hRcFHy8jI1m2VG+LaPUR1LhqDa5G8tVjuUXmOrpuAgqsA1pN0+ldgr3aKUH+QLI9hCY/OcRYisERejw==" + }, + "detect-port-alt": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/detect-port-alt/-/detect-port-alt-1.1.6.tgz", + "integrity": "sha512-5tQykt+LqfJFBEYaDITx7S7cR7mJ/zQmLXZ2qt5w04ainYZw6tBf9dBunMjVeVOdYVRUzUOE4HkY5J7+uttb5Q==", + "requires": { + "address": "^1.0.1", + "debug": "^2.6.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + } + } + }, + "diff-sequences": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-26.6.2.tgz", + "integrity": "sha512-Mv/TDa3nZ9sbc5soK+OoA74BsS3mL37yixCvUAQkiuA4Wz6YtwP/K47n2rv2ovzHZvoiQeA5FTQOschKkEwB0Q==" + }, + "diffie-hellman": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", + "integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==", + "requires": { + "bn.js": "^4.1.0", + "miller-rabin": "^4.0.0", + "randombytes": "^2.0.0" + }, + "dependencies": { + "bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" + } + } + }, + "dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "requires": { + "path-type": "^4.0.0" + } + }, + "discontinuous-range": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/discontinuous-range/-/discontinuous-range-1.0.0.tgz", + "integrity": "sha512-c68LpLbO+7kP/b1Hr1qs8/BJ09F5khZGTxqxZuhzxpmwJKOgRFHJWIb9/KmqnqHhLdO55aOxFH/EGBvUQbL/RQ==", + "dev": true + }, + "dns-equal": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/dns-equal/-/dns-equal-1.0.0.tgz", + "integrity": "sha512-z+paD6YUQsk+AbGCEM4PrOXSss5gd66QfcVBFTKR/HpFL9jCqikS94HYwKww6fQyO7IxrIIyUu+g0Ka9tUS2Cg==" + }, + "dns-packet": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-1.3.1.tgz", + "integrity": "sha512-0UxfQkMhYAUaZI+xrNZOz/as5KgDU0M/fQ9b6SpkyLbk3GEswDi6PADJVaYJradtRVsRIlF1zLyOodbcTCDzUg==", + "requires": { + "ip": "^1.1.0", + "safe-buffer": "^5.0.1" + } + }, + "dns-txt": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/dns-txt/-/dns-txt-2.0.2.tgz", + "integrity": "sha512-Ix5PrWjphuSoUXV/Zv5gaFHjnaJtb02F2+Si3Ht9dyJ87+Z/lMmy+dpNHtTGraNK958ndXq2i+GLkWsWHcKaBQ==", + "requires": { + "buffer-indexof": "^1.0.0" + } + }, + "doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "requires": { + "esutils": "^2.0.2" + } + }, + "dom-accessibility-api": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.3.0.tgz", + "integrity": "sha512-PzwHEmsRP3IGY4gv/Ug+rMeaTIyTJvadCb+ujYXYeIylbHJezIyNToe8KfEgHTCEYyC+/bUghYOGg8yMGlZ6vA==", + "dev": true + }, + "dom-converter": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/dom-converter/-/dom-converter-0.2.0.tgz", + "integrity": "sha512-gd3ypIPfOMr9h5jIKq8E3sHOTCjeirnl0WK5ZdS1AW0Odt0b1PaWaHdJ4Qk4klv+YB9aJBS7mESXjFoDQPu6DA==", + "requires": { + "utila": "~0.4" + } + }, + "dom-helpers": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz", + "integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==", + "requires": { + "@babel/runtime": "^7.8.7", + "csstype": "^3.0.2" + } + }, + "dom-serializer": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.2.2.tgz", + "integrity": "sha512-2/xPb3ORsQ42nHYiSunXkDjPLBaEj/xTwUO4B7XCZQTRk7EBtTOPaygh10YAAh2OI1Qrp6NWfpAhzswj0ydt9g==", + "requires": { + "domelementtype": "^2.0.1", + "entities": "^2.0.0" + }, + "dependencies": { + "domelementtype": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.2.0.tgz", + "integrity": "sha512-DtBMo82pv1dFtUmHyr48beiuq792Sxohr+8Hm9zoxklYPfa6n0Z3Byjj2IV7bmr2IyqClnqEQhfgHJJ5QF0R5A==" + } + } + }, + "domain-browser": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.2.0.tgz", + "integrity": "sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA==" + }, + "domelementtype": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz", + "integrity": "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==" + }, + "domexception": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/domexception/-/domexception-2.0.1.tgz", + "integrity": "sha512-yxJ2mFy/sibVQlu5qHjOkf9J3K6zgmCxgJ94u2EdvDOV09H+32LtRswEcUsmUWN72pVLOEnTSRaIVVzVQgS0dg==", + "requires": { + "webidl-conversions": "^5.0.0" + }, + "dependencies": { + "webidl-conversions": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-5.0.0.tgz", + "integrity": "sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA==" + } + } + }, + "domhandler": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.4.2.tgz", + "integrity": "sha512-JiK04h0Ht5u/80fdLMCEmV4zkNh2BcoMFBmZ/91WtYZ8qVXSKjiw7fXMgFPnHcSZgOo3XdinHvmnDUeMf5R4wA==", + "requires": { + "domelementtype": "1" + } + }, + "domutils": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.7.0.tgz", + "integrity": "sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg==", + "requires": { + "dom-serializer": "0", + "domelementtype": "1" + } + }, + "dot-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz", + "integrity": "sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==", + "requires": { + "no-case": "^3.0.4", + "tslib": "^2.0.3" + }, + "dependencies": { + "tslib": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.2.0.tgz", + "integrity": "sha512-gS9GVHRU+RGn5KQM2rllAlR3dU6m7AcpJKdtH8gFvQiC4Otgk98XnmMU+nZenHt/+VhnBPWwgrJsyrdcw6i23w==" + } + } + }, + "dot-prop": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz", + "integrity": "sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==", + "requires": { + "is-obj": "^2.0.0" + } + }, + "dotenv": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-8.2.0.tgz", + "integrity": "sha512-8sJ78ElpbDJBHNeBzUbUVLsqKdccaa/BXF1uPTw3GrvQTBgrQrtObr2mUrE38vzYd8cEv+m/JBfDLioYcfXoaw==" + }, + "dotenv-expand": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-5.1.0.tgz", + "integrity": "sha512-YXQl1DSa4/PQyRfgrv6aoNjhasp/p4qs9FjJ4q4cQk+8m4r6k4ZSiEyytKG8f8W9gi8WsQtIObNmKd+tMzNTmA==" + }, + "dotgitignore": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/dotgitignore/-/dotgitignore-2.1.0.tgz", + "integrity": "sha512-sCm11ak2oY6DglEPpCB8TixLjWAxd3kJTs6UIcSasNYxXdFPV+YKlye92c8H4kKFqV5qYMIh7d+cYecEg0dIkA==", + "dev": true, + "requires": { + "find-up": "^3.0.0", + "minimatch": "^3.0.4" + }, + "dependencies": { + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dev": true, + "requires": { + "locate-path": "^3.0.0" + } + }, + "locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dev": true, + "requires": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + } + }, + "p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "dev": true, + "requires": { + "p-limit": "^2.0.0" + } + }, + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", + "dev": true + } + } + }, + "duplexer": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz", + "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==" + }, + "duplexify": { + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.7.1.tgz", + "integrity": "sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g==", + "requires": { + "end-of-stream": "^1.0.0", + "inherits": "^2.0.1", + "readable-stream": "^2.0.0", + "stream-shift": "^1.0.0" + } + }, + "ecc-jsbn": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", + "integrity": "sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw==", + "requires": { + "jsbn": "~0.1.0", + "safer-buffer": "^2.1.0" + } + }, + "ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" + }, + "ejs": { + "version": "2.7.4", + "resolved": "https://registry.npmjs.org/ejs/-/ejs-2.7.4.tgz", + "integrity": "sha512-7vmuyh5+kuUyJKePhQfRQBhXV5Ce+RnaeeQArKu1EAMpL3WbgMt5WG6uQZpEVvYSSsxMXRKOewtDk9RaTKXRlA==" + }, + "electron-to-chromium": { + "version": "1.3.723", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.723.tgz", + "integrity": "sha512-L+WXyXI7c7+G1V8ANzRsPI5giiimLAUDC6Zs1ojHHPhYXb3k/iTABFmWjivEtsWrRQymjnO66/rO2ZTABGdmWg==" + }, + "elliptic": { + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.4.tgz", + "integrity": "sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==", + "requires": { + "bn.js": "^4.11.9", + "brorand": "^1.1.0", + "hash.js": "^1.0.0", + "hmac-drbg": "^1.0.1", + "inherits": "^2.0.4", + "minimalistic-assert": "^1.0.1", + "minimalistic-crypto-utils": "^1.0.1" + }, + "dependencies": { + "bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" + } + } + }, + "emittery": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.7.2.tgz", + "integrity": "sha512-A8OG5SR/ij3SsJdWDJdkkSYUjQdCUx6APQXem0SaEePBSRg4eymGYwBkKo1Y6DU+af/Jn2dBQqDBvjnr9Vi8nQ==" + }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + }, + "emojis-list": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", + "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==" + }, + "encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==" + }, + "end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "requires": { + "once": "^1.4.0" + } + }, + "enhanced-resolve": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-4.5.0.tgz", + "integrity": "sha512-Nv9m36S/vxpsI+Hc4/ZGRs0n9mXqSWGGq49zxb/cJfPAQMbUtttJAlNPS4AQzaBdw/pKskw5bMbekT/Y7W/Wlg==", + "requires": { + "graceful-fs": "^4.1.2", + "memory-fs": "^0.5.0", + "tapable": "^1.0.0" + }, + "dependencies": { + "memory-fs": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.5.0.tgz", + "integrity": "sha512-jA0rdU5KoQMC0e6ppoNRtpp6vjFq6+NY7r8hywnC7V+1Xj/MtHwGIbB1QaK/dunyjWteJzmkpd7ooeWg10T7GA==", + "requires": { + "errno": "^0.1.3", + "readable-stream": "^2.0.1" + } + } + } + }, + "enquirer": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", + "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", + "requires": { + "ansi-colors": "^4.1.1" + } + }, + "entities": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", + "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==" + }, + "enzyme": { + "version": "3.11.0", + "resolved": "https://registry.npmjs.org/enzyme/-/enzyme-3.11.0.tgz", + "integrity": "sha512-Dw8/Gs4vRjxY6/6i9wU0V+utmQO9kvh9XLnz3LIudviOnVYDEe2ec+0k+NQoMamn1VrjKgCUOWj5jG/5M5M0Qw==", + "dev": true, + "requires": { + "array.prototype.flat": "^1.2.3", + "cheerio": "^1.0.0-rc.3", + "enzyme-shallow-equal": "^1.0.1", + "function.prototype.name": "^1.1.2", + "has": "^1.0.3", + "html-element-map": "^1.2.0", + "is-boolean-object": "^1.0.1", + "is-callable": "^1.1.5", + "is-number-object": "^1.0.4", + "is-regex": "^1.0.5", + "is-string": "^1.0.5", + "is-subset": "^0.1.1", + "lodash.escape": "^4.0.1", + "lodash.isequal": "^4.5.0", + "object-inspect": "^1.7.0", + "object-is": "^1.0.2", + "object.assign": "^4.1.0", + "object.entries": "^1.1.1", + "object.values": "^1.1.1", + "raf": "^3.4.1", + "rst-selector-parser": "^2.2.3", + "string.prototype.trim": "^1.2.1" + } + }, + "enzyme-adapter-react-16": { + "version": "1.15.6", + "resolved": "https://registry.npmjs.org/enzyme-adapter-react-16/-/enzyme-adapter-react-16-1.15.6.tgz", + "integrity": "sha512-yFlVJCXh8T+mcQo8M6my9sPgeGzj85HSHi6Apgf1Cvq/7EL/J9+1JoJmJsRxZgyTvPMAqOEpRSu/Ii/ZpyOk0g==", + "dev": true, + "requires": { + "enzyme-adapter-utils": "^1.14.0", + "enzyme-shallow-equal": "^1.0.4", + "has": "^1.0.3", + "object.assign": "^4.1.2", + "object.values": "^1.1.2", + "prop-types": "^15.7.2", + "react-is": "^16.13.1", + "react-test-renderer": "^16.0.0-0", + "semver": "^5.7.0" + }, + "dependencies": { + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + } + } + }, + "enzyme-adapter-utils": { + "version": "1.14.0", + "resolved": "https://registry.npmjs.org/enzyme-adapter-utils/-/enzyme-adapter-utils-1.14.0.tgz", + "integrity": "sha512-F/z/7SeLt+reKFcb7597IThpDp0bmzcH1E9Oabqv+o01cID2/YInlqHbFl7HzWBl4h3OdZYedtwNDOmSKkk0bg==", + "dev": true, + "requires": { + "airbnb-prop-types": "^2.16.0", + "function.prototype.name": "^1.1.3", + "has": "^1.0.3", + "object.assign": "^4.1.2", + "object.fromentries": "^2.0.3", + "prop-types": "^15.7.2", + "semver": "^5.7.1" + }, + "dependencies": { + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + } + } + }, + "enzyme-shallow-equal": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/enzyme-shallow-equal/-/enzyme-shallow-equal-1.0.4.tgz", + "integrity": "sha512-MttIwB8kKxypwHvRynuC3ahyNc+cFbR8mjVIltnmzQ0uKGqmsfO4bfBuLxb0beLNPhjblUEYvEbsg+VSygvF1Q==", + "dev": true, + "requires": { + "has": "^1.0.3", + "object-is": "^1.1.2" + } + }, + "errno": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.8.tgz", + "integrity": "sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==", + "requires": { + "prr": "~1.0.1" + } + }, + "error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "requires": { + "is-arrayish": "^0.2.1" + } + }, + "error-stack-parser": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/error-stack-parser/-/error-stack-parser-2.0.6.tgz", + "integrity": "sha512-d51brTeqC+BHlwF0BhPtcYgF5nlzf9ZZ0ZIUQNZpc9ZB9qw5IJ2diTrBY9jlCJkTLITYPjmiX6OWCwH+fuyNgQ==", + "requires": { + "stackframe": "^1.1.1" + } + }, + "es-abstract": { + "version": "1.18.0", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.0.tgz", + "integrity": "sha512-LJzK7MrQa8TS0ja2w3YNLzUgJCGPdPOV1yVvezjNnS89D+VR08+Szt2mz3YB2Dck/+w5tfIq/RoUAFqJJGM2yw==", + "requires": { + "call-bind": "^1.0.2", + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "get-intrinsic": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.2", + "is-callable": "^1.2.3", + "is-negative-zero": "^2.0.1", + "is-regex": "^1.1.2", + "is-string": "^1.0.5", + "object-inspect": "^1.9.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.2", + "string.prototype.trimend": "^1.0.4", + "string.prototype.trimstart": "^1.0.4", + "unbox-primitive": "^1.0.0" + } + }, + "es-to-primitive": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "requires": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + } + }, + "es5-ext": { + "version": "0.10.53", + "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.53.tgz", + "integrity": "sha512-Xs2Stw6NiNHWypzRTY1MtaG/uJlwCk8kH81920ma8mvN8Xq1gsfhZvpkImLQArw8AHnv8MT2I45J3c0R8slE+Q==", + "requires": { + "es6-iterator": "~2.0.3", + "es6-symbol": "~3.1.3", + "next-tick": "~1.0.0" + } + }, + "es6-iterator": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", + "integrity": "sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g==", + "requires": { + "d": "1", + "es5-ext": "^0.10.35", + "es6-symbol": "^3.1.1" + } + }, + "es6-symbol": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.3.tgz", + "integrity": "sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==", + "requires": { + "d": "^1.0.1", + "ext": "^1.1.2" + } + }, + "escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==" + }, + "escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==" + }, + "escodegen": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.0.0.tgz", + "integrity": "sha512-mmHKys/C8BFUGI+MAWNcSYoORYLMdPzjrknd2Vc+bUsjN5bXcr8EhrNB+UTqfL1y3I9c4fw2ihgtMPQLBRiQxw==", + "requires": { + "esprima": "^4.0.1", + "estraverse": "^5.2.0", + "esutils": "^2.0.2", + "optionator": "^0.8.1", + "source-map": "~0.6.1" + }, + "dependencies": { + "estraverse": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", + "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==" + }, + "levn": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", + "integrity": "sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA==", + "requires": { + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2" + } + }, + "optionator": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", + "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", + "requires": { + "deep-is": "~0.1.3", + "fast-levenshtein": "~2.0.6", + "levn": "~0.3.0", + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2", + "word-wrap": "~1.2.3" + } + }, + "prelude-ls": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", + "integrity": "sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w==" + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "optional": true + }, + "type-check": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", + "integrity": "sha512-ZCmOJdvOWDBYJlzAoFkC+Q0+bUyEOS1ltgp1MGU03fqHG+dbi9tBFU2Rd9QKiDZFAYrhPh2JUf7rZRIuHRKtOg==", + "requires": { + "prelude-ls": "~1.1.2" + } + } + } + }, + "eslint": { + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.25.0.tgz", + "integrity": "sha512-TVpSovpvCNpLURIScDRB6g5CYu/ZFq9GfX2hLNIV4dSBKxIWojeDODvYl3t0k0VtMxYeR8OXPCFE5+oHMlGfhw==", + "requires": { + "@babel/code-frame": "7.12.11", + "@eslint/eslintrc": "^0.4.0", + "ajv": "^6.10.0", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.0.1", + "doctrine": "^3.0.0", + "enquirer": "^2.3.5", + "eslint-scope": "^5.1.1", + "eslint-utils": "^2.1.0", + "eslint-visitor-keys": "^2.0.0", + "espree": "^7.3.1", + "esquery": "^1.4.0", + "esutils": "^2.0.2", + "file-entry-cache": "^6.0.1", + "functional-red-black-tree": "^1.0.1", + "glob-parent": "^5.0.0", + "globals": "^13.6.0", + "ignore": "^4.0.6", + "import-fresh": "^3.0.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "js-yaml": "^3.13.1", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash": "^4.17.21", + "minimatch": "^3.0.4", + "natural-compare": "^1.4.0", + "optionator": "^0.9.1", + "progress": "^2.0.0", + "regexpp": "^3.1.0", + "semver": "^7.2.1", + "strip-ansi": "^6.0.0", + "strip-json-comments": "^3.1.0", + "table": "^6.0.4", + "text-table": "^0.2.0", + "v8-compile-cache": "^2.0.3" + }, + "dependencies": { + "@babel/code-frame": { + "version": "7.12.11", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.11.tgz", + "integrity": "sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw==", + "requires": { + "@babel/highlight": "^7.10.4" + } + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", + "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "requires": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + } + }, + "globals": { + "version": "13.8.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.8.0.tgz", + "integrity": "sha512-rHtdA6+PDBIjeEvA91rpqzEvk/k3/i7EeNQiryiWuJH0Hw9cpyJMAt2jtbAwUaRdhD+573X4vWw6IcjKPasi9Q==", + "requires": { + "type-fest": "^0.20.2" + } + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" + }, + "ignore": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", + "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==" + }, + "path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==" + }, + "shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "requires": { + "shebang-regex": "^3.0.0" + } + }, + "shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==" + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "requires": { + "has-flag": "^4.0.0" + } + }, + "type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==" + }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "requires": { + "isexe": "^2.0.0" + } + } + } + }, + "eslint-config-airbnb": { + "version": "18.2.1", + "resolved": "https://registry.npmjs.org/eslint-config-airbnb/-/eslint-config-airbnb-18.2.1.tgz", + "integrity": "sha512-glZNDEZ36VdlZWoxn/bUR1r/sdFKPd1mHPbqUtkctgNG4yT2DLLtJ3D+yCV+jzZCc2V1nBVkmdknOJBZ5Hc0fg==", + "dev": true, + "requires": { + "eslint-config-airbnb-base": "^14.2.1", + "object.assign": "^4.1.2", + "object.entries": "^1.1.2" + } + }, + "eslint-config-airbnb-base": { + "version": "14.2.1", + "resolved": "https://registry.npmjs.org/eslint-config-airbnb-base/-/eslint-config-airbnb-base-14.2.1.tgz", + "integrity": "sha512-GOrQyDtVEc1Xy20U7vsB2yAoB4nBlfH5HZJeatRXHleO+OS5Ot+MWij4Dpltw4/DyIkqUfqz1epfhVR5XWWQPA==", + "dev": true, + "requires": { + "confusing-browser-globals": "^1.0.10", + "object.assign": "^4.1.2", + "object.entries": "^1.1.2" + } + }, + "eslint-config-prettier": { + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-6.15.0.tgz", + "integrity": "sha512-a1+kOYLR8wMGustcgAjdydMsQ2A/2ipRPwRKUmfYaSxc9ZPcrku080Ctl6zrZzZNs/U82MjSv+qKREkoq3bJaw==", + "dev": true, + "requires": { + "get-stdin": "^6.0.0" + }, + "dependencies": { + "get-stdin": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-6.0.0.tgz", + "integrity": "sha512-jp4tHawyV7+fkkSKyvjuLZswblUtz+SQKzSWnBbii16BuZksJlU1wuBYXY75r+duh/llF1ur6oNwi+2ZzjKZ7g==", + "dev": true + } + } + }, + "eslint-config-react": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/eslint-config-react/-/eslint-config-react-1.1.7.tgz", + "integrity": "sha512-P4Z6u68wf0BvIvZNu+U8uQsk3DcZ1CcCI1XpUkJlG6vOa+iVcSQLgE01f2DB2kXlKRcT8/3dsH+wveLgvEgbkQ==", + "dev": true + }, + "eslint-config-react-app": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/eslint-config-react-app/-/eslint-config-react-app-6.0.0.tgz", + "integrity": "sha512-bpoAAC+YRfzq0dsTk+6v9aHm/uqnDwayNAXleMypGl6CpxI9oXXscVHo4fk3eJPIn+rsbtNetB4r/ZIidFIE8A==", + "requires": { + "confusing-browser-globals": "^1.0.10" + } + }, + "eslint-import-resolver-node": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.4.tgz", + "integrity": "sha512-ogtf+5AB/O+nM6DIeBUNr2fuT7ot9Qg/1harBfBtaP13ekEWFQEEMP94BCB7zaNW3gyY+8SHYF00rnqYwXKWOA==", + "requires": { + "debug": "^2.6.9", + "resolve": "^1.13.1" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + } + } + }, + "eslint-module-utils": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.6.0.tgz", + "integrity": "sha512-6j9xxegbqe8/kZY8cYpcp0xhbK0EgJlg3g9mib3/miLaExuuwc3n5UEfSnU6hWMbT0FAYVvDbL9RrRgpUeQIvA==", + "requires": { + "debug": "^2.6.9", + "pkg-dir": "^2.0.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", + "requires": { + "locate-path": "^2.0.0" + } + }, + "locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", + "requires": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + }, + "p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "requires": { + "p-try": "^1.0.0" + } + }, + "p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", + "requires": { + "p-limit": "^1.1.0" + } + }, + "p-try": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=" + }, + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=" + }, + "pkg-dir": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-2.0.0.tgz", + "integrity": "sha1-9tXREJ4Z1j7fQo4L1X4Sd3YVM0s=", + "requires": { + "find-up": "^2.1.0" + } + } + } + }, + "eslint-plugin-flowtype": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-flowtype/-/eslint-plugin-flowtype-5.7.2.tgz", + "integrity": "sha512-7Oq/N0+3nijBnYWQYzz/Mp/7ZCpwxYvClRyW/PLAmimY9uLCBvoXsNsERcJdkKceyOjgRbFhhxs058KTrne9Mg==", + "requires": { + "lodash": "^4.17.15", + "string-natural-compare": "^3.0.1" + } + }, + "eslint-plugin-import": { + "version": "2.22.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.22.1.tgz", + "integrity": "sha512-8K7JjINHOpH64ozkAhpT3sd+FswIZTfMZTjdx052pnWrgRCVfp8op9tbjpAk3DdUeI/Ba4C8OjdC0r90erHEOw==", + "requires": { + "array-includes": "^3.1.1", + "array.prototype.flat": "^1.2.3", + "contains-path": "^0.1.0", + "debug": "^2.6.9", + "doctrine": "1.5.0", + "eslint-import-resolver-node": "^0.3.4", + "eslint-module-utils": "^2.6.0", + "has": "^1.0.3", + "minimatch": "^3.0.4", + "object.values": "^1.1.1", + "read-pkg-up": "^2.0.0", + "resolve": "^1.17.0", + "tsconfig-paths": "^3.9.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "doctrine": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-1.5.0.tgz", + "integrity": "sha512-lsGyRuYr4/PIB0txi+Fy2xOMI2dGaTguCaotzFGkVZuKR5usKfcRWIFKNM3QNrU7hh/+w2bwTW+ZeXPK5l8uVg==", + "requires": { + "esutils": "^2.0.2", + "isarray": "^1.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + } + } + }, + "eslint-plugin-jest": { + "version": "24.3.6", + "resolved": "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-24.3.6.tgz", + "integrity": "sha512-WOVH4TIaBLIeCX576rLcOgjNXqP+jNlCiEmRgFTfQtJ52DpwnIQKAVGlGPAN7CZ33bW6eNfHD6s8ZbEUTQubJg==", + "requires": { + "@typescript-eslint/experimental-utils": "^4.0.1" + } + }, + "eslint-plugin-jsx-a11y": { + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.4.1.tgz", + "integrity": "sha512-0rGPJBbwHoGNPU73/QCLP/vveMlM1b1Z9PponxO87jfr6tuH5ligXbDT6nHSSzBC8ovX2Z+BQu7Bk5D/Xgq9zg==", + "requires": { + "@babel/runtime": "^7.11.2", + "aria-query": "^4.2.2", + "array-includes": "^3.1.1", + "ast-types-flow": "^0.0.7", + "axe-core": "^4.0.2", + "axobject-query": "^2.2.0", + "damerau-levenshtein": "^1.0.6", + "emoji-regex": "^9.0.0", + "has": "^1.0.3", + "jsx-ast-utils": "^3.1.0", + "language-tags": "^1.0.5" + }, + "dependencies": { + "emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==" + } + } + }, + "eslint-plugin-prettier": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-3.4.0.tgz", + "integrity": "sha512-UDK6rJT6INSfcOo545jiaOwB701uAIt2/dR7WnFQoGCVl1/EMqdANBmwUaqqQ45aXprsTGzSa39LI1PyuRBxxw==", + "dev": true, + "requires": { + "prettier-linter-helpers": "^1.0.0" + } + }, + "eslint-plugin-react": { + "version": "7.23.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.23.2.tgz", + "integrity": "sha512-AfjgFQB+nYszudkxRkTFu0UR1zEQig0ArVMPloKhxwlwkzaw/fBiH0QWcBBhZONlXqQC51+nfqFrkn4EzHcGBw==", + "requires": { + "array-includes": "^3.1.3", + "array.prototype.flatmap": "^1.2.4", + "doctrine": "^2.1.0", + "has": "^1.0.3", + "jsx-ast-utils": "^2.4.1 || ^3.0.0", + "minimatch": "^3.0.4", + "object.entries": "^1.1.3", + "object.fromentries": "^2.0.4", + "object.values": "^1.1.3", + "prop-types": "^15.7.2", + "resolve": "^2.0.0-next.3", + "string.prototype.matchall": "^4.0.4" + }, + "dependencies": { + "doctrine": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "requires": { + "esutils": "^2.0.2" + } + }, + "resolve": { + "version": "2.0.0-next.3", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.3.tgz", + "integrity": "sha512-W8LucSynKUIDu9ylraa7ueVZ7hc0uAgJBxVsQSKOXOyle8a93qXhcz+XAXZ8bIq2d6i4Ehddn6Evt+0/UwKk6Q==", + "requires": { + "is-core-module": "^2.2.0", + "path-parse": "^1.0.6" + } + } + } + }, + "eslint-plugin-react-hooks": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.2.0.tgz", + "integrity": "sha512-623WEiZJqxR7VdxFCKLI6d6LLpwJkGPYKODnkH3D7WpOG5KM8yWueBd8TLsNAetEJNF5iJmolaAKO3F8yzyVBQ==" + }, + "eslint-plugin-testing-library": { + "version": "3.10.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-testing-library/-/eslint-plugin-testing-library-3.10.2.tgz", + "integrity": "sha512-WAmOCt7EbF1XM8XfbCKAEzAPnShkNSwcIsAD2jHdsMUT9mZJPjLCG7pMzbcC8kK366NOuGip8HKLDC+Xk4yIdA==", + "requires": { + "@typescript-eslint/experimental-utils": "^3.10.1" + }, + "dependencies": { + "@typescript-eslint/experimental-utils": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-3.10.1.tgz", + "integrity": "sha512-DewqIgscDzmAfd5nOGe4zm6Bl7PKtMG2Ad0KG8CUZAHlXfAKTF9Ol5PXhiMh39yRL2ChRH1cuuUGOcVyyrhQIw==", + "requires": { + "@types/json-schema": "^7.0.3", + "@typescript-eslint/types": "3.10.1", + "@typescript-eslint/typescript-estree": "3.10.1", + "eslint-scope": "^5.0.0", + "eslint-utils": "^2.0.0" + } + }, + "@typescript-eslint/types": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-3.10.1.tgz", + "integrity": "sha512-+3+FCUJIahE9q0lDi1WleYzjCwJs5hIsbugIgnbB+dSCYUxl8L6PwmsyOPFZde2hc1DlTo/xnkOgiTLSyAbHiQ==" + }, + "@typescript-eslint/typescript-estree": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-3.10.1.tgz", + "integrity": "sha512-QbcXOuq6WYvnB3XPsZpIwztBoquEYLXh2MtwVU+kO8jgYCiv4G5xrSP/1wg4tkvrEE+esZVquIPX/dxPlePk1w==", + "requires": { + "@typescript-eslint/types": "3.10.1", + "@typescript-eslint/visitor-keys": "3.10.1", + "debug": "^4.1.1", + "glob": "^7.1.6", + "is-glob": "^4.0.1", + "lodash": "^4.17.15", + "semver": "^7.3.2", + "tsutils": "^3.17.1" + } + }, + "@typescript-eslint/visitor-keys": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-3.10.1.tgz", + "integrity": "sha512-9JgC82AaQeglebjZMgYR5wgmfUdUc+EitGUUMW8u2nDckaeimzW+VsoLV6FoimPv2id3VQzfjwBxEMVz08ameQ==", + "requires": { + "eslint-visitor-keys": "^1.1.0" + } + }, + "eslint-visitor-keys": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", + "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==" + } + } + }, + "eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "requires": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + } + }, + "eslint-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", + "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", + "requires": { + "eslint-visitor-keys": "^1.1.0" + }, + "dependencies": { + "eslint-visitor-keys": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", + "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==" + } + } + }, + "eslint-visitor-keys": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.0.0.tgz", + "integrity": "sha512-QudtT6av5WXels9WjIM7qz1XD1cWGvX4gGXvp/zBn9nXG02D0utdU3Em2m/QjTnrsk6bBjmCygl3rmj118msQQ==" + }, + "eslint-webpack-plugin": { + "version": "2.5.4", + "resolved": "https://registry.npmjs.org/eslint-webpack-plugin/-/eslint-webpack-plugin-2.5.4.tgz", + "integrity": "sha512-7rYh0m76KyKSDE+B+2PUQrlNS4HJ51t3WKpkJg6vo2jFMbEPTG99cBV0Dm7LXSHucN4WGCG65wQcRiTFrj7iWw==", + "requires": { + "@types/eslint": "^7.2.6", + "arrify": "^2.0.1", + "jest-worker": "^26.6.2", + "micromatch": "^4.0.2", + "normalize-path": "^3.0.0", + "schema-utils": "^3.0.0" + }, + "dependencies": { + "schema-utils": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.0.0.tgz", + "integrity": "sha512-6D82/xSzO094ajanoOSbe4YvXWMfn2A//8Y1+MUqFAJul5Bs+yn36xbK9OtNDcRVSBJ9jjeoXftM6CfztsjOAA==", + "requires": { + "@types/json-schema": "^7.0.6", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + } + } + } + }, + "espree": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-7.3.1.tgz", + "integrity": "sha512-v3JCNCE64umkFpmkFGqzVKsOT0tN1Zr+ueqLZfpV1Ob8e+CEgPWa+OxCoGH3tnhimMKIaBm4m/vaRpJ/krRz2g==", + "requires": { + "acorn": "^7.4.0", + "acorn-jsx": "^5.3.1", + "eslint-visitor-keys": "^1.3.0" + }, + "dependencies": { + "eslint-visitor-keys": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", + "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==" + } + } + }, + "esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==" + }, + "esquery": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", + "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", + "requires": { + "estraverse": "^5.1.0" + }, + "dependencies": { + "estraverse": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", + "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==" + } + } + }, + "esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "requires": { + "estraverse": "^5.2.0" + }, + "dependencies": { + "estraverse": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", + "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==" + } + } + }, + "estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==" + }, + "estree-walker": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-1.0.1.tgz", + "integrity": "sha512-1fMXF3YP4pZZVozF8j/ZLfvnR8NSIljt56UhbZ5PeeDmmGHpgpdwQt7ITlGvYaQukCvuBRMLEiKiYC+oeIg4cg==" + }, + "esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==" + }, + "etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==" + }, + "eventemitter3": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==" + }, + "events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==" + }, + "eventsource": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-1.1.0.tgz", + "integrity": "sha512-VSJjT5oCNrFvCS6igjzPAt5hBzQ2qPBFIbJ03zLI9SE0mxwZpMw6BfJrbFHm1a141AavMEB8JHmBhWAd66PfCg==", + "requires": { + "original": "^1.0.0" + } + }, + "evp_bytestokey": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz", + "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==", + "requires": { + "md5.js": "^1.3.4", + "safe-buffer": "^5.1.1" + } + }, + "exec-sh": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/exec-sh/-/exec-sh-0.3.6.tgz", + "integrity": "sha512-nQn+hI3yp+oD0huYhKwvYI32+JFeq+XkNcD1GAo3Y/MjxsfVGmrrzrnzjWiNY6f+pUCP440fThsFh5gZrRAU/w==" + }, + "execa": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", + "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", + "requires": { + "cross-spawn": "^6.0.0", + "get-stream": "^4.0.0", + "is-stream": "^1.1.0", + "npm-run-path": "^2.0.0", + "p-finally": "^1.0.0", + "signal-exit": "^3.0.0", + "strip-eof": "^1.0.0" + } + }, + "exit": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", + "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==" + }, + "exit-on-epipe": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/exit-on-epipe/-/exit-on-epipe-1.0.1.tgz", + "integrity": "sha512-h2z5mrROTxce56S+pnvAV890uu7ls7f1kEvVGJbw1OlFH3/mlJ5bkXu0KRyW94v37zzHPiUd55iLn3DA7TjWpw==" + }, + "expand-brackets": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", + "integrity": "sha512-w/ozOKR9Obk3qoWeY/WDi6MFta9AoMR+zud60mdnbniMcBxRuFJyDt2LdX/14A1UABeqk+Uk+LDfUpvoGKppZA==", + "requires": { + "debug": "^2.3.3", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "posix-character-classes": "^0.1.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==", + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "requires": { + "is-extendable": "^0.1.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + } + } + }, + "expand-tilde": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/expand-tilde/-/expand-tilde-2.0.2.tgz", + "integrity": "sha512-A5EmesHW6rfnZ9ysHQjPdJRni0SRar0tjtG5MNtm9n5TUvsYU8oozprtRD4AqHxcZWWlVuAmQo2nWKfN9oyjTw==", + "dev": true, + "requires": { + "homedir-polyfill": "^1.0.1" + } + }, + "expect": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/expect/-/expect-26.6.2.tgz", + "integrity": "sha512-9/hlOBkQl2l/PLHJx6JjoDF6xPKcJEsUlWKb23rKE7KzeDqUZKXKNMW27KIue5JMdBV9HgmoJPcc8HtO85t9IA==", + "requires": { + "@jest/types": "^26.6.2", + "ansi-styles": "^4.0.0", + "jest-get-type": "^26.3.0", + "jest-matcher-utils": "^26.6.2", + "jest-message-util": "^26.6.2", + "jest-regex-util": "^26.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "requires": { + "color-convert": "^2.0.1" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + } + } + }, + "express": { + "version": "4.17.1", + "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz", + "integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==", + "requires": { + "accepts": "~1.3.7", + "array-flatten": "1.1.1", + "body-parser": "1.19.0", + "content-disposition": "0.5.3", + "content-type": "~1.0.4", + "cookie": "0.4.0", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "~1.1.2", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "~1.1.2", + "fresh": "0.5.2", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.5", + "qs": "6.7.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.1.2", + "send": "0.17.1", + "serve-static": "1.14.1", + "setprototypeof": "1.1.1", + "statuses": "~1.5.0", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "dependencies": { + "array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" + }, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + }, + "path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" + }, + "qs": { + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", + "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==" + } + } + }, + "ext": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/ext/-/ext-1.4.0.tgz", + "integrity": "sha512-Key5NIsUxdqKg3vIsdw9dSuXpPCQ297y6wBjL30edxwPgt2E44WcWBZey/ZvUc6sERLTxKdyCu4gZFmUbk1Q7A==", + "requires": { + "type": "^2.0.0" + }, + "dependencies": { + "type": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/type/-/type-2.5.0.tgz", + "integrity": "sha512-180WMDQaIMm3+7hGXWf12GtdniDEy7nYcyFMKJn/eZz/6tSLXrUN9V0wKSbMjej0I1WHWbpREDEKHtqPQa9NNw==" + } + } + }, + "extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" + }, + "extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha512-BwY5b5Ql4+qZoefgMj2NUmx+tehVTH/Kf4k1ZEtOHNFcm2wSxMRo992l6X3TIgni2eZVTZ85xMOjF31fwZAj6Q==", + "requires": { + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" + }, + "dependencies": { + "is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "requires": { + "is-plain-object": "^2.0.4" + } + } + } + }, + "external-editor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", + "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", + "dev": true, + "requires": { + "chardet": "^0.7.0", + "iconv-lite": "^0.4.24", + "tmp": "^0.0.33" + } + }, + "extglob": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", + "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", + "requires": { + "array-unique": "^0.3.2", + "define-property": "^1.0.0", + "expand-brackets": "^2.1.4", + "extend-shallow": "^2.0.1", + "fragment-cache": "^0.2.1", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha512-cZTYKFWspt9jZsMscWo8sc/5lbPC9Q0N5nBLgb+Yd915iL3udB1uFgS3B8YCx66UVHq018DAVFoee7x+gxggeA==", + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "requires": { + "is-extendable": "^0.1.0" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } + } + }, + "extsprintf": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", + "integrity": "sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g==" + }, + "fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" + }, + "fast-diff": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.2.0.tgz", + "integrity": "sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w==", + "dev": true + }, + "fast-glob": { + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.5.tgz", + "integrity": "sha512-2DtFcgT68wiTTiwZ2hNdJfcHNke9XOfnwmBRWXhmeKM8rF0TGwmC/Qto3S7RoZKp5cilZbxzO5iTNTQsJ+EeDg==", + "requires": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.0", + "merge2": "^1.3.0", + "micromatch": "^4.0.2", + "picomatch": "^2.2.1" + } + }, + "fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" + }, + "fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==" + }, + "fastq": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.11.0.tgz", + "integrity": "sha512-7Eczs8gIPDrVzT+EksYBcupqMyxSHXXrHOLRRxU2/DicV8789MRBRR8+Hc2uWzUupOs4YS4JzBmBxjjCVBxD/g==", + "requires": { + "reusify": "^1.0.4" + } + }, + "faye-websocket": { + "version": "0.11.3", + "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.3.tgz", + "integrity": "sha512-D2y4bovYpzziGgbHYtGCMjlJM36vAl/y+xUyn1C+FVx8szd1E+86KwVw6XvYSzOP8iMpm1X0I4xJD+QtUb36OA==", + "requires": { + "websocket-driver": ">=0.5.1" + } + }, + "fb-watchman": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.1.tgz", + "integrity": "sha512-DkPJKQeY6kKwmuMretBhr7G6Vodr7bFwDYTXIkfG1gjvNpaxBTQV3PbXg6bR1c1UP4jPOX0jHUbbHANL9vRjVg==", + "requires": { + "bser": "2.1.1" + } + }, + "figgy-pudding": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/figgy-pudding/-/figgy-pudding-3.5.2.tgz", + "integrity": "sha512-0btnI/H8f2pavGMN8w40mlSKOfTK2SVJmBfBeVIj3kNw0swwgzyRq0d5TJVOwodFmtvpPeWPN/MCcfuWF0Ezbw==" + }, + "figures": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz", + "integrity": "sha512-Oa2M9atig69ZkfwiApY8F2Yy+tzMbazyvqv21R0NsSC8floSOC09BbT1ITWAdoMGQvJ/aZnR1KMwdx9tvHnTNA==", + "dev": true, + "requires": { + "escape-string-regexp": "^1.0.5" + } + }, + "file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "requires": { + "flat-cache": "^3.0.4" + } + }, + "file-loader": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/file-loader/-/file-loader-6.1.1.tgz", + "integrity": "sha512-Klt8C4BjWSXYQAfhpYYkG4qHNTna4toMHEbWrI5IuVoxbU6uiDKeKAP99R8mmbJi3lvewn/jQBOgU4+NS3tDQw==", + "requires": { + "loader-utils": "^2.0.0", + "schema-utils": "^3.0.0" + }, + "dependencies": { + "schema-utils": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.0.0.tgz", + "integrity": "sha512-6D82/xSzO094ajanoOSbe4YvXWMfn2A//8Y1+MUqFAJul5Bs+yn36xbK9OtNDcRVSBJ9jjeoXftM6CfztsjOAA==", + "requires": { + "@types/json-schema": "^7.0.6", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + } + } + } + }, + "file-saver": { + "version": "2.0.5", + "resolved": "https://landmarkcu.pkgs.visualstudio.com/_packaging/LCU-Internal/npm/registry/file-saver/-/file-saver-2.0.5.tgz", + "integrity": "sha1-1hz+LOBZ9BTYmendbUEH7iVnDDg=" + }, + "file-uri-to-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", + "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", + "optional": true + }, + "filesize": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/filesize/-/filesize-6.1.0.tgz", + "integrity": "sha512-LpCHtPQ3sFx67z+uh2HnSyWSLLu5Jxo21795uRDuar/EOuYWXib5EmPaGIBuSnRqH2IODiKA2k5re/K9OnN/Yg==" + }, + "fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "requires": { + "to-regex-range": "^5.0.1" + } + }, + "finalhandler": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", + "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", + "requires": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "statuses": "~1.5.0", + "unpipe": "~1.0.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + } + } + }, + "find-cache-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-2.1.0.tgz", + "integrity": "sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ==", + "requires": { + "commondir": "^1.0.1", + "make-dir": "^2.0.0", + "pkg-dir": "^3.0.0" + } + }, + "find-node-modules": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/find-node-modules/-/find-node-modules-2.0.0.tgz", + "integrity": "sha512-8MWIBRgJi/WpjjfVXumjPKCtmQ10B+fjx6zmSA+770GMJirLhWIzg8l763rhjl9xaeaHbnxPNRQKq2mgMhr+aw==", + "dev": true, + "requires": { + "findup-sync": "^3.0.0", + "merge": "^1.2.1" + } + }, + "find-parent-dir": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/find-parent-dir/-/find-parent-dir-0.3.0.tgz", + "integrity": "sha1-M8RLQpqysvBkYpnF+fcY83b/jVQ=", + "dev": true + }, + "find-root": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz", + "integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==", + "dev": true + }, + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, + "findup": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/findup/-/findup-0.1.5.tgz", + "integrity": "sha512-Udxo3C9A6alt2GZ2MNsgnIvX7De0V3VGxeP/x98NSVgSlizcDHdmJza61LI7zJy4OEtSiJyE72s0/+tBl5/ZxA==", + "dev": true, + "requires": { + "colors": "~0.6.0-1", + "commander": "~2.1.0" + }, + "dependencies": { + "commander": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.1.0.tgz", + "integrity": "sha512-J2wnb6TKniXNOtoHS8TSrG9IOQluPrsmyAJ8oCUJOBmv+uLBCyPYAZkD2jFvw2DCzIXNnISIM01NIvr35TkBMQ==", + "dev": true + } + } + }, + "findup-sync": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-3.0.0.tgz", + "integrity": "sha512-YbffarhcicEhOrm4CtrwdKBdCuz576RLdhJDsIfvNtxUuhdRet1qZcsMjqbePtAseKdAnDyM/IyXbu7PRPRLYg==", + "dev": true, + "requires": { + "detect-file": "^1.0.0", + "is-glob": "^4.0.0", + "micromatch": "^3.0.4", + "resolve-dir": "^1.0.1" + }, + "dependencies": { + "braces": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "dev": true, + "requires": { + "arr-flatten": "^1.1.0", + "array-unique": "^0.3.2", + "extend-shallow": "^2.0.1", + "fill-range": "^4.0.0", + "isobject": "^3.0.1", + "repeat-element": "^1.1.2", + "snapdragon": "^0.8.1", + "snapdragon-node": "^2.0.1", + "split-string": "^3.0.2", + "to-regex": "^3.0.1" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha512-VcpLTWqWDiTerugjj8e3+esbg+skS3M9e54UuR3iCeIDMXCLTsAH8hTSzDQU/X6/6t3eYkOKoZSef2PlU6U1XQ==", + "dev": true, + "requires": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha512-4cboCqIpliH+mAvFNegjZQ4kgKc3ZUhQVr3HvWbSh5q3WH2v82ct+T2Y1hdU5Gdtorx/cLifQjqCbL7bpznLTg==", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", + "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "dev": true, + "requires": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "braces": "^2.3.1", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "extglob": "^2.0.4", + "fragment-cache": "^0.2.1", + "kind-of": "^6.0.2", + "nanomatch": "^1.2.9", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.2" + } + }, + "to-regex-range": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", + "integrity": "sha512-ZZWNfCjUokXXDGXFpZehJIkZqq91BcULFq/Pi7M5i4JnxXdhMKAK682z8bCW3o8Hj1wuuzoKcW3DfVzaP6VuNg==", + "dev": true, + "requires": { + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" + } + } + } + }, + "flat-cache": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", + "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", + "requires": { + "flatted": "^3.1.0", + "rimraf": "^3.0.2" + } + }, + "flatted": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.1.1.tgz", + "integrity": "sha512-zAoAQiudy+r5SvnSw3KJy5os/oRJYHzrzja/tBDqrZtNhUw8bt6y8OBzMWcjWr+8liV8Eb6yOhw8WZ7VFZ5ZzA==" + }, + "flatten": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/flatten/-/flatten-1.0.3.tgz", + "integrity": "sha512-dVsPA/UwQ8+2uoFe5GHtiBMu48dWLTdsuEd7CKGlZlD78r1TTWBvDuFaFGKCo/ZfEr95Uk56vZoX86OsHkUeIg==" + }, + "flush-write-stream": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/flush-write-stream/-/flush-write-stream-1.1.1.tgz", + "integrity": "sha512-3Z4XhFZ3992uIq0XOqb9AreonueSYphE6oYbpt5+3u06JWklbsPkNv3ZKkP9Bz/r+1MWCaMoSQ28P85+1Yc77w==", + "requires": { + "inherits": "^2.0.3", + "readable-stream": "^2.3.6" + } + }, + "follow-redirects": { + "version": "1.14.0", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.0.tgz", + "integrity": "sha512-0vRwd7RKQBTt+mgu87mtYeofLFZpTas2S9zY+jIeuLJMNvudIgF52nr19q40HOwH5RrhWIPuj9puybzSJiRrVg==" + }, + "font-awesome": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/font-awesome/-/font-awesome-4.7.0.tgz", + "integrity": "sha512-U6kGnykA/6bFmg1M/oT9EkFeIYv7JlX3bozwQJWiiLz6L0w3F5vBVPxHlwyX/vtNq1ckcpRKOB9f2Qal/VtFpg==" + }, + "for-in": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", + "integrity": "sha512-7EwmXrOjyL+ChxMhmG5lnW9MPt1aIeZEwKhQzoBUdTV0N3zuwWDZYVJatDvZ2OyzPUvdIAZDsCetk3coyMfcnQ==" + }, + "forever-agent": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "integrity": "sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw==" + }, + "fork-ts-checker-webpack-plugin": { + "version": "4.1.6", + "resolved": "https://registry.npmjs.org/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-4.1.6.tgz", + "integrity": "sha512-DUxuQaKoqfNne8iikd14SAkh5uw4+8vNifp6gmA73yYNS6ywLIWSLD/n/mBzHQRpW3J7rbATEakmiA8JvkTyZw==", + "requires": { + "@babel/code-frame": "^7.5.5", + "chalk": "^2.4.1", + "micromatch": "^3.1.10", + "minimatch": "^3.0.4", + "semver": "^5.6.0", + "tapable": "^1.0.0", + "worker-rpc": "^0.1.0" + }, + "dependencies": { + "braces": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "requires": { + "arr-flatten": "^1.1.0", + "array-unique": "^0.3.2", + "extend-shallow": "^2.0.1", + "fill-range": "^4.0.0", + "isobject": "^3.0.1", + "repeat-element": "^1.1.2", + "snapdragon": "^0.8.1", + "snapdragon-node": "^2.0.1", + "split-string": "^3.0.2", + "to-regex": "^3.0.1" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha512-VcpLTWqWDiTerugjj8e3+esbg+skS3M9e54UuR3iCeIDMXCLTsAH8hTSzDQU/X6/6t3eYkOKoZSef2PlU6U1XQ==", + "requires": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha512-4cboCqIpliH+mAvFNegjZQ4kgKc3ZUhQVr3HvWbSh5q3WH2v82ct+T2Y1hdU5Gdtorx/cLifQjqCbL7bpznLTg==", + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", + "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "requires": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "braces": "^2.3.1", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "extglob": "^2.0.4", + "fragment-cache": "^0.2.1", + "kind-of": "^6.0.2", + "nanomatch": "^1.2.9", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.2" + } + }, + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" + }, + "to-regex-range": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", + "integrity": "sha512-ZZWNfCjUokXXDGXFpZehJIkZqq91BcULFq/Pi7M5i4JnxXdhMKAK682z8bCW3o8Hj1wuuzoKcW3DfVzaP6VuNg==", + "requires": { + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" + } + } + } + }, + "form-data": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", + "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + } + }, + "forwarded": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", + "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=" + }, + "frac": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/frac/-/frac-1.1.2.tgz", + "integrity": "sha512-w/XBfkibaTl3YDqASwfDUqkna4Z2p9cFSr1aHDt0WoMTECnRfBOv2WArlZILlqgWlmdIlALXGpM2AOhEk5W3IA==" + }, + "fragment-cache": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", + "integrity": "sha512-GMBAbW9antB8iZRHLoGw0b3HANt57diZYFO/HL1JGIC1MjKrdmhxvrJbupnVvpys0zsz7yBApXdQyfepKly2kA==", + "requires": { + "map-cache": "^0.2.2" + } + }, + "fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==" + }, + "from2": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/from2/-/from2-2.3.0.tgz", + "integrity": "sha512-OMcX/4IC/uqEPVgGeyfN22LJk6AZrMkRZHxcHBMBvHScDGgwTm2GT2Wkgtocyd3JfZffjj2kYUDXXII0Fk9W0g==", + "requires": { + "inherits": "^2.0.1", + "readable-stream": "^2.0.0" + } + }, + "fs-access": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/fs-access/-/fs-access-1.0.1.tgz", + "integrity": "sha512-05cXDIwNbFaoFWaz5gNHlUTbH5whiss/hr/ibzPd4MH3cR4w0ZKeIPiVdbyJurg3O5r/Bjpvn9KOb1/rPMf3nA==", + "dev": true, + "requires": { + "null-check": "^1.0.0" + } + }, + "fs-extra": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", + "requires": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + } + }, + "fs-minipass": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", + "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", + "requires": { + "minipass": "^3.0.0" + } + }, + "fs-write-stream-atomic": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/fs-write-stream-atomic/-/fs-write-stream-atomic-1.0.10.tgz", + "integrity": "sha512-gehEzmPn2nAwr39eay+x3X34Ra+M2QlVUTLhkXPjWdeO8RF9kszk116avgBJM3ZyNHgHXBNx+VmPaFC36k0PzA==", + "requires": { + "graceful-fs": "^4.1.2", + "iferr": "^0.1.5", + "imurmurhash": "^0.1.4", + "readable-stream": "1 || 2" + } + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" + }, + "fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "optional": true + }, + "fstream": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.12.tgz", + "integrity": "sha512-WvJ193OHa0GHPEL+AycEJgxvBEwyfRkN1vhjca23OaPVMCaLCXTd5qAu82AjTcgP1UJmytkOKb63Ypde7raDIg==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "inherits": "~2.0.0", + "mkdirp": ">=0.5 0", + "rimraf": "2" + }, + "dependencies": { + "rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + } + } + }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" + }, + "function.prototype.name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.4.tgz", + "integrity": "sha512-iqy1pIotY/RmhdFZygSSlW0wko2yxkSCKqsuv4pr8QESohpYyG/Z7B/XXvPRKTJS//960rgguE5mSRUsDdaJrQ==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.18.0-next.2", + "functions-have-names": "^1.2.2" + } + }, + "functional-red-black-tree": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", + "integrity": "sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g==" + }, + "functions-have-names": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.2.tgz", + "integrity": "sha512-bLgc3asbWdwPbx2mNk2S49kmJCuQeu0nfmaOgbs8WIyzzkw3r4htszdIi9Q9EMezDPTYuJx2wvjZ/EwgAthpnA==", + "dev": true + }, + "gauge": { + "version": "2.7.4", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", + "integrity": "sha512-14x4kjc6lkD3ltw589k0NrPD6cCNTD6CWoVUNpB85+DrtONoZn+Rug6xZU5RvSC4+TZPxA5AnBibQYAvZn41Hg==", + "dev": true, + "requires": { + "aproba": "^1.0.3", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.0", + "object-assign": "^4.1.0", + "signal-exit": "^3.0.0", + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wide-align": "^1.1.0" + }, + "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha512-1pqUqRjkhPJ9miNq9SwMfdvi6lBJcd6eFxvfaivQhaH3SgisfiuudvFntdKOmxuee/77l+FPjKrQjWvmPjWrRw==", + "dev": true, + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha512-0XsVpQLnVCXHJfyEs8tC0zpTVIr5PKKsQtkT29IwupnPTjtPmQ3xT/4yCREF9hYkV/3M3kzcUTSAZT6a6h81tw==", + "dev": true, + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + } + } + }, + "gaze": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/gaze/-/gaze-1.1.3.tgz", + "integrity": "sha512-BRdNm8hbWzFzWHERTrejLqwHDfS4GibPoq5wjTPIoJHoBtKGPg3xAFfxmM+9ztbXelxcf2hwQcaz1PtmFeue8g==", + "dev": true, + "requires": { + "globule": "^1.0.0" + } + }, + "gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==" + }, + "get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==" + }, + "get-intrinsic": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz", + "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==", + "requires": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1" + } + }, + "get-own-enumerable-property-symbols": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.2.tgz", + "integrity": "sha512-I0UBV/XOz1XkIJHEUDMZAbzCThU/H8DxmSfmdGcKPnVhu2VfFqr34jr9777IyaTYvxjedWhqVIilEDsCdP5G6g==" + }, + "get-package-type": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", + "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==" + }, + "get-pkg-repo": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/get-pkg-repo/-/get-pkg-repo-1.4.0.tgz", + "integrity": "sha1-xztInAbYDMVTbCyFP54FIyBWly0=", + "dev": true, + "requires": { + "hosted-git-info": "^2.1.4", + "meow": "^3.3.0", + "normalize-package-data": "^2.3.0", + "parse-github-repo-url": "^1.3.0", + "through2": "^2.0.0" + } + }, + "get-stdin": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz", + "integrity": "sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4=", + "dev": true + }, + "get-stream": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", + "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", + "requires": { + "pump": "^3.0.0" + } + }, + "get-value": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", + "integrity": "sha512-Ln0UQDlxH1BapMu3GPtf7CuYNwRZf2gwCuPqbyG6pB8WfmFpzqcy4xtAaAMUhnNqjMKTiCPZG2oMT3YSx8U2NA==" + }, + "getpass": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "integrity": "sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng==", + "requires": { + "assert-plus": "^1.0.0" + } + }, + "git-describe": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/git-describe/-/git-describe-4.0.4.tgz", + "integrity": "sha512-L1X9OO1e4MusB4PzG9LXeXCQifRvyuoHTpuuZ521Qyxn/B0kWHWEOtsT4LsSfSNacZz0h4ZdYDsDG7f+SrA3hg==", + "dev": true, + "requires": { + "lodash": "^4.17.11", + "semver": "^5.6.0" + }, + "dependencies": { + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true, + "optional": true + } + } + }, + "git-raw-commits": { + "version": "2.0.10", + "resolved": "https://registry.npmjs.org/git-raw-commits/-/git-raw-commits-2.0.10.tgz", + "integrity": "sha512-sHhX5lsbG9SOO6yXdlwgEMQ/ljIn7qMpAbJZCGfXX2fq5T8M5SrDnpYk9/4HswTildcIqatsWa91vty6VhWSaQ==", + "dev": true, + "requires": { + "dargs": "^7.0.0", + "lodash": "^4.17.15", + "meow": "^8.0.0", + "split2": "^3.0.0", + "through2": "^4.0.0" + }, + "dependencies": { + "camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true + }, + "camelcase-keys": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-6.2.2.tgz", + "integrity": "sha512-YrwaA0vEKazPBkn0ipTiMpSajYDSe+KjQfrjhcBMxJt/znbvlHd8Pw/Vamaz5EB4Wfhs3SUR3Z9mwRu/P3s3Yg==", + "dev": true, + "requires": { + "camelcase": "^5.3.1", + "map-obj": "^4.0.0", + "quick-lru": "^4.0.1" + } + }, + "hosted-git-info": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.0.2.tgz", + "integrity": "sha512-c9OGXbZ3guC/xOlCg1Ci/VgWlwsqDv1yMQL1CWqXDL0hDjXuNcq0zuR4xqPSuasI3kqFDhqSyTjREz5gzq0fXg==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "requires": { + "yallist": "^4.0.0" + } + }, + "map-obj": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-4.2.1.tgz", + "integrity": "sha512-+WA2/1sPmDj1dlvvJmB5G6JKfY9dpn7EVBUL06+y6PoljPkh+6V1QihwxNkbcGxCRjt2b0F9K0taiCuo7MbdFQ==", + "dev": true + }, + "meow": { + "version": "8.1.2", + "resolved": "https://registry.npmjs.org/meow/-/meow-8.1.2.tgz", + "integrity": "sha512-r85E3NdZ+mpYk1C6RjPFEMSE+s1iZMuHtsHAqY0DT3jZczl0diWUZ8g6oU7h0M9cD2EL+PzaYghhCLzR0ZNn5Q==", + "dev": true, + "requires": { + "@types/minimist": "^1.2.0", + "camelcase-keys": "^6.2.2", + "decamelize-keys": "^1.1.0", + "hard-rejection": "^2.1.0", + "minimist-options": "4.1.0", + "normalize-package-data": "^3.0.0", + "read-pkg-up": "^7.0.1", + "redent": "^3.0.0", + "trim-newlines": "^3.0.0", + "type-fest": "^0.18.0", + "yargs-parser": "^20.2.3" + } + }, + "normalize-package-data": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-3.0.2.tgz", + "integrity": "sha512-6CdZocmfGaKnIHPVFhJJZ3GuR8SsLKvDANFp47Jmy51aKIr8akjAWTSxtpI+MBgBFdSMRyo4hMpDlT6dTffgZg==", + "dev": true, + "requires": { + "hosted-git-info": "^4.0.1", + "resolve": "^1.20.0", + "semver": "^7.3.4", + "validate-npm-package-license": "^3.0.1" + } + }, + "read-pkg": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", + "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", + "dev": true, + "requires": { + "@types/normalize-package-data": "^2.4.0", + "normalize-package-data": "^2.5.0", + "parse-json": "^5.0.0", + "type-fest": "^0.6.0" + }, + "dependencies": { + "hosted-git-info": { + "version": "2.8.9", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", + "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", + "dev": true + }, + "normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "dev": true, + "requires": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + }, + "type-fest": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", + "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", + "dev": true + } + } + }, + "read-pkg-up": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz", + "integrity": "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==", + "dev": true, + "requires": { + "find-up": "^4.1.0", + "read-pkg": "^5.2.0", + "type-fest": "^0.8.1" + }, + "dependencies": { + "type-fest": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "dev": true + } + } + }, + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + }, + "resolve": { + "version": "1.20.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz", + "integrity": "sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==", + "dev": true, + "requires": { + "is-core-module": "^2.2.0", + "path-parse": "^1.0.6" + } + }, + "semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "through2": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/through2/-/through2-4.0.2.tgz", + "integrity": "sha512-iOqSav00cVxEEICeD7TjLB1sueEL+81Wpzp2bY17uZjZN0pWZPuo4suZ/61VujxmqSGFfgOcNuTZ85QJwNZQpw==", + "dev": true, + "requires": { + "readable-stream": "3" + } + }, + "trim-newlines": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-3.0.0.tgz", + "integrity": "sha512-C4+gOpvmxaSMKuEf9Qc134F1ZuOHVXKRbtEflf4NTtuuJDEIJ9p5PXsalL8SkeRw+qit1Mo+yuvMPAKwWg/1hA==", + "dev": true + }, + "type-fest": { + "version": "0.18.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.18.1.tgz", + "integrity": "sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw==", + "dev": true + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "yargs-parser": { + "version": "20.2.7", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.7.tgz", + "integrity": "sha512-FiNkvbeHzB/syOjIUxFDCnhSfzAL8R5vs40MgLFBorXACCOAEaWu0gRZl14vG8MR9AOJIZbmkjhusqBYZ3HTHw==", + "dev": true + } + } + }, + "git-remote-origin-url": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/git-remote-origin-url/-/git-remote-origin-url-2.0.0.tgz", + "integrity": "sha512-eU+GGrZgccNJcsDH5LkXR3PB9M958hxc7sbA8DFJjrv9j4L2P/eZfKhM+QD6wyzpiv+b1BpK0XrYCxkovtjSLw==", + "dev": true, + "requires": { + "gitconfiglocal": "^1.0.0", + "pify": "^2.3.0" + } + }, + "git-semver-tags": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/git-semver-tags/-/git-semver-tags-4.1.1.tgz", + "integrity": "sha512-OWyMt5zBe7xFs8vglMmhM9lRQzCWL3WjHtxNNfJTMngGym7pC1kh8sP6jevfydJ6LP3ZvGxfb6ABYgPUM0mtsA==", + "dev": true, + "requires": { + "meow": "^8.0.0", + "semver": "^6.0.0" + }, + "dependencies": { + "camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true + }, + "camelcase-keys": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-6.2.2.tgz", + "integrity": "sha512-YrwaA0vEKazPBkn0ipTiMpSajYDSe+KjQfrjhcBMxJt/znbvlHd8Pw/Vamaz5EB4Wfhs3SUR3Z9mwRu/P3s3Yg==", + "dev": true, + "requires": { + "camelcase": "^5.3.1", + "map-obj": "^4.0.0", + "quick-lru": "^4.0.1" + } + }, + "hosted-git-info": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.0.2.tgz", + "integrity": "sha512-c9OGXbZ3guC/xOlCg1Ci/VgWlwsqDv1yMQL1CWqXDL0hDjXuNcq0zuR4xqPSuasI3kqFDhqSyTjREz5gzq0fXg==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "requires": { + "yallist": "^4.0.0" + } + }, + "map-obj": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-4.2.1.tgz", + "integrity": "sha512-+WA2/1sPmDj1dlvvJmB5G6JKfY9dpn7EVBUL06+y6PoljPkh+6V1QihwxNkbcGxCRjt2b0F9K0taiCuo7MbdFQ==", + "dev": true + }, + "meow": { + "version": "8.1.2", + "resolved": "https://registry.npmjs.org/meow/-/meow-8.1.2.tgz", + "integrity": "sha512-r85E3NdZ+mpYk1C6RjPFEMSE+s1iZMuHtsHAqY0DT3jZczl0diWUZ8g6oU7h0M9cD2EL+PzaYghhCLzR0ZNn5Q==", + "dev": true, + "requires": { + "@types/minimist": "^1.2.0", + "camelcase-keys": "^6.2.2", + "decamelize-keys": "^1.1.0", + "hard-rejection": "^2.1.0", + "minimist-options": "4.1.0", + "normalize-package-data": "^3.0.0", + "read-pkg-up": "^7.0.1", + "redent": "^3.0.0", + "trim-newlines": "^3.0.0", + "type-fest": "^0.18.0", + "yargs-parser": "^20.2.3" + } + }, + "normalize-package-data": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-3.0.2.tgz", + "integrity": "sha512-6CdZocmfGaKnIHPVFhJJZ3GuR8SsLKvDANFp47Jmy51aKIr8akjAWTSxtpI+MBgBFdSMRyo4hMpDlT6dTffgZg==", + "dev": true, + "requires": { + "hosted-git-info": "^4.0.1", + "resolve": "^1.20.0", + "semver": "^7.3.4", + "validate-npm-package-license": "^3.0.1" + }, + "dependencies": { + "semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + } + } + }, + "read-pkg": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", + "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", + "dev": true, + "requires": { + "@types/normalize-package-data": "^2.4.0", + "normalize-package-data": "^2.5.0", + "parse-json": "^5.0.0", + "type-fest": "^0.6.0" + }, + "dependencies": { + "hosted-git-info": { + "version": "2.8.9", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", + "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", + "dev": true + }, + "normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "dev": true, + "requires": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + }, + "type-fest": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", + "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", + "dev": true + } + } + }, + "read-pkg-up": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz", + "integrity": "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==", + "dev": true, + "requires": { + "find-up": "^4.1.0", + "read-pkg": "^5.2.0", + "type-fest": "^0.8.1" + }, + "dependencies": { + "type-fest": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "dev": true + } + } + }, + "resolve": { + "version": "1.20.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz", + "integrity": "sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==", + "dev": true, + "requires": { + "is-core-module": "^2.2.0", + "path-parse": "^1.0.6" + } + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + }, + "trim-newlines": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-3.0.0.tgz", + "integrity": "sha512-C4+gOpvmxaSMKuEf9Qc134F1ZuOHVXKRbtEflf4NTtuuJDEIJ9p5PXsalL8SkeRw+qit1Mo+yuvMPAKwWg/1hA==", + "dev": true + }, + "type-fest": { + "version": "0.18.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.18.1.tgz", + "integrity": "sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw==", + "dev": true + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "yargs-parser": { + "version": "20.2.7", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.7.tgz", + "integrity": "sha512-FiNkvbeHzB/syOjIUxFDCnhSfzAL8R5vs40MgLFBorXACCOAEaWu0gRZl14vG8MR9AOJIZbmkjhusqBYZ3HTHw==", + "dev": true + } + } + }, + "gitconfiglocal": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/gitconfiglocal/-/gitconfiglocal-1.0.0.tgz", + "integrity": "sha512-spLUXeTAVHxDtKsJc8FkFVgFtMdEN9qPGpL23VfSHx4fP4+Ds097IXLvymbnDH8FnmxX5Nr9bPw3A+AQ6mWEaQ==", + "dev": true, + "requires": { + "ini": "^1.3.2" + } + }, + "glob": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "requires": { + "is-glob": "^4.0.1" + } + }, + "global-dirs": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-0.1.1.tgz", + "integrity": "sha512-NknMLn7F2J7aflwFOlGdNIuCDpN3VGoSoB+aap3KABFWbHVn1TCgFC+np23J8W2BiZbjfEw3BFBycSMv1AFblg==", + "dev": true, + "optional": true, + "requires": { + "ini": "^1.3.4" + } + }, + "global-modules": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-2.0.0.tgz", + "integrity": "sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A==", + "requires": { + "global-prefix": "^3.0.0" + } + }, + "global-prefix": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-3.0.0.tgz", + "integrity": "sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg==", + "requires": { + "ini": "^1.3.5", + "kind-of": "^6.0.2", + "which": "^1.3.1" + } + }, + "globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==" + }, + "globby": { + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.0.3.tgz", + "integrity": "sha512-ffdmosjA807y7+lA1NM0jELARVmYul/715xiILEjo3hBLPTcirgQNnXECn5g3mtR8TOLCVbkfua1Hpen25/Xcg==", + "requires": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.1.1", + "ignore": "^5.1.4", + "merge2": "^1.3.0", + "slash": "^3.0.0" + } + }, + "globule": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/globule/-/globule-1.3.2.tgz", + "integrity": "sha512-7IDTQTIu2xzXkT+6mlluidnWo+BypnbSoEVVQCGfzqnl5Ik8d3e1d4wycb8Rj9tWW+Z39uPWsdlquqiqPCd/pA==", + "dev": true, + "requires": { + "glob": "~7.1.1", + "lodash": "~4.17.10", + "minimatch": "~3.0.2" + } + }, + "goober": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/goober/-/goober-1.8.0.tgz", + "integrity": "sha512-9ZFoOkBccexjqIgcwlhq7C/eCSkgTZX0BdNUkOnBFLedrJgo3R8lp9ckd/qqtngtF/JDyXSxJzwMU98kNjZ4Mw==" + }, + "graceful-fs": { + "version": "4.2.6", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.6.tgz", + "integrity": "sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ==" + }, + "growly": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/growly/-/growly-1.3.0.tgz", + "integrity": "sha512-+xGQY0YyAWCnqy7Cd++hc2JqMYzlm0dG30Jd0beaA64sROr8C4nt8Yc9V5Ro3avlSUDTN0ulqP/VBKi1/lLygw==", + "optional": true + }, + "gud": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/gud/-/gud-1.0.0.tgz", + "integrity": "sha512-zGEOVKFM5sVPPrYs7J5/hYEw2Pof8KCyOwyhG8sAF26mCAeUFAcYPu1mwB7hhpIP29zOIBaDqwuHdLp0jvZXjw==" + }, + "gzip-size": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/gzip-size/-/gzip-size-5.1.1.tgz", + "integrity": "sha512-FNHi6mmoHvs1mxZAds4PpdCS6QG8B4C1krxJsMutgxl5t3+GlRTzzI3NEkifXx2pVsOvJdOGSmIgDhQ55FwdPA==", + "requires": { + "duplexer": "^0.1.1", + "pify": "^4.0.1" + }, + "dependencies": { + "pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==" + } + } + }, + "handle-thing": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.1.tgz", + "integrity": "sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg==" + }, + "handlebars": { + "version": "4.7.7", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.7.tgz", + "integrity": "sha512-aAcXm5OAfE/8IXkcZvCepKU3VzW1/39Fb5ZuqMtgI/hT8X2YgoMvBY5dLhq/cpOvw7Lk1nK/UF71aLG/ZnVYRA==", + "dev": true, + "requires": { + "minimist": "^1.2.5", + "neo-async": "^2.6.0", + "source-map": "^0.6.1", + "uglify-js": "^3.1.4", + "wordwrap": "^1.0.0" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "har-schema": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", + "integrity": "sha512-Oqluz6zhGX8cyRaTQlFMPw80bSJVG2x/cFb8ZPhUILGgHka9SsokCCOQgpveePerqidZOrT14ipqfJb7ILcW5Q==" + }, + "har-validator": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz", + "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==", + "requires": { + "ajv": "^6.12.3", + "har-schema": "^2.0.0" + } + }, + "hard-rejection": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/hard-rejection/-/hard-rejection-2.1.0.tgz", + "integrity": "sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA==", + "dev": true + }, + "harmony-reflect": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/harmony-reflect/-/harmony-reflect-1.6.2.tgz", + "integrity": "sha512-HIp/n38R9kQjDEziXyDTuW3vvoxxyxjxFzXLrBr18uB47GnSt+G9D29fqrpM5ZkspMcPICud3XsBJQ4Y2URg8g==" + }, + "has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "requires": { + "function-bind": "^1.1.1" + } + }, + "has-ansi": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", + "integrity": "sha512-C8vBJ8DwUCx19vhm7urhTuUsr4/IyP6l4VzNQDv+ryHQObW3TTTp9yB68WpYgRe2bbaGuZ/se74IqFeVnMnLZg==", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", + "dev": true + } + } + }, + "has-bigints": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.1.tgz", + "integrity": "sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA==" + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==" + }, + "has-symbols": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz", + "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==" + }, + "has-unicode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==", + "dev": true + }, + "has-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", + "integrity": "sha512-IBXk4GTsLYdQ7Rvt+GRBrFSVEkmuOUy4re0Xjd9kJSUQpnTrWR4/y9RpfexN9vkAPMFuQoeWKwqzPozRTlasGw==", + "requires": { + "get-value": "^2.0.6", + "has-values": "^1.0.0", + "isobject": "^3.0.0" + } + }, + "has-values": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz", + "integrity": "sha512-ODYZC64uqzmtfGMEAX/FvZiRyWLpAC3vYnNunURUnkGVTS+mI0smVsWaPydRBsE3g+ok7h960jChO8mFcWlHaQ==", + "requires": { + "is-number": "^3.0.0", + "kind-of": "^4.0.0" + }, + "dependencies": { + "is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha512-4cboCqIpliH+mAvFNegjZQ4kgKc3ZUhQVr3HvWbSh5q3WH2v82ct+T2Y1hdU5Gdtorx/cLifQjqCbL7bpznLTg==", + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "kind-of": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", + "integrity": "sha512-24XsCxmEbRwEDbz/qz3stgin8TTzZ1ESR56OMCN0ujYg+vRutNSiOj9bHH9u85DKgXguraugV5sFuvbD4FW/hw==", + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "hash-base": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.1.0.tgz", + "integrity": "sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA==", + "requires": { + "inherits": "^2.0.4", + "readable-stream": "^3.6.0", + "safe-buffer": "^5.2.0" + }, + "dependencies": { + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + }, + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" + } + } + }, + "hash.js": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", + "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", + "requires": { + "inherits": "^2.0.3", + "minimalistic-assert": "^1.0.1" + } + }, + "he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==" + }, + "hex-color-regex": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/hex-color-regex/-/hex-color-regex-1.1.0.tgz", + "integrity": "sha512-l9sfDFsuqtOqKDsQdqrMRk0U85RZc0RtOR9yPI7mRVOa4FsR/BVnZ0shmQRM96Ji99kYZP/7hn1cedc1+ApsTQ==" + }, + "history": { + "version": "4.10.1", + "resolved": "https://registry.npmjs.org/history/-/history-4.10.1.tgz", + "integrity": "sha512-36nwAD620w12kuzPAsyINPWJqlNbij+hpK1k9XRloDtym8mxzGYl2c17LnV6IAGB2Dmg4tEa7G7DlawS0+qjew==", + "requires": { + "@babel/runtime": "^7.1.2", + "loose-envify": "^1.2.0", + "resolve-pathname": "^3.0.0", + "tiny-invariant": "^1.0.2", + "tiny-warning": "^1.0.0", + "value-equal": "^1.0.1" + } + }, + "hmac-drbg": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", + "integrity": "sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg==", + "requires": { + "hash.js": "^1.0.3", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "hoist-non-react-statics": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", + "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==", + "requires": { + "react-is": "^16.7.0" + } + }, + "homedir-polyfill": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz", + "integrity": "sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA==", + "dev": true, + "requires": { + "parse-passwd": "^1.0.0" + } + }, + "hoopy": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/hoopy/-/hoopy-0.1.4.tgz", + "integrity": "sha512-HRcs+2mr52W0K+x8RzcLzuPPmVIKMSv97RGHy0Ea9y/mpcaK+xTrjICA04KAHi4GRzxliNqNJEFYWHghy3rSfQ==" + }, + "hosted-git-info": { + "version": "2.8.9", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", + "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==" + }, + "hpack.js": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz", + "integrity": "sha512-zJxVehUdMGIKsRaNt7apO2Gqp0BdqW5yaiGHXXmbpvxgBYVZnAql+BJb4RO5ad2MgpbZKn5G6nMnegrH1FcNYQ==", + "requires": { + "inherits": "^2.0.1", + "obuf": "^1.0.0", + "readable-stream": "^2.0.1", + "wbuf": "^1.1.0" + } + }, + "hsl-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/hsl-regex/-/hsl-regex-1.0.0.tgz", + "integrity": "sha512-M5ezZw4LzXbBKMruP+BNANf0k+19hDQMgpzBIYnya//Al+fjNct9Wf3b1WedLqdEs2hKBvxq/jh+DsHJLj0F9A==" + }, + "hsla-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/hsla-regex/-/hsla-regex-1.0.0.tgz", + "integrity": "sha512-7Wn5GMLuHBjZCb2bTmnDOycho0p/7UVaAeqXZGbHrBCl6Yd/xDhQJAXe6Ga9AXJH2I5zY1dEdYw2u1UptnSBJA==" + }, + "html-element-map": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/html-element-map/-/html-element-map-1.3.0.tgz", + "integrity": "sha512-AqCt/m9YaiMwaaAyOPdq4Ga0cM+jdDWWGueUMkdROZcTeClaGpN0AQeyGchZhTegQoABmc6+IqH7oCR/8vhQYg==", + "dev": true, + "requires": { + "array-filter": "^1.0.0", + "call-bind": "^1.0.2" + } + }, + "html-encoding-sniffer": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-2.0.1.tgz", + "integrity": "sha512-D5JbOMBIR/TVZkubHT+OyT2705QvogUW4IBn6nHd756OwieSF9aDYFj4dv6HHEVGYbHaLETa3WggZYWWMyy3ZQ==", + "requires": { + "whatwg-encoding": "^1.0.5" + } + }, + "html-entities": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-1.4.0.tgz", + "integrity": "sha512-8nxjcBcd8wovbeKx7h3wTji4e6+rhaVuPNpMqwWgnHh+N9ToqsCs6XztWRBPQ+UtzsoMAdKZtUENoVzU/EMtZA==" + }, + "html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==" + }, + "html-minifier-terser": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/html-minifier-terser/-/html-minifier-terser-5.1.1.tgz", + "integrity": "sha512-ZPr5MNObqnV/T9akshPKbVgyOqLmy+Bxo7juKCfTfnjNniTAMdy4hz21YQqoofMBJD2kdREaqPPdThoR78Tgxg==", + "requires": { + "camel-case": "^4.1.1", + "clean-css": "^4.2.3", + "commander": "^4.1.1", + "he": "^1.2.0", + "param-case": "^3.0.3", + "relateurl": "^0.2.7", + "terser": "^4.6.3" + } + }, + "html-webpack-plugin": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-4.5.0.tgz", + "integrity": "sha512-MouoXEYSjTzCrjIxWwg8gxL5fE2X2WZJLmBYXlaJhQUH5K/b5OrqmV7T4dB7iu0xkmJ6JlUuV6fFVtnqbPopZw==", + "requires": { + "@types/html-minifier-terser": "^5.0.0", + "@types/tapable": "^1.0.5", + "@types/webpack": "^4.41.8", + "html-minifier-terser": "^5.0.1", + "loader-utils": "^1.2.3", + "lodash": "^4.17.15", + "pretty-error": "^2.1.1", + "tapable": "^1.1.3", + "util.promisify": "1.0.0" + }, + "dependencies": { + "json5": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", + "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", + "requires": { + "minimist": "^1.2.0" + } + }, + "loader-utils": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz", + "integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==", + "requires": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^1.0.1" + } + }, + "util.promisify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/util.promisify/-/util.promisify-1.0.0.tgz", + "integrity": "sha512-i+6qA2MPhvoKLuxnJNpXAGhg7HphQOSUq2LKMZD0m15EiskXUkMvKdF4Uui0WYeCUGea+o2cw/ZuwehtfsrNkA==", + "requires": { + "define-properties": "^1.1.2", + "object.getownpropertydescriptors": "^2.0.3" + } + } + } + }, + "htmlparser2": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.10.1.tgz", + "integrity": "sha512-IgieNijUMbkDovyoKObU1DUhm1iwNYE/fuifEoEHfd1oZKZDaONBSkal7Y01shxsM49R4XaMdGez3WnF9UfiCQ==", + "requires": { + "domelementtype": "^1.3.1", + "domhandler": "^2.3.0", + "domutils": "^1.5.1", + "entities": "^1.1.1", + "inherits": "^2.0.1", + "readable-stream": "^3.1.1" + }, + "dependencies": { + "entities": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.2.tgz", + "integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==" + }, + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + } + } + }, + "http-deceiver": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz", + "integrity": "sha512-LmpOGxTfbpgtGVxJrj5k7asXHCgNZp5nLfp+hWc8QQRqtb7fUy6kRY3BO1h9ddF6yIPYUARgxGOwB42DnxIaNw==" + }, + "http-errors": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", + "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==", + "requires": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.1", + "statuses": ">= 1.5.0 < 2", + "toidentifier": "1.0.0" + }, + "dependencies": { + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + } + } + }, + "http-parser-js": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.3.tgz", + "integrity": "sha512-t7hjvef/5HEK7RWTdUzVUhl8zkEu+LlaE0IYzdMuvbSDipxBRpOn4Uhw8ZyECEa808iVT8XCjzo6xmYt4CiLZg==" + }, + "http-proxy": { + "version": "1.18.1", + "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz", + "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==", + "requires": { + "eventemitter3": "^4.0.0", + "follow-redirects": "^1.0.0", + "requires-port": "^1.0.0" + } + }, + "http-proxy-middleware": { + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-0.19.1.tgz", + "integrity": "sha512-yHYTgWMQO8VvwNS22eLLloAkvungsKdKTLO8AJlftYIKNfJr3GK3zK0ZCfzDDGUBttdGc8xFy1mCitvNKQtC3Q==", + "requires": { + "http-proxy": "^1.17.0", + "is-glob": "^4.0.0", + "lodash": "^4.17.11", + "micromatch": "^3.1.10" + }, + "dependencies": { + "braces": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "requires": { + "arr-flatten": "^1.1.0", + "array-unique": "^0.3.2", + "extend-shallow": "^2.0.1", + "fill-range": "^4.0.0", + "isobject": "^3.0.1", + "repeat-element": "^1.1.2", + "snapdragon": "^0.8.1", + "snapdragon-node": "^2.0.1", + "split-string": "^3.0.2", + "to-regex": "^3.0.1" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha512-VcpLTWqWDiTerugjj8e3+esbg+skS3M9e54UuR3iCeIDMXCLTsAH8hTSzDQU/X6/6t3eYkOKoZSef2PlU6U1XQ==", + "requires": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha512-4cboCqIpliH+mAvFNegjZQ4kgKc3ZUhQVr3HvWbSh5q3WH2v82ct+T2Y1hdU5Gdtorx/cLifQjqCbL7bpznLTg==", + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", + "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "requires": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "braces": "^2.3.1", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "extglob": "^2.0.4", + "fragment-cache": "^0.2.1", + "kind-of": "^6.0.2", + "nanomatch": "^1.2.9", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.2" + } + }, + "to-regex-range": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", + "integrity": "sha512-ZZWNfCjUokXXDGXFpZehJIkZqq91BcULFq/Pi7M5i4JnxXdhMKAK682z8bCW3o8Hj1wuuzoKcW3DfVzaP6VuNg==", + "requires": { + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" + } + } + } + }, + "http-signature": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", + "integrity": "sha512-CAbnr6Rz4CYQkLYUtSNXxQPUH2gK8f3iWexVlsnMeD+GjlsQ0Xsy1cOX+mN3dtxYomRy21CiOzU8Uhw6OwncEQ==", + "requires": { + "assert-plus": "^1.0.0", + "jsprim": "^1.2.2", + "sshpk": "^1.7.0" + } + }, + "https-browserify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz", + "integrity": "sha512-J+FkSdyD+0mA0N+81tMotaRMfSL9SGi+xpD3T6YApKsc3bGSXJlfXri3VyFOeYkfLRQisDk1W+jIFFKBeUBbBg==" + }, + "human-signals": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-1.1.1.tgz", + "integrity": "sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==" + }, + "husky": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/husky/-/husky-2.7.0.tgz", + "integrity": "sha512-LIi8zzT6PyFpcYKdvWRCn/8X+6SuG2TgYYMrM6ckEYhlp44UcEduVymZGIZNLiwOUjrEud+78w/AsAiqJA/kRg==", + "dev": true, + "requires": { + "cosmiconfig": "^5.2.0", + "execa": "^1.0.0", + "find-up": "^3.0.0", + "get-stdin": "^7.0.0", + "is-ci": "^2.0.0", + "pkg-dir": "^4.1.0", + "please-upgrade-node": "^3.1.1", + "read-pkg": "^5.1.1", + "run-node": "^1.0.0", + "slash": "^3.0.0" + }, + "dependencies": { + "cosmiconfig": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-5.2.1.tgz", + "integrity": "sha512-H65gsXo1SKjf8zmrJ67eJk8aIRKV5ff2D4uKZIBZShbhGSpEmsQOPW/SKMKYhSTrqR7ufy6RP69rPogdaPh/kA==", + "dev": true, + "requires": { + "import-fresh": "^2.0.0", + "is-directory": "^0.3.1", + "js-yaml": "^3.13.1", + "parse-json": "^4.0.0" + } + }, + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dev": true, + "requires": { + "locate-path": "^3.0.0" + } + }, + "get-stdin": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-7.0.0.tgz", + "integrity": "sha512-zRKcywvrXlXsA0v0i9Io4KDRaAw7+a1ZpjRwl9Wox8PFlVCCHra7E9c4kqXCoCM9nR5tBkaTTZRBoCm60bFqTQ==", + "dev": true + }, + "import-fresh": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-2.0.0.tgz", + "integrity": "sha512-eZ5H8rcgYazHbKC3PG4ClHNykCSxtAhxSSEM+2mb+7evD2CKF5V7c0dNum7AdpDh0ZdICwZY9sRSn8f+KH96sg==", + "dev": true, + "requires": { + "caller-path": "^2.0.0", + "resolve-from": "^3.0.0" + } + }, + "locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dev": true, + "requires": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + } + }, + "p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "dev": true, + "requires": { + "p-limit": "^2.0.0" + } + }, + "parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==", + "dev": true, + "requires": { + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" + } + }, + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", + "dev": true + }, + "pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "requires": { + "find-up": "^4.0.0" + }, + "dependencies": { + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "requires": { + "p-locate": "^4.1.0" + } + }, + "p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "requires": { + "p-limit": "^2.2.0" + } + }, + "path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true + } + } + }, + "read-pkg": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", + "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", + "dev": true, + "requires": { + "@types/normalize-package-data": "^2.4.0", + "normalize-package-data": "^2.5.0", + "parse-json": "^5.0.0", + "type-fest": "^0.6.0" + }, + "dependencies": { + "parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + } + } + } + }, + "resolve-from": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz", + "integrity": "sha512-GnlH6vxLymXJNMBo7XP1fJIzBFbdYt49CuTwmB/6N53t+kMPRMFKz783LlQ4tv28XoQfMWinAJX6WCGf2IlaIw==", + "dev": true + }, + "type-fest": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", + "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", + "dev": true + } + } + }, + "iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "icss-utils": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-4.1.1.tgz", + "integrity": "sha512-4aFq7wvWyMHKgxsH8QQtGpvbASCf+eM3wPRLI6R+MgAnTCZ6STYsRvttLvRWK0Nfif5piF394St3HeJDaljGPA==", + "requires": { + "postcss": "^7.0.14" + } + }, + "identity-obj-proxy": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/identity-obj-proxy/-/identity-obj-proxy-3.0.0.tgz", + "integrity": "sha512-00n6YnVHKrinT9t0d9+5yZC6UBNJANpYEQvL2LlX6Ab9lnmxzIRcEmTPuyGScvl1+jKuCICX1Z0Ab1pPKKdikA==", + "requires": { + "harmony-reflect": "^1.4.6" + } + }, + "ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==" + }, + "iferr": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/iferr/-/iferr-0.1.5.tgz", + "integrity": "sha512-DUNFN5j7Tln0D+TxzloUjKB+CtVu6myn0JEFak6dG18mNt9YkQ6lzGCdafwofISZ1lLF3xRHJ98VKy9ynkcFaA==" + }, + "ignore": { + "version": "5.1.8", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.8.tgz", + "integrity": "sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw==" + }, + "immer": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/immer/-/immer-8.0.1.tgz", + "integrity": "sha512-aqXhGP7//Gui2+UrEtvxZxSquQVXTpZ7KDxfCcKAF3Vysvw0CViVaW9RZ1j1xlIYqaaaipBoqdqeibkc18PNvA==" + }, + "immutable": { + "version": "3.8.2", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-3.8.2.tgz", + "integrity": "sha512-15gZoQ38eYjEjxkorfbcgBKBL6R7T459OuK+CpcWt7O3KF4uPCx2tD0uFETlUDIyo+1789crbMhTvQBSR5yBMg==" + }, + "import-cwd": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/import-cwd/-/import-cwd-2.1.0.tgz", + "integrity": "sha512-Ew5AZzJQFqrOV5BTW3EIoHAnoie1LojZLXKcCQ/yTRyVZosBhK1x1ViYjHGf5pAFOq8ZyChZp6m/fSN7pJyZtg==", + "requires": { + "import-from": "^2.1.0" + } + }, + "import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "requires": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + } + }, + "import-from": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/import-from/-/import-from-2.1.0.tgz", + "integrity": "sha512-0vdnLL2wSGnhlRmzHJAg5JHjt1l2vYhzJ7tNLGbeVg0fse56tpGaH0uzH+r9Slej+BSXXEHvBKDEnVSLLE9/+w==", + "requires": { + "resolve-from": "^3.0.0" + }, + "dependencies": { + "resolve-from": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz", + "integrity": "sha512-GnlH6vxLymXJNMBo7XP1fJIzBFbdYt49CuTwmB/6N53t+kMPRMFKz783LlQ4tv28XoQfMWinAJX6WCGf2IlaIw==" + } + } + }, + "import-local": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.0.2.tgz", + "integrity": "sha512-vjL3+w0oulAVZ0hBHnxa/Nm5TAurf9YLQJDhqRZyqb+VKGOB6LU8t9H1Nr5CIo16vh9XfJTOoHwU0B71S557gA==", + "requires": { + "pkg-dir": "^4.2.0", + "resolve-cwd": "^3.0.0" + }, + "dependencies": { + "pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "requires": { + "find-up": "^4.0.0" + } + } + } + }, + "imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==" + }, + "in-publish": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/in-publish/-/in-publish-2.0.1.tgz", + "integrity": "sha512-oDM0kUSNFC31ShNxHKUyfZKy8ZeXZBWMjMdZHKLOk13uvT27VTL/QzRGfRUcevJhpkZAvlhPYuXkF7eNWrtyxQ==", + "dev": true + }, + "indent-string": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-2.1.0.tgz", + "integrity": "sha1-ji1INIdCEhtKghi3oTfppSBJ3IA=", + "dev": true, + "requires": { + "repeating": "^2.0.0" + } + }, + "indexes-of": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/indexes-of/-/indexes-of-1.0.1.tgz", + "integrity": "sha512-bup+4tap3Hympa+JBJUG7XuOsdNQ6fxt0MHyXMKuLBKn0OqsTfvUxkUrroEX1+B2VsSHvCjiIcZVxRtYa4nllA==" + }, + "infer-owner": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/infer-owner/-/infer-owner-1.0.4.tgz", + "integrity": "sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==" + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==" + }, + "inquirer": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-6.5.2.tgz", + "integrity": "sha512-cntlB5ghuB0iuO65Ovoi8ogLHiWGs/5yNrtUcKjFhSSiVeAIVpD7koaSU9RM8mpXw5YDi9RdYXGQMaOURB7ycQ==", + "dev": true, + "requires": { + "ansi-escapes": "^3.2.0", + "chalk": "^2.4.2", + "cli-cursor": "^2.1.0", + "cli-width": "^2.0.0", + "external-editor": "^3.0.3", + "figures": "^2.0.0", + "lodash": "^4.17.12", + "mute-stream": "0.0.7", + "run-async": "^2.2.0", + "rxjs": "^6.4.0", + "string-width": "^2.1.0", + "strip-ansi": "^5.1.0", + "through": "^2.3.6" + }, + "dependencies": { + "ansi-escapes": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz", + "integrity": "sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ==", + "dev": true + }, + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==", + "dev": true + }, + "string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "dev": true, + "requires": { + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" + }, + "dependencies": { + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha512-4XaJ2zQdCzROZDivEVIDPkcQn8LMFSa8kj8Gxb/Lnwzv9A8VctNZ+lfivC/sV3ivW8ElJTERXZoPBRrZKkNKow==", + "dev": true, + "requires": { + "ansi-regex": "^3.0.0" + } + } + } + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "requires": { + "ansi-regex": "^4.1.0" + }, + "dependencies": { + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "dev": true + } + } + } + } + }, + "internal-ip": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/internal-ip/-/internal-ip-4.3.0.tgz", + "integrity": "sha512-S1zBo1D6zcsyuC6PMmY5+55YMILQ9av8lotMx447Bq6SAgo/sDK6y6uUKmuYhW7eacnIhFfsPmCNYdDzsnnDCg==", + "requires": { + "default-gateway": "^4.2.0", + "ipaddr.js": "^1.9.0" + } + }, + "internal-slot": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.3.tgz", + "integrity": "sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA==", + "requires": { + "get-intrinsic": "^1.1.0", + "has": "^1.0.3", + "side-channel": "^1.0.4" + } + }, + "interpret": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.4.0.tgz", + "integrity": "sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==", + "dev": true + }, + "ip": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz", + "integrity": "sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo=" + }, + "ip-regex": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-2.1.0.tgz", + "integrity": "sha512-58yWmlHpp7VYfcdTwMTvwMmqx/Elfxjd9RXTDyMsbL7lLWmhMylLEqiYVLKuLzOZqVgiWXD9MfR62Vv89VRxkw==" + }, + "ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==" + }, + "is-absolute-url": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-absolute-url/-/is-absolute-url-2.1.0.tgz", + "integrity": "sha512-vOx7VprsKyllwjSkLV79NIhpyLfr3jAp7VaTCMXOJHu4m0Ew1CZ2fcjASwmV1jI3BWuWHB013M48eyeldk9gYg==" + }, + "is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha512-e1BM1qnDbMRG3ll2U9dSK0UMHuWOs3pY3AtcFsmvwPtKL3MML/Q86i+GilLfvqEs4GW+ExB91tQ3Ig9noDIZ+A==", + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-arguments": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.0.tgz", + "integrity": "sha512-1Ij4lOMPl/xB5kBDn7I+b2ttPMKa8szhEIrXDuXQD/oe3HJLTLhqhgGspwgyGd6MOywBUqVvYicF72lkgDnIHg==", + "requires": { + "call-bind": "^1.0.0" + } + }, + "is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==" + }, + "is-bigint": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.1.tgz", + "integrity": "sha512-J0ELF4yHFxHy0cmSxZuheDOz2luOdVvqjwmEcj8H/L1JHeuEDSDbeRP+Dk9kFVk5RTFzbucJ2Kb9F7ixY2QaCg==" + }, + "is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "optional": true, + "requires": { + "binary-extensions": "^2.0.0" + } + }, + "is-boolean-object": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.0.tgz", + "integrity": "sha512-a7Uprx8UtD+HWdyYwnD1+ExtTgqQtD2k/1yJgtXP6wnMm8byhkoTZRl+95LLThpzNZJ5aEvi46cdH+ayMFRwmA==", + "requires": { + "call-bind": "^1.0.0" + } + }, + "is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" + }, + "is-callable": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.3.tgz", + "integrity": "sha512-J1DcMe8UYTBSrKezuIUTUwjXsho29693unXM2YhJUTR2txK/eG47bvNa/wipPFmZFgr/N6f1GA66dv0mEyTIyQ==" + }, + "is-ci": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz", + "integrity": "sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==", + "requires": { + "ci-info": "^2.0.0" + } + }, + "is-color-stop": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-color-stop/-/is-color-stop-1.1.0.tgz", + "integrity": "sha512-H1U8Vz0cfXNujrJzEcvvwMDW9Ra+biSYA3ThdQvAnMLJkEHQXn6bWzLkxHtVYJ+Sdbx0b6finn3jZiaVe7MAHA==", + "requires": { + "css-color-names": "^0.0.4", + "hex-color-regex": "^1.1.0", + "hsl-regex": "^1.0.0", + "hsla-regex": "^1.0.0", + "rgb-regex": "^1.0.1", + "rgba-regex": "^1.0.0" + } + }, + "is-core-module": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.3.0.tgz", + "integrity": "sha512-xSphU2KG9867tsYdLD4RWQ1VqdFl4HTO9Thf3I/3dLEfr0dbPTWKsuCKrgqMljg4nPE+Gq0VCnzT3gr0CyBmsw==", + "requires": { + "has": "^1.0.3" + } + }, + "is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha512-+w9D5ulSoBNlmw9OHn3U2v51SyoCd0he+bB3xMl62oijhrspxowjU+AIcDY0N3iEJbUEkB15IlMASQsxYigvXg==", + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-date-object": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.2.tgz", + "integrity": "sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g==" + }, + "is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "requires": { + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" + }, + "dependencies": { + "kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==" + } + } + }, + "is-directory": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/is-directory/-/is-directory-0.3.1.tgz", + "integrity": "sha512-yVChGzahRFvbkscn2MlwGismPO12i9+znNruC5gVEntG3qu0xQMzsGg/JFbrsqDOHtHFPci+V5aP5T9I+yeKqw==" + }, + "is-docker": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", + "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==" + }, + "is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==" + }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==" + }, + "is-finite": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.1.0.tgz", + "integrity": "sha512-cdyMtqX/BOqqNBBiKlIVkytNHm49MtMlYyn1zxzvJKWmFMlGzm+ry5BBfYyeY9YmNKbRSo/o7OX9w9ale0wg3w==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==" + }, + "is-generator-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", + "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==" + }, + "is-glob": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", + "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", + "requires": { + "is-extglob": "^2.1.1" + } + }, + "is-module": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz", + "integrity": "sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g==" + }, + "is-negative-zero": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.1.tgz", + "integrity": "sha512-2z6JzQvZRa9A2Y7xC6dQQm4FSTSTNWjKIYYTt4246eMTJmIo0Q+ZyOsU66X8lxK1AbB92dFeglPLrhwpeRKO6w==" + }, + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==" + }, + "is-number-object": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.4.tgz", + "integrity": "sha512-zohwelOAur+5uXtk8O3GPQ1eAcu4ZX3UwxQhUlfFFMNpUd83gXgjbhJh6HmB6LUNV/ieOLQuDwJO3dWJosUeMw==" + }, + "is-obj": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", + "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==" + }, + "is-path-cwd": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-2.2.0.tgz", + "integrity": "sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ==" + }, + "is-path-in-cwd": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-2.1.0.tgz", + "integrity": "sha512-rNocXHgipO+rvnP6dk3zI20RpOtrAM/kzbB258Uw5BWr3TpXi861yzjo16Dn4hUox07iw5AyeMLHWsujkjzvRQ==", + "requires": { + "is-path-inside": "^2.1.0" + } + }, + "is-path-inside": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-2.1.0.tgz", + "integrity": "sha512-wiyhTzfDWsvwAW53OBWF5zuvaOGlZ6PwYxAbPVDhpm+gM09xKQGjBq/8uYN12aDvMxnAnq3dxTyoSoRNmg5YFg==", + "requires": { + "path-is-inside": "^1.0.2" + } + }, + "is-plain-obj": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", + "integrity": "sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg==" + }, + "is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "requires": { + "isobject": "^3.0.1" + } + }, + "is-potential-custom-element-name": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", + "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==" + }, + "is-regex": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.2.tgz", + "integrity": "sha512-axvdhb5pdhEVThqJzYXwMlVuZwC+FF2DpcOhTS+y/8jVq4trxyPgfcwIxIKiyeuLlSQYKkmUaPQJ8ZE4yNKXDg==", + "requires": { + "call-bind": "^1.0.2", + "has-symbols": "^1.0.1" + } + }, + "is-regexp": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-regexp/-/is-regexp-1.0.0.tgz", + "integrity": "sha512-7zjFAPO4/gwyQAAgRRmqeEeyIICSdmCqa3tsVHMdBzaXXRiqopZL4Cyghg/XulGWrtABTpbnYYzzIRffLkP4oA==" + }, + "is-resolvable": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-resolvable/-/is-resolvable-1.1.0.tgz", + "integrity": "sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg==" + }, + "is-root": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-root/-/is-root-2.1.0.tgz", + "integrity": "sha512-AGOriNp96vNBd3HtU+RzFEc75FfR5ymiYv8E553I71SCeXBiMsVDUtdio1OEFvrPyLIQ9tVR5RxXIFe5PUFjMg==" + }, + "is-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ==" + }, + "is-string": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.5.tgz", + "integrity": "sha512-buY6VNRjhQMiF1qWDouloZlQbRhDPCebwxSjxMjxgemYT46YMd2NR0/H+fBhEfWX4A/w9TBJ+ol+okqJKFE6vQ==" + }, + "is-subset": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-subset/-/is-subset-0.1.1.tgz", + "integrity": "sha512-6Ybun0IkarhmEqxXCNw/C0bna6Zb/TkfUX9UbwJtK6ObwAVCxmAP308WWTHviM/zAqXk05cdhYsUsZeGQh99iw==", + "dev": true + }, + "is-symbol": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.3.tgz", + "integrity": "sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ==", + "requires": { + "has-symbols": "^1.0.1" + } + }, + "is-text-path": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-text-path/-/is-text-path-1.0.1.tgz", + "integrity": "sha512-xFuJpne9oFz5qDaodwmmG08e3CawH/2ZV8Qqza1Ko7Sk8POWbkRdwIoAWVhqvq0XeUzANEhKo2n0IXUGBm7A/w==", + "dev": true, + "requires": { + "text-extensions": "^1.0.0" + } + }, + "is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==" + }, + "is-utf8": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", + "integrity": "sha512-rMYPYvCzsXywIsldgLaSoPlw5PfoB/ssr7hY4pLfcodrA5M/eArza1a9VmTiNIBNMjOGr1Ow9mTyU2o69U6U9Q==", + "dev": true + }, + "is-windows": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", + "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==" + }, + "is-wsl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", + "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "requires": { + "is-docker": "^2.0.0" + } + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" + }, + "isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==" + }, + "isstream": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "integrity": "sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==" + }, + "istanbul-lib-coverage": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.0.0.tgz", + "integrity": "sha512-UiUIqxMgRDET6eR+o5HbfRYP1l0hqkWOs7vNxC/mggutCMUIhWMm8gAHb8tHlyfD3/l6rlgNA5cKdDzEAf6hEg==" + }, + "istanbul-lib-instrument": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.3.tgz", + "integrity": "sha512-BXgQl9kf4WTCPCCpmFGoJkz/+uhvm7h7PFKUYxh7qarQd3ER33vHG//qaE8eN25l07YqZPpHXU9I09l/RD5aGQ==", + "requires": { + "@babel/core": "^7.7.5", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.0.0", + "semver": "^6.3.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" + } + } + }, + "istanbul-lib-report": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", + "integrity": "sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==", + "requires": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^3.0.0", + "supports-color": "^7.1.0" + }, + "dependencies": { + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" + }, + "make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "requires": { + "semver": "^6.0.0" + } + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "istanbul-lib-source-maps": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.0.tgz", + "integrity": "sha512-c16LpFRkR8vQXyHZ5nLpY35JZtzj1PQY1iZmesUbf1FZHbIupcWfjgOXBY9YHkLEQ6puz1u4Dgj6qmU/DisrZg==", + "requires": { + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0", + "source-map": "^0.6.1" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + } + } + }, + "istanbul-reports": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.0.2.tgz", + "integrity": "sha512-9tZvz7AiR3PEDNGiV9vIouQ/EAcqMXFmkcA1CDFTwOB98OZVDL0PH9glHotf5Ugp6GCOTypfzGWI/OqjWNCRUw==", + "requires": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + } + }, + "jest": { + "version": "26.6.0", + "resolved": "https://registry.npmjs.org/jest/-/jest-26.6.0.tgz", + "integrity": "sha512-jxTmrvuecVISvKFFhOkjsWRZV7sFqdSUAd1ajOKY+/QE/aLBVstsJ/dX8GczLzwiT6ZEwwmZqtCUHLHHQVzcfA==", + "requires": { + "@jest/core": "^26.6.0", + "import-local": "^3.0.2", + "jest-cli": "^26.6.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", + "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" + }, + "jest-cli": { + "version": "26.6.3", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-26.6.3.tgz", + "integrity": "sha512-GF9noBSa9t08pSyl3CY4frMrqp+aQXFGFkf5hEPbh/pIUFYWMK6ZLTfbmadxJVcJrdRoChlWQsA2VkJcDFK8hg==", + "requires": { + "@jest/core": "^26.6.3", + "@jest/test-result": "^26.6.2", + "@jest/types": "^26.6.2", + "chalk": "^4.0.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.4", + "import-local": "^3.0.2", + "is-ci": "^2.0.0", + "jest-config": "^26.6.3", + "jest-util": "^26.6.2", + "jest-validate": "^26.6.2", + "prompts": "^2.0.1", + "yargs": "^15.4.1" + } + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "jest-changed-files": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-26.6.2.tgz", + "integrity": "sha512-fDS7szLcY9sCtIip8Fjry9oGf3I2ht/QT21bAHm5Dmf0mD4X3ReNUf17y+bO6fR8WgbIZTlbyG1ak/53cbRzKQ==", + "requires": { + "@jest/types": "^26.6.2", + "execa": "^4.0.0", + "throat": "^5.0.0" + }, + "dependencies": { + "cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "requires": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + } + }, + "execa": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-4.1.0.tgz", + "integrity": "sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA==", + "requires": { + "cross-spawn": "^7.0.0", + "get-stream": "^5.0.0", + "human-signals": "^1.1.1", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.0", + "onetime": "^5.1.0", + "signal-exit": "^3.0.2", + "strip-final-newline": "^2.0.0" + } + }, + "get-stream": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "requires": { + "pump": "^3.0.0" + } + }, + "is-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz", + "integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==" + }, + "npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "requires": { + "path-key": "^3.0.0" + } + }, + "path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==" + }, + "shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "requires": { + "shebang-regex": "^3.0.0" + } + }, + "shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==" + }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "requires": { + "isexe": "^2.0.0" + } + } + } + }, + "jest-circus": { + "version": "26.6.0", + "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-26.6.0.tgz", + "integrity": "sha512-L2/Y9szN6FJPWFK8kzWXwfp+FOR7xq0cUL4lIsdbIdwz3Vh6P1nrpcqOleSzr28zOtSHQNV9Z7Tl+KkuK7t5Ng==", + "requires": { + "@babel/traverse": "^7.1.0", + "@jest/environment": "^26.6.0", + "@jest/test-result": "^26.6.0", + "@jest/types": "^26.6.0", + "@types/babel__traverse": "^7.0.4", + "@types/node": "*", + "chalk": "^4.0.0", + "co": "^4.6.0", + "dedent": "^0.7.0", + "expect": "^26.6.0", + "is-generator-fn": "^2.0.0", + "jest-each": "^26.6.0", + "jest-matcher-utils": "^26.6.0", + "jest-message-util": "^26.6.0", + "jest-runner": "^26.6.0", + "jest-runtime": "^26.6.0", + "jest-snapshot": "^26.6.0", + "jest-util": "^26.6.0", + "pretty-format": "^26.6.0", + "stack-utils": "^2.0.2", + "throat": "^5.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", + "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "jest-config": { + "version": "26.6.3", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-26.6.3.tgz", + "integrity": "sha512-t5qdIj/bCj2j7NFVHb2nFB4aUdfucDn3JRKgrZnplb8nieAirAzRSHP8uDEd+qV6ygzg9Pz4YG7UTJf94LPSyg==", + "requires": { + "@babel/core": "^7.1.0", + "@jest/test-sequencer": "^26.6.3", + "@jest/types": "^26.6.2", + "babel-jest": "^26.6.3", + "chalk": "^4.0.0", + "deepmerge": "^4.2.2", + "glob": "^7.1.1", + "graceful-fs": "^4.2.4", + "jest-environment-jsdom": "^26.6.2", + "jest-environment-node": "^26.6.2", + "jest-get-type": "^26.3.0", + "jest-jasmine2": "^26.6.3", + "jest-regex-util": "^26.0.0", + "jest-resolve": "^26.6.2", + "jest-util": "^26.6.2", + "jest-validate": "^26.6.2", + "micromatch": "^4.0.2", + "pretty-format": "^26.6.2" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", + "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" + }, + "jest-resolve": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-26.6.2.tgz", + "integrity": "sha512-sOxsZOq25mT1wRsfHcbtkInS+Ek7Q8jCHUB0ZUTP0tc/c41QHriU/NunqMfCUWsL4H3MHpvQD4QR9kSYhS7UvQ==", + "requires": { + "@jest/types": "^26.6.2", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.4", + "jest-pnp-resolver": "^1.2.2", + "jest-util": "^26.6.2", + "read-pkg-up": "^7.0.1", + "resolve": "^1.18.1", + "slash": "^3.0.0" + } + }, + "read-pkg": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", + "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", + "requires": { + "@types/normalize-package-data": "^2.4.0", + "normalize-package-data": "^2.5.0", + "parse-json": "^5.0.0", + "type-fest": "^0.6.0" + }, + "dependencies": { + "type-fest": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", + "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==" + } + } + }, + "read-pkg-up": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz", + "integrity": "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==", + "requires": { + "find-up": "^4.1.0", + "read-pkg": "^5.2.0", + "type-fest": "^0.8.1" + } + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "jest-diff": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-26.6.2.tgz", + "integrity": "sha512-6m+9Z3Gv9wN0WFVasqjCL/06+EFCMTqDEUl/b87HYK2rAPTyfz4ZIuSlPhY51PIQRWx5TaxeF1qmXKe9gfN3sA==", + "requires": { + "chalk": "^4.0.0", + "diff-sequences": "^26.6.2", + "jest-get-type": "^26.3.0", + "pretty-format": "^26.6.2" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", + "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "jest-docblock": { + "version": "26.0.0", + "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-26.0.0.tgz", + "integrity": "sha512-RDZ4Iz3QbtRWycd8bUEPxQsTlYazfYn/h5R65Fc6gOfwozFhoImx+affzky/FFBuqISPTqjXomoIGJVKBWoo0w==", + "requires": { + "detect-newline": "^3.0.0" + } + }, + "jest-each": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-26.6.2.tgz", + "integrity": "sha512-Mer/f0KaATbjl8MCJ+0GEpNdqmnVmDYqCTJYTvoo7rqmRiDllmp2AYN+06F93nXcY3ur9ShIjS+CO/uD+BbH4A==", + "requires": { + "@jest/types": "^26.6.2", + "chalk": "^4.0.0", + "jest-get-type": "^26.3.0", + "jest-util": "^26.6.2", + "pretty-format": "^26.6.2" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", + "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "jest-environment-jsdom": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-26.6.2.tgz", + "integrity": "sha512-jgPqCruTlt3Kwqg5/WVFyHIOJHsiAvhcp2qiR2QQstuG9yWox5+iHpU3ZrcBxW14T4fe5Z68jAfLRh7joCSP2Q==", + "requires": { + "@jest/environment": "^26.6.2", + "@jest/fake-timers": "^26.6.2", + "@jest/types": "^26.6.2", + "@types/node": "*", + "jest-mock": "^26.6.2", + "jest-util": "^26.6.2", + "jsdom": "^16.4.0" + } + }, + "jest-environment-node": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-26.6.2.tgz", + "integrity": "sha512-zhtMio3Exty18dy8ee8eJ9kjnRyZC1N4C1Nt/VShN1apyXc8rWGtJ9lI7vqiWcyyXS4BVSEn9lxAM2D+07/Tag==", + "requires": { + "@jest/environment": "^26.6.2", + "@jest/fake-timers": "^26.6.2", + "@jest/types": "^26.6.2", + "@types/node": "*", + "jest-mock": "^26.6.2", + "jest-util": "^26.6.2" + } + }, + "jest-get-type": { + "version": "26.3.0", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-26.3.0.tgz", + "integrity": "sha512-TpfaviN1R2pQWkIihlfEanwOXK0zcxrKEE4MlU6Tn7keoXdN6/3gK/xl0yEh8DOunn5pOVGKf8hB4R9gVh04ig==" + }, + "jest-haste-map": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-26.6.2.tgz", + "integrity": "sha512-easWIJXIw71B2RdR8kgqpjQrbMRWQBgiBwXYEhtGUTaX+doCjBheluShdDMeR8IMfJiTqH4+zfhtg29apJf/8w==", + "requires": { + "@jest/types": "^26.6.2", + "@types/graceful-fs": "^4.1.2", + "@types/node": "*", + "anymatch": "^3.0.3", + "fb-watchman": "^2.0.0", + "fsevents": "^2.1.2", + "graceful-fs": "^4.2.4", + "jest-regex-util": "^26.0.0", + "jest-serializer": "^26.6.2", + "jest-util": "^26.6.2", + "jest-worker": "^26.6.2", + "micromatch": "^4.0.2", + "sane": "^4.0.3", + "walker": "^1.0.7" + } + }, + "jest-jasmine2": { + "version": "26.6.3", + "resolved": "https://registry.npmjs.org/jest-jasmine2/-/jest-jasmine2-26.6.3.tgz", + "integrity": "sha512-kPKUrQtc8aYwBV7CqBg5pu+tmYXlvFlSFYn18ev4gPFtrRzB15N2gW/Roew3187q2w2eHuu0MU9TJz6w0/nPEg==", + "requires": { + "@babel/traverse": "^7.1.0", + "@jest/environment": "^26.6.2", + "@jest/source-map": "^26.6.2", + "@jest/test-result": "^26.6.2", + "@jest/types": "^26.6.2", + "@types/node": "*", + "chalk": "^4.0.0", + "co": "^4.6.0", + "expect": "^26.6.2", + "is-generator-fn": "^2.0.0", + "jest-each": "^26.6.2", + "jest-matcher-utils": "^26.6.2", + "jest-message-util": "^26.6.2", + "jest-runtime": "^26.6.3", + "jest-snapshot": "^26.6.2", + "jest-util": "^26.6.2", + "pretty-format": "^26.6.2", + "throat": "^5.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", + "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "jest-junit": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/jest-junit/-/jest-junit-10.0.0.tgz", + "integrity": "sha512-dbOVRyxHprdSpwSAR9/YshLwmnwf+RSl5hf0kCGlhAcEeZY9aRqo4oNmaT0tLC16Zy9D0zekDjWkjHGjXlglaQ==", + "dev": true, + "requires": { + "jest-validate": "^24.9.0", + "mkdirp": "^0.5.1", + "strip-ansi": "^5.2.0", + "uuid": "^3.3.3", + "xml": "^1.0.1" + }, + "dependencies": { + "@jest/types": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-24.9.0.tgz", + "integrity": "sha512-XKK7ze1apu5JWQ5eZjHITP66AX+QsLlbaJRBGYr8pNzwcAE2JVkwnf0yqjHTsDRcjR0mujy/NmZMXw5kl+kGBw==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^1.1.1", + "@types/yargs": "^13.0.0" + } + }, + "@types/istanbul-reports": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-1.1.2.tgz", + "integrity": "sha512-P/W9yOX/3oPZSpaYOCQzGqgCQRXn0FFO/V8bWrCQs+wLmvVVxk6CRBXALEvNs9OHIatlnlFokfhuDo2ug01ciw==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "*", + "@types/istanbul-lib-report": "*" + } + }, + "@types/yargs": { + "version": "13.0.11", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-13.0.11.tgz", + "integrity": "sha512-NRqD6T4gktUrDi1o1wLH3EKC1o2caCr7/wR87ODcbVITQF106OM3sFN92ysZ++wqelOd1CTzatnOBRDYYG6wGQ==", + "dev": true, + "requires": { + "@types/yargs-parser": "*" + } + }, + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "dev": true + }, + "camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true + }, + "jest-get-type": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-24.9.0.tgz", + "integrity": "sha512-lUseMzAley4LhIcpSP9Jf+fTrQ4a1yHQwLNeeVa2cEmbCGeoZAtYPOIv8JaxLD/sUpKxetKGP+gsHl8f8TSj8Q==", + "dev": true + }, + "jest-validate": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-24.9.0.tgz", + "integrity": "sha512-HPIt6C5ACwiqSiwi+OfSSHbK8sG7akG8eATl+IPKaeIjtPOeBUd/g3J7DghugzxrGjI93qS/+RPKe1H6PqvhRQ==", + "dev": true, + "requires": { + "@jest/types": "^24.9.0", + "camelcase": "^5.3.1", + "chalk": "^2.0.1", + "jest-get-type": "^24.9.0", + "leven": "^3.1.0", + "pretty-format": "^24.9.0" + } + }, + "pretty-format": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-24.9.0.tgz", + "integrity": "sha512-00ZMZUiHaJrNfk33guavqgvfJS30sLYf0f8+Srklv0AMPodGGHcoHgksZ3OThYnIvOd+8yMCn0YiEOogjlgsnA==", + "dev": true, + "requires": { + "@jest/types": "^24.9.0", + "ansi-regex": "^4.0.0", + "ansi-styles": "^3.2.0", + "react-is": "^16.8.4" + } + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "requires": { + "ansi-regex": "^4.1.0" + } + }, + "uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", + "dev": true + } + } + }, + "jest-leak-detector": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-26.6.2.tgz", + "integrity": "sha512-i4xlXpsVSMeKvg2cEKdfhh0H39qlJlP5Ex1yQxwF9ubahboQYMgTtz5oML35AVA3B4Eu+YsmwaiKVev9KCvLxg==", + "requires": { + "jest-get-type": "^26.3.0", + "pretty-format": "^26.6.2" + } + }, + "jest-matcher-utils": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-26.6.2.tgz", + "integrity": "sha512-llnc8vQgYcNqDrqRDXWwMr9i7rS5XFiCwvh6DTP7Jqa2mqpcCBBlpCbn+trkG0KNhPu/h8rzyBkriOtBstvWhw==", + "requires": { + "chalk": "^4.0.0", + "jest-diff": "^26.6.2", + "jest-get-type": "^26.3.0", + "pretty-format": "^26.6.2" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", + "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "jest-message-util": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-26.6.2.tgz", + "integrity": "sha512-rGiLePzQ3AzwUshu2+Rn+UMFk0pHN58sOG+IaJbk5Jxuqo3NYO1U2/MIR4S1sKgsoYSXSzdtSa0TgrmtUwEbmA==", + "requires": { + "@babel/code-frame": "^7.0.0", + "@jest/types": "^26.6.2", + "@types/stack-utils": "^2.0.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.4", + "micromatch": "^4.0.2", + "pretty-format": "^26.6.2", + "slash": "^3.0.0", + "stack-utils": "^2.0.2" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", + "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "jest-mock": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-26.6.2.tgz", + "integrity": "sha512-YyFjePHHp1LzpzYcmgqkJ0nm0gg/lJx2aZFzFy1S6eUqNjXsOqTK10zNRff2dNfssgokjkG65OlWNcIlgd3zew==", + "requires": { + "@jest/types": "^26.6.2", + "@types/node": "*" + } + }, + "jest-pnp-resolver": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.2.tgz", + "integrity": "sha512-olV41bKSMm8BdnuMsewT4jqlZ8+3TCARAXjZGT9jcoSnrfUnRCqnMoF9XEeoWjbzObpqF9dRhHQj0Xb9QdF6/w==" + }, + "jest-regex-util": { + "version": "26.0.0", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-26.0.0.tgz", + "integrity": "sha512-Gv3ZIs/nA48/Zvjrl34bf+oD76JHiGDUxNOVgUjh3j890sblXryjY4rss71fPtD/njchl6PSE2hIhvyWa1eT0A==" + }, + "jest-resolve": { + "version": "26.6.0", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-26.6.0.tgz", + "integrity": "sha512-tRAz2bwraHufNp+CCmAD8ciyCpXCs1NQxB5EJAmtCFy6BN81loFEGWKzYu26Y62lAJJe4X4jg36Kf+NsQyiStQ==", + "requires": { + "@jest/types": "^26.6.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.4", + "jest-pnp-resolver": "^1.2.2", + "jest-util": "^26.6.0", + "read-pkg-up": "^7.0.1", + "resolve": "^1.17.0", + "slash": "^3.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", + "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" + }, + "read-pkg": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", + "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", + "requires": { + "@types/normalize-package-data": "^2.4.0", + "normalize-package-data": "^2.5.0", + "parse-json": "^5.0.0", + "type-fest": "^0.6.0" + }, + "dependencies": { + "type-fest": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", + "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==" + } + } + }, + "read-pkg-up": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz", + "integrity": "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==", + "requires": { + "find-up": "^4.1.0", + "read-pkg": "^5.2.0", + "type-fest": "^0.8.1" + } + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "jest-resolve-dependencies": { + "version": "26.6.3", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-26.6.3.tgz", + "integrity": "sha512-pVwUjJkxbhe4RY8QEWzN3vns2kqyuldKpxlxJlzEYfKSvY6/bMvxoFrYYzUO1Gx28yKWN37qyV7rIoIp2h8fTg==", + "requires": { + "@jest/types": "^26.6.2", + "jest-regex-util": "^26.0.0", + "jest-snapshot": "^26.6.2" + } + }, + "jest-runner": { + "version": "26.6.3", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-26.6.3.tgz", + "integrity": "sha512-atgKpRHnaA2OvByG/HpGA4g6CSPS/1LK0jK3gATJAoptC1ojltpmVlYC3TYgdmGp+GLuhzpH30Gvs36szSL2JQ==", + "requires": { + "@jest/console": "^26.6.2", + "@jest/environment": "^26.6.2", + "@jest/test-result": "^26.6.2", + "@jest/types": "^26.6.2", + "@types/node": "*", + "chalk": "^4.0.0", + "emittery": "^0.7.1", + "exit": "^0.1.2", + "graceful-fs": "^4.2.4", + "jest-config": "^26.6.3", + "jest-docblock": "^26.0.0", + "jest-haste-map": "^26.6.2", + "jest-leak-detector": "^26.6.2", + "jest-message-util": "^26.6.2", + "jest-resolve": "^26.6.2", + "jest-runtime": "^26.6.3", + "jest-util": "^26.6.2", + "jest-worker": "^26.6.2", + "source-map-support": "^0.5.6", + "throat": "^5.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", + "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" + }, + "jest-resolve": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-26.6.2.tgz", + "integrity": "sha512-sOxsZOq25mT1wRsfHcbtkInS+Ek7Q8jCHUB0ZUTP0tc/c41QHriU/NunqMfCUWsL4H3MHpvQD4QR9kSYhS7UvQ==", + "requires": { + "@jest/types": "^26.6.2", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.4", + "jest-pnp-resolver": "^1.2.2", + "jest-util": "^26.6.2", + "read-pkg-up": "^7.0.1", + "resolve": "^1.18.1", + "slash": "^3.0.0" + } + }, + "read-pkg": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", + "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", + "requires": { + "@types/normalize-package-data": "^2.4.0", + "normalize-package-data": "^2.5.0", + "parse-json": "^5.0.0", + "type-fest": "^0.6.0" + }, + "dependencies": { + "type-fest": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", + "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==" + } + } + }, + "read-pkg-up": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz", + "integrity": "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==", + "requires": { + "find-up": "^4.1.0", + "read-pkg": "^5.2.0", + "type-fest": "^0.8.1" + } + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "jest-runtime": { + "version": "26.6.3", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-26.6.3.tgz", + "integrity": "sha512-lrzyR3N8sacTAMeonbqpnSka1dHNux2uk0qqDXVkMv2c/A3wYnvQ4EXuI013Y6+gSKSCxdaczvf4HF0mVXHRdw==", + "requires": { + "@jest/console": "^26.6.2", + "@jest/environment": "^26.6.2", + "@jest/fake-timers": "^26.6.2", + "@jest/globals": "^26.6.2", + "@jest/source-map": "^26.6.2", + "@jest/test-result": "^26.6.2", + "@jest/transform": "^26.6.2", + "@jest/types": "^26.6.2", + "@types/yargs": "^15.0.0", + "chalk": "^4.0.0", + "cjs-module-lexer": "^0.6.0", + "collect-v8-coverage": "^1.0.0", + "exit": "^0.1.2", + "glob": "^7.1.3", + "graceful-fs": "^4.2.4", + "jest-config": "^26.6.3", + "jest-haste-map": "^26.6.2", + "jest-message-util": "^26.6.2", + "jest-mock": "^26.6.2", + "jest-regex-util": "^26.0.0", + "jest-resolve": "^26.6.2", + "jest-snapshot": "^26.6.2", + "jest-util": "^26.6.2", + "jest-validate": "^26.6.2", + "slash": "^3.0.0", + "strip-bom": "^4.0.0", + "yargs": "^15.4.1" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", + "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" + }, + "jest-resolve": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-26.6.2.tgz", + "integrity": "sha512-sOxsZOq25mT1wRsfHcbtkInS+Ek7Q8jCHUB0ZUTP0tc/c41QHriU/NunqMfCUWsL4H3MHpvQD4QR9kSYhS7UvQ==", + "requires": { + "@jest/types": "^26.6.2", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.4", + "jest-pnp-resolver": "^1.2.2", + "jest-util": "^26.6.2", + "read-pkg-up": "^7.0.1", + "resolve": "^1.18.1", + "slash": "^3.0.0" + } + }, + "read-pkg": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", + "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", + "requires": { + "@types/normalize-package-data": "^2.4.0", + "normalize-package-data": "^2.5.0", + "parse-json": "^5.0.0", + "type-fest": "^0.6.0" + }, + "dependencies": { + "type-fest": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", + "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==" + } + } + }, + "read-pkg-up": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz", + "integrity": "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==", + "requires": { + "find-up": "^4.1.0", + "read-pkg": "^5.2.0", + "type-fest": "^0.8.1" + } + }, + "strip-bom": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", + "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==" + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "jest-serializer": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-26.6.2.tgz", + "integrity": "sha512-S5wqyz0DXnNJPd/xfIzZ5Xnp1HrJWBczg8mMfMpN78OJ5eDxXyf+Ygld9wX1DnUWbIbhM1YDY95NjR4CBXkb2g==", + "requires": { + "@types/node": "*", + "graceful-fs": "^4.2.4" + } + }, + "jest-snapshot": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-26.6.2.tgz", + "integrity": "sha512-OLhxz05EzUtsAmOMzuupt1lHYXCNib0ECyuZ/PZOx9TrZcC8vL0x+DUG3TL+GLX3yHG45e6YGjIm0XwDc3q3og==", + "requires": { + "@babel/types": "^7.0.0", + "@jest/types": "^26.6.2", + "@types/babel__traverse": "^7.0.4", + "@types/prettier": "^2.0.0", + "chalk": "^4.0.0", + "expect": "^26.6.2", + "graceful-fs": "^4.2.4", + "jest-diff": "^26.6.2", + "jest-get-type": "^26.3.0", + "jest-haste-map": "^26.6.2", + "jest-matcher-utils": "^26.6.2", + "jest-message-util": "^26.6.2", + "jest-resolve": "^26.6.2", + "natural-compare": "^1.4.0", + "pretty-format": "^26.6.2", + "semver": "^7.3.2" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", + "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" + }, + "jest-resolve": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-26.6.2.tgz", + "integrity": "sha512-sOxsZOq25mT1wRsfHcbtkInS+Ek7Q8jCHUB0ZUTP0tc/c41QHriU/NunqMfCUWsL4H3MHpvQD4QR9kSYhS7UvQ==", + "requires": { + "@jest/types": "^26.6.2", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.4", + "jest-pnp-resolver": "^1.2.2", + "jest-util": "^26.6.2", + "read-pkg-up": "^7.0.1", + "resolve": "^1.18.1", + "slash": "^3.0.0" + } + }, + "read-pkg": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", + "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", + "requires": { + "@types/normalize-package-data": "^2.4.0", + "normalize-package-data": "^2.5.0", + "parse-json": "^5.0.0", + "type-fest": "^0.6.0" + }, + "dependencies": { + "type-fest": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", + "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==" + } + } + }, + "read-pkg-up": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz", + "integrity": "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==", + "requires": { + "find-up": "^4.1.0", + "read-pkg": "^5.2.0", + "type-fest": "^0.8.1" + } + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "jest-util": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-26.6.2.tgz", + "integrity": "sha512-MDW0fKfsn0OI7MS7Euz6h8HNDXVQ0gaM9uW6RjfDmd1DAFcaxX9OqIakHIqhbnmF08Cf2DLDG+ulq8YQQ0Lp0Q==", + "requires": { + "@jest/types": "^26.6.2", + "@types/node": "*", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.4", + "is-ci": "^2.0.0", + "micromatch": "^4.0.2" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", + "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "jest-validate": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-26.6.2.tgz", + "integrity": "sha512-NEYZ9Aeyj0i5rQqbq+tpIOom0YS1u2MVu6+euBsvpgIme+FOfRmoC4R5p0JiAUpaFvFy24xgrpMknarR/93XjQ==", + "requires": { + "@jest/types": "^26.6.2", + "camelcase": "^6.0.0", + "chalk": "^4.0.0", + "jest-get-type": "^26.3.0", + "leven": "^3.1.0", + "pretty-format": "^26.6.2" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", + "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "jest-watch-typeahead": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/jest-watch-typeahead/-/jest-watch-typeahead-0.6.1.tgz", + "integrity": "sha512-ITVnHhj3Jd/QkqQcTqZfRgjfyRhDFM/auzgVo2RKvSwi18YMvh0WvXDJFoFED6c7jd/5jxtu4kSOb9PTu2cPVg==", + "requires": { + "ansi-escapes": "^4.3.1", + "chalk": "^4.0.0", + "jest-regex-util": "^26.0.0", + "jest-watcher": "^26.3.0", + "slash": "^3.0.0", + "string-length": "^4.0.1", + "strip-ansi": "^6.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", + "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "jest-watcher": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-26.6.2.tgz", + "integrity": "sha512-WKJob0P/Em2csiVthsI68p6aGKTIcsfjH9Gsx1f0A3Italz43e3ho0geSAVsmj09RWOELP1AZ/DXyJgOgDKxXQ==", + "requires": { + "@jest/test-result": "^26.6.2", + "@jest/types": "^26.6.2", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "jest-util": "^26.6.2", + "string-length": "^4.0.1" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", + "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "jest-worker": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-26.6.2.tgz", + "integrity": "sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ==", + "requires": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^7.0.0" + }, + "dependencies": { + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "jquery": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.6.0.tgz", + "integrity": "sha512-JVzAR/AjBvVt2BmYhxRCSYysDsPcssdmTFnzyLEts9qNwmjmu4JTAMYubEfwVOSwpQ1I1sKKFcxhZCI2buerfw==" + }, + "js-base64": { + "version": "2.6.4", + "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.6.4.tgz", + "integrity": "sha512-pZe//GGmwJndub7ZghVHz7vjb2LgC1m8B07Au3eYqeqv9emhESByMXxaEgkUkEqJe87oBbSniGYoQNIBklc7IQ==", + "dev": true + }, + "js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" + }, + "js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "requires": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + } + }, + "jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==" + }, + "jsdom": { + "version": "16.5.3", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-16.5.3.tgz", + "integrity": "sha512-Qj1H+PEvUsOtdPJ056ewXM4UJPCi4hhLA8wpiz9F2YvsRBhuFsXxtrIFAgGBDynQA9isAMGE91PfUYbdMPXuTA==", + "requires": { + "abab": "^2.0.5", + "acorn": "^8.1.0", + "acorn-globals": "^6.0.0", + "cssom": "^0.4.4", + "cssstyle": "^2.3.0", + "data-urls": "^2.0.0", + "decimal.js": "^10.2.1", + "domexception": "^2.0.1", + "escodegen": "^2.0.0", + "html-encoding-sniffer": "^2.0.1", + "is-potential-custom-element-name": "^1.0.0", + "nwsapi": "^2.2.0", + "parse5": "6.0.1", + "request": "^2.88.2", + "request-promise-native": "^1.0.9", + "saxes": "^5.0.1", + "symbol-tree": "^3.2.4", + "tough-cookie": "^4.0.0", + "w3c-hr-time": "^1.0.2", + "w3c-xmlserializer": "^2.0.0", + "webidl-conversions": "^6.1.0", + "whatwg-encoding": "^1.0.5", + "whatwg-mimetype": "^2.3.0", + "whatwg-url": "^8.5.0", + "ws": "^7.4.4", + "xml-name-validator": "^3.0.0" + }, + "dependencies": { + "acorn": { + "version": "8.2.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.2.1.tgz", + "integrity": "sha512-z716cpm5TX4uzOzILx8PavOE6C6DKshHDw1aQN52M/yNSqE9s5O8SMfyhCCfCJ3HmTL0NkVOi+8a/55T7YB3bg==" + } + } + }, + "jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==" + }, + "json-parse-better-errors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", + "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==" + }, + "json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==" + }, + "json-schema": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", + "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=" + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" + }, + "json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==" + }, + "json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==" + }, + "json3": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/json3/-/json3-3.3.3.tgz", + "integrity": "sha512-c7/8mbUsKigAbLkD5B010BK4D9LZm7A1pNItkEwiUZRpIN66exu/e7YQWysGun+TRKaJp8MhemM+VkfWv42aCA==" + }, + "json5": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.0.tgz", + "integrity": "sha512-f+8cldu7X/y7RAJurMEJmdoKXGB/X550w2Nr3tTbezL6RwEE/iMcm+tZnXeoZtKuOq6ft8+CqzEkrIgx1fPoQA==", + "requires": { + "minimist": "^1.2.5" + } + }, + "jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "requires": { + "graceful-fs": "^4.1.6", + "universalify": "^2.0.0" + } + }, + "jsonparse": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz", + "integrity": "sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==", + "dev": true + }, + "jsprim": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", + "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", + "requires": { + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.2.3", + "verror": "1.10.0" + } + }, + "jsx-ast-utils": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.2.0.tgz", + "integrity": "sha512-EIsmt3O3ljsU6sot/J4E1zDRxfBNrhjyf/OKjlydwgEimQuznlM4Wv7U+ueONJMyEn1WRE0K8dhi3dVAXYT24Q==", + "requires": { + "array-includes": "^3.1.2", + "object.assign": "^4.1.2" + } + }, + "killable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/killable/-/killable-1.0.1.tgz", + "integrity": "sha512-LzqtLKlUwirEUyl/nicirVmNiPvYs7l5n8wOPP7fyJVpUPkvCnW/vuiXGpylGUlnPDnB7311rARzAt3Mhswpjg==" + }, + "kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==" + }, + "kleur": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", + "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==" + }, + "klona": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/klona/-/klona-2.0.4.tgz", + "integrity": "sha512-ZRbnvdg/NxqzC7L9Uyqzf4psi1OM4Cuc+sJAkQPjO6XkQIJTNbfK2Rsmbw8fx1p2mkZdp2FZYo2+LwXYY/uwIA==" + }, + "language-subtag-registry": { + "version": "0.3.21", + "resolved": "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.21.tgz", + "integrity": "sha512-L0IqwlIXjilBVVYKFT37X9Ih11Um5NEl9cbJIuU/SwP/zEEAbBPOnEeeuxVMf45ydWQRDQN3Nqc96OgbH1K+Pg==" + }, + "language-tags": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/language-tags/-/language-tags-1.0.5.tgz", + "integrity": "sha512-qJhlO9cGXi6hBGKoxEG/sKZDAHD5Hnu9Hs4WbOY3pCWXDhw0N8x1NenNzm2EnNLkLkk7J2SdxAkDSbb6ftT+UQ==", + "requires": { + "language-subtag-registry": "~0.3.2" + } + }, + "last-call-webpack-plugin": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/last-call-webpack-plugin/-/last-call-webpack-plugin-3.0.0.tgz", + "integrity": "sha512-7KI2l2GIZa9p2spzPIVZBYyNKkN+e/SQPpnjlTiPhdbDW3F86tdKKELxKpzJ5sgU19wQWsACULZmpTPYHeWO5w==", + "requires": { + "lodash": "^4.17.5", + "webpack-sources": "^1.1.0" + } + }, + "leven": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", + "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==" + }, + "levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "requires": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + } + }, + "lines-and-columns": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.1.6.tgz", + "integrity": "sha512-8ZmlJFVK9iCmtLz19HpSsR8HaAMWBT284VMNednLwlIMDP2hJDCIhUp0IZ2xUcZ+Ob6BM0VvCSJwzASDM45NLQ==" + }, + "load-json-file": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz", + "integrity": "sha512-3p6ZOGNbiX4CdvEd1VcE6yi78UrGNpjHO33noGwHCnT/o2fyllJDepsm8+mFFv/DvtwFHht5HIHSyOy5a+ChVQ==", + "requires": { + "graceful-fs": "^4.1.2", + "parse-json": "^2.2.0", + "pify": "^2.0.0", + "strip-bom": "^3.0.0" + }, + "dependencies": { + "parse-json": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", + "integrity": "sha512-QR/GGaKCkhwk1ePQNYDRKYZ3mwU9ypsKhB0XyFnLQdomyEqk3e8wpW3V5Jp88zbxK4n5ST1nqo+g9juTpownhQ==", + "requires": { + "error-ex": "^1.2.0" + } + }, + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=" + } + } + }, + "loader-runner": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-2.4.0.tgz", + "integrity": "sha512-Jsmr89RcXGIwivFY21FcRrisYZfvLMTWx5kOLc+JTxtpBOG6xML0vzbc6SEQG2FO9/4Fc3wW4LVcB5DmGflaRw==" + }, + "loader-utils": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.0.tgz", + "integrity": "sha512-rP4F0h2RaWSvPEkD7BLDFQnvSf+nK+wr3ESUjNTyAGobqrijmW92zc+SO6d4p4B1wh7+B/Jg1mkQe5NYUEHtHQ==", + "requires": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + } + }, + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "requires": { + "p-locate": "^4.1.0" + } + }, + "lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + }, + "lodash._reinterpolate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz", + "integrity": "sha512-xYHt68QRoYGjeeM/XOE1uJtvXQAgvszfBhjV4yvsQH0u2i9I6cI6c6/eG4Hh3UAOVn0y/xAXwmTzEay49Q//HA==" + }, + "lodash.clonedeep": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", + "integrity": "sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==" + }, + "lodash.debounce": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", + "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==" + }, + "lodash.escape": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.escape/-/lodash.escape-4.0.1.tgz", + "integrity": "sha512-nXEOnb/jK9g0DYMr1/Xvq6l5xMD7GDG55+GSYIYmS0G4tBk/hURD4JR9WCavs04t33WmJx9kCyp9vJ+mr4BOUw==", + "dev": true + }, + "lodash.flatten": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz", + "integrity": "sha1-8xwiIlqWMtK7+OSt2+8kCqdlph8=" + }, + "lodash.flattendeep": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz", + "integrity": "sha512-uHaJFihxmJcEX3kT4I23ABqKKalJ/zDrDg0lsFtc1h+3uw49SIJ5beyhx5ExVRti3AvKoOJngIj7xz3oylPdWQ==", + "dev": true + }, + "lodash.isequal": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", + "integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==", + "dev": true + }, + "lodash.isequalwith": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.isequalwith/-/lodash.isequalwith-4.4.0.tgz", + "integrity": "sha512-dcZON0IalGBpRmJBmMkaoV7d3I80R2O+FrzsZyHdNSFrANq/cgDqKQNmAHE8UEj4+QYWwwhkQOVdLHiAopzlsQ==" + }, + "lodash.ismatch": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.ismatch/-/lodash.ismatch-4.4.0.tgz", + "integrity": "sha512-fPMfXjGQEV9Xsq/8MTSgUf255gawYRbjwMyDbcvDhXgV7enSZA0hynz6vMPnpAb5iONEzBHBPsT+0zes5Z301g==", + "dev": true + }, + "lodash.map": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/lodash.map/-/lodash.map-4.6.0.tgz", + "integrity": "sha512-worNHGKLDetmcEYDvh2stPCrrQRkP20E4l0iIS7F8EvzMqBBi7ltvFN5m1HvTf1P7Jk1txKhvFcmYsCr8O2F1Q==", + "dev": true + }, + "lodash.memoize": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", + "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==" + }, + "lodash.pick": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.pick/-/lodash.pick-4.4.0.tgz", + "integrity": "sha512-hXt6Ul/5yWjfklSGvLQl8vM//l3FtyHZeuelpzK6mm99pNvN9yTDruNZPEJZD1oWrqo+izBmB7oUfWgcCX7s4Q==" + }, + "lodash.template": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.template/-/lodash.template-4.5.0.tgz", + "integrity": "sha512-84vYFxIkmidUiFxidA/KjjH9pAycqW+h980j7Fuz5qxRtO9pgB7MDFTdys1N7A5mcucRiDyEq4fusljItR1T/A==", + "requires": { + "lodash._reinterpolate": "^3.0.0", + "lodash.templatesettings": "^4.0.0" + } + }, + "lodash.templatesettings": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.templatesettings/-/lodash.templatesettings-4.2.0.tgz", + "integrity": "sha512-stgLz+i3Aa9mZgnjr/O+v9ruKZsPsndy7qPZOchbqk2cnTU1ZaldKK+v7m54WoKIyxiuMZTKT2H81F8BeAc3ZQ==", + "requires": { + "lodash._reinterpolate": "^3.0.0" + } + }, + "lodash.throttle": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.throttle/-/lodash.throttle-4.1.1.tgz", + "integrity": "sha512-wIkUCfVKpVsWo3JSZlc+8MB5it+2AN5W8J7YVMST30UrvcQNZ1Okbj+rbVniijTWE6FGYy4XJq/rHkas8qJMLQ==" + }, + "lodash.truncate": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz", + "integrity": "sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==" + }, + "lodash.uniq": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", + "integrity": "sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==" + }, + "loglevel": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.7.1.tgz", + "integrity": "sha512-Hesni4s5UkWkwCGJMQGAh71PaLUmKFM60dHvq0zi/vDhhrzuk+4GgNbTXJ12YYQJn6ZKBDNIjYcuQGKudvqrIw==" + }, + "longest": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/longest/-/longest-2.0.1.tgz", + "integrity": "sha512-Ajzxb8CM6WAnFjgiloPsI3bF+WCxcvhdIG3KNA2KN962+tdBsHcuQ4k4qX/EcS/2CRkcc0iAkR956Nib6aXU/Q==", + "dev": true + }, + "loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "requires": { + "js-tokens": "^3.0.0 || ^4.0.0" + } + }, + "loud-rejection": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/loud-rejection/-/loud-rejection-1.6.0.tgz", + "integrity": "sha512-RPNliZOFkqFumDhvYqOaNY4Uz9oJM2K9tC6JWsJJsNdhuONW4LQHRBpb0qf4pJApVffI5N39SwzWZJuEhfd7eQ==", + "dev": true, + "requires": { + "currently-unhandled": "^0.4.1", + "signal-exit": "^3.0.0" + } + }, + "lower-case": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz", + "integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==", + "requires": { + "tslib": "^2.0.3" + }, + "dependencies": { + "tslib": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.2.0.tgz", + "integrity": "sha512-gS9GVHRU+RGn5KQM2rllAlR3dU6m7AcpJKdtH8gFvQiC4Otgk98XnmMU+nZenHt/+VhnBPWwgrJsyrdcw6i23w==" + } + } + }, + "lru-cache": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", + "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", + "dev": true, + "requires": { + "pseudomap": "^1.0.2", + "yallist": "^2.1.2" + } + }, + "magic-string": { + "version": "0.25.7", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.7.tgz", + "integrity": "sha512-4CrMT5DOHTDk4HYDlzmwu4FVCcIYI8gauveasrdCu2IKIFOJ3f0v/8MDGJCDL9oD2ppz/Av1b0Nj345H9M+XIA==", + "requires": { + "sourcemap-codec": "^1.4.4" + } + }, + "make-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", + "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", + "requires": { + "pify": "^4.0.1", + "semver": "^5.6.0" + }, + "dependencies": { + "pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==" + }, + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" + } + } + }, + "make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true + }, + "makeerror": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.11.tgz", + "integrity": "sha1-4BpckQnyr3lmDk6LlYd5AYT1qWw=", + "requires": { + "tmpl": "1.0.x" + } + }, + "map-cache": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", + "integrity": "sha512-8y/eV9QQZCiyn1SprXSrCmqJN0yNRATe+PO8ztwqrvrbdRLA3eYJF0yaR0YayLWkMbsQSKWS9N2gPcGEc4UsZg==" + }, + "map-obj": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", + "integrity": "sha512-7N/q3lyZ+LVCp7PzuxrJr4KMbBE2hW7BT7YNia330OFxIf4d3r5zVpicP2650l7CPN6RM9zOJRl3NGpqSiw3Eg==", + "dev": true + }, + "map-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz", + "integrity": "sha512-4y7uGv8bd2WdM9vpQsiQNo41Ln1NvhvDRuVt0k2JZQ+ezN2uaQes7lZeZ+QQUHOLQAtDaBJ+7wCbi+ab/KFs+w==", + "requires": { + "object-visit": "^1.0.0" + } + }, + "md5.js": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", + "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==", + "requires": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "mdn-data": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.4.tgz", + "integrity": "sha512-iV3XNKw06j5Q7mi6h+9vbx23Tv7JkjEVgKHW4pimwyDGWm0OIQntJJ+u1C6mg6mK1EaTv42XQ7w76yuzH7M2cA==" + }, + "media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==" + }, + "memoize-one": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-5.2.1.tgz", + "integrity": "sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q==" + }, + "memory-fs": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.4.1.tgz", + "integrity": "sha512-cda4JKCxReDXFXRqOHPQscuIYg1PvxbE2S2GP45rnwfEK+vZaXC8C1OFvdHIbgw0DLzowXGVoxLaAmlgRy14GQ==", + "requires": { + "errno": "^0.1.3", + "readable-stream": "^2.0.1" + } + }, + "meow": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/meow/-/meow-3.7.0.tgz", + "integrity": "sha512-TNdwZs0skRlpPpCUK25StC4VH+tP5GgeY1HQOOGP+lQ2xtdkN2VtT/5tiX9k3IWpkBPV9b3LsAWXn4GGi/PrSA==", + "dev": true, + "requires": { + "camelcase-keys": "^2.0.0", + "decamelize": "^1.1.2", + "loud-rejection": "^1.0.0", + "map-obj": "^1.0.1", + "minimist": "^1.1.3", + "normalize-package-data": "^2.3.4", + "object-assign": "^4.0.1", + "read-pkg-up": "^1.0.1", + "redent": "^1.0.0", + "trim-newlines": "^1.0.0" + }, + "dependencies": { + "find-up": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", + "integrity": "sha512-jvElSjyuo4EMQGoTwo1uJU5pQMwTW5lS1x05zzfJuTIyLR3zwO27LYrxNg+dlvKpGOuGy/MzBdXh80g0ve5+HA==", + "dev": true, + "requires": { + "path-exists": "^2.0.0", + "pinkie-promise": "^2.0.0" + } + }, + "load-json-file": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", + "integrity": "sha512-cy7ZdNRXdablkXYNI049pthVeXFurRyb9+hA/dZzerZ0pGTx42z+y+ssxBaVV2l70t1muq5IdKhn4UtcoGUY9A==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "parse-json": "^2.2.0", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0", + "strip-bom": "^2.0.0" + } + }, + "parse-json": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", + "integrity": "sha512-QR/GGaKCkhwk1ePQNYDRKYZ3mwU9ypsKhB0XyFnLQdomyEqk3e8wpW3V5Jp88zbxK4n5ST1nqo+g9juTpownhQ==", + "dev": true, + "requires": { + "error-ex": "^1.2.0" + } + }, + "path-exists": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", + "integrity": "sha512-yTltuKuhtNeFJKa1PiRzfLAU5182q1y4Eb4XCJ3PBqyzEDkAZRzBrKKBct682ls9reBVHf9udYLN5Nd+K1B9BQ==", + "dev": true, + "requires": { + "pinkie-promise": "^2.0.0" + } + }, + "path-type": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz", + "integrity": "sha512-S4eENJz1pkiQn9Znv33Q+deTOKmbl+jj1Fl+qiP/vYezj+S8x+J3Uo0ISrx/QoEvIlOaDWJhPaRd1flJ9HXZqg==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" + } + }, + "read-pkg": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz", + "integrity": "sha512-7BGwRHqt4s/uVbuyoeejRn4YmFnYZiFl4AuaeXHlgZf3sONF0SOGlxs2Pw8g6hCKupo08RafIO5YXFNOKTfwsQ==", + "dev": true, + "requires": { + "load-json-file": "^1.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^1.0.0" + } + }, + "read-pkg-up": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz", + "integrity": "sha512-WD9MTlNtI55IwYUS27iHh9tK3YoIVhxis8yKhLpTqWtml739uXc9NWTpxoHkfZf3+DkCCsXox94/VWZniuZm6A==", + "dev": true, + "requires": { + "find-up": "^1.0.0", + "read-pkg": "^1.0.0" + } + }, + "redent": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/redent/-/redent-1.0.0.tgz", + "integrity": "sha512-qtW5hKzGQZqKoh6JNSD+4lfitfPKGz42e6QwiRmPM5mmKtR0N41AbJRYu0xJi7nhOJ4WDgRkKvAk6tw4WIwR4g==", + "dev": true, + "requires": { + "indent-string": "^2.1.0", + "strip-indent": "^1.0.1" + } + }, + "strip-bom": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", + "integrity": "sha512-kwrX1y7czp1E69n2ajbG65mIo9dqvJ+8aBQXOGVxqwvNbsXdFM6Lq37dLAY3mknUwru8CfcCbfOLL/gMo+fi3g==", + "dev": true, + "requires": { + "is-utf8": "^0.2.0" + } + }, + "strip-indent": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-1.0.1.tgz", + "integrity": "sha512-I5iQq6aFMM62fBEAIB/hXzwJD6EEZ0xEGCX2t7oXqaKPIRgt4WruAQ285BISgdkP+HLGWyeGmNJcpIwFeRYRUA==", + "dev": true, + "requires": { + "get-stdin": "^4.0.1" + } + } + } + }, + "merge": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/merge/-/merge-1.2.1.tgz", + "integrity": "sha512-VjFo4P5Whtj4vsLzsYBu5ayHhoHJ0UqNm7ibvShmbmoz7tGi0vXaoJbGdB+GmDMLUdg8DpQXEIeVDAe8MaABvQ==", + "dev": true + }, + "merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==" + }, + "merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==" + }, + "merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==" + }, + "methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==" + }, + "microevent.ts": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/microevent.ts/-/microevent.ts-0.1.1.tgz", + "integrity": "sha512-jo1OfR4TaEwd5HOrt5+tAZ9mqT4jmpNAusXtyfNzqVm9uiSYFZlKM1wYL4oU7azZW/PxQW53wM0S6OR1JHNa2g==" + }, + "micromatch": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", + "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", + "requires": { + "braces": "^3.0.1", + "picomatch": "^2.2.3" + } + }, + "miller-rabin": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz", + "integrity": "sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==", + "requires": { + "bn.js": "^4.0.0", + "brorand": "^1.0.1" + }, + "dependencies": { + "bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" + } + } + }, + "mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==" + }, + "mime-db": { + "version": "1.47.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.47.0.tgz", + "integrity": "sha512-QBmA/G2y+IfeS4oktet3qRZ+P5kPhCKRXxXnQEudYqUaEioAU1/Lq2us3D/t1Jfo4hE9REQPrbB7K5sOczJVIw==" + }, + "mime-types": { + "version": "2.1.30", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.30.tgz", + "integrity": "sha512-crmjA4bLtR8m9qLpHvgxSChT+XoSlZi8J4n/aIdn3z92e/U47Z0V/yl+Wh9W046GgFVAmoNR/fmdbZYcSSIUeg==", + "requires": { + "mime-db": "1.47.0" + } + }, + "mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==" + }, + "min-indent": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", + "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==", + "dev": true + }, + "mini-create-react-context": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/mini-create-react-context/-/mini-create-react-context-0.4.1.tgz", + "integrity": "sha512-YWCYEmd5CQeHGSAKrYvXgmzzkrvssZcuuQDDeqkT+PziKGMgE+0MCCtcKbROzocGBG1meBLl2FotlRwf4gAzbQ==", + "requires": { + "@babel/runtime": "^7.12.1", + "tiny-warning": "^1.0.3" + } + }, + "mini-css-extract-plugin": { + "version": "0.11.3", + "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-0.11.3.tgz", + "integrity": "sha512-n9BA8LonkOkW1/zn+IbLPQmovsL0wMb9yx75fMJQZf2X1Zoec9yTZtyMePcyu19wPkmFbzZZA6fLTotpFhQsOA==", + "requires": { + "loader-utils": "^1.1.0", + "normalize-url": "1.9.1", + "schema-utils": "^1.0.0", + "webpack-sources": "^1.1.0" + }, + "dependencies": { + "json5": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", + "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", + "requires": { + "minimist": "^1.2.0" + } + }, + "loader-utils": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz", + "integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==", + "requires": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^1.0.1" + } + }, + "schema-utils": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz", + "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==", + "requires": { + "ajv": "^6.1.0", + "ajv-errors": "^1.0.0", + "ajv-keywords": "^3.1.0" + } + } + } + }, + "minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==" + }, + "minimalistic-crypto-utils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", + "integrity": "sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg==" + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" + }, + "minimist-options": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/minimist-options/-/minimist-options-4.1.0.tgz", + "integrity": "sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A==", + "dev": true, + "requires": { + "arrify": "^1.0.1", + "is-plain-obj": "^1.1.0", + "kind-of": "^6.0.3" + }, + "dependencies": { + "arrify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", + "integrity": "sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA==", + "dev": true + } + } + }, + "minipass": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.1.3.tgz", + "integrity": "sha512-Mgd2GdMVzY+x3IJ+oHnVM+KG3lA5c8tnabyJKmHSaG2kAGpudxuOf8ToDkhumF7UzME7DecbQE9uOZhNm7PuJg==", + "requires": { + "yallist": "^4.0.0" + }, + "dependencies": { + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + } + } + }, + "minipass-collect": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/minipass-collect/-/minipass-collect-1.0.2.tgz", + "integrity": "sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA==", + "requires": { + "minipass": "^3.0.0" + } + }, + "minipass-flush": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/minipass-flush/-/minipass-flush-1.0.5.tgz", + "integrity": "sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==", + "requires": { + "minipass": "^3.0.0" + } + }, + "minipass-pipeline": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz", + "integrity": "sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==", + "requires": { + "minipass": "^3.0.0" + } + }, + "minizlib": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", + "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", + "requires": { + "minipass": "^3.0.0", + "yallist": "^4.0.0" + }, + "dependencies": { + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + } + } + }, + "mississippi": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/mississippi/-/mississippi-3.0.0.tgz", + "integrity": "sha512-x471SsVjUtBRtcvd4BzKE9kFC+/2TeWgKCgw0bZcw1b9l2X3QX5vCWgF+KaZaYm87Ss//rHnWryupDrgLvmSkA==", + "requires": { + "concat-stream": "^1.5.0", + "duplexify": "^3.4.2", + "end-of-stream": "^1.1.0", + "flush-write-stream": "^1.0.0", + "from2": "^2.1.0", + "parallel-transform": "^1.1.0", + "pump": "^3.0.0", + "pumpify": "^1.3.3", + "stream-each": "^1.1.0", + "through2": "^2.0.0" + } + }, + "mixin-deep": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz", + "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==", + "requires": { + "for-in": "^1.0.2", + "is-extendable": "^1.0.1" + }, + "dependencies": { + "is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "requires": { + "is-plain-object": "^2.0.4" + } + } + } + }, + "mkdirp": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", + "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", + "requires": { + "minimist": "^1.2.5" + } + }, + "modify-values": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/modify-values/-/modify-values-1.0.1.tgz", + "integrity": "sha512-xV2bxeN6F7oYjZWTe/YPAy6MN2M+sL4u/Rlm2AHCIVGfo2p1yGmBHQ6vHehl4bRTZBdHu3TSkWdYgkwpYzAGSw==", + "dev": true + }, + "moment": { + "version": "2.29.1", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.1.tgz", + "integrity": "sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ==" + }, + "moo": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/moo/-/moo-0.5.1.tgz", + "integrity": "sha512-I1mnb5xn4fO80BH9BLcF0yLypy2UKl+Cb01Fu0hJRkJjlCRtxZMWkTdAtDd5ZqCOxtCkhmRwyI57vWT+1iZ67w==", + "dev": true + }, + "move-concurrently": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/move-concurrently/-/move-concurrently-1.0.1.tgz", + "integrity": "sha512-hdrFxZOycD/g6A6SoI2bB5NA/5NEqD0569+S47WZhPvm46sD50ZHdYaFmnua5lndde9rCHGjmfK7Z8BuCt/PcQ==", + "requires": { + "aproba": "^1.1.1", + "copy-concurrently": "^1.0.0", + "fs-write-stream-atomic": "^1.0.8", + "mkdirp": "^0.5.1", + "rimraf": "^2.5.4", + "run-queue": "^1.0.3" + }, + "dependencies": { + "rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "requires": { + "glob": "^7.1.3" + } + } + } + }, + "mri": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/mri/-/mri-1.1.6.tgz", + "integrity": "sha512-oi1b3MfbyGa7FJMP9GmLTttni5JoICpYBRlq+x5V16fZbLsnL9N3wFqqIm/nIG43FjUFkFh9Epzp/kzUGUnJxQ==", + "dev": true + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "multicast-dns": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/multicast-dns/-/multicast-dns-6.2.3.tgz", + "integrity": "sha512-ji6J5enbMyGRHIAkAOu3WdV8nggqviKCEKtXcOqfphZZtQrmHKycfynJ2V7eVPUA4NhJ6V7Wf4TmGbTwKE9B6g==", + "requires": { + "dns-packet": "^1.3.1", + "thunky": "^1.0.2" + } + }, + "multicast-dns-service-types": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/multicast-dns-service-types/-/multicast-dns-service-types-1.1.0.tgz", + "integrity": "sha512-cnAsSVxIDsYt0v7HmC0hWZFwwXSh+E6PgCrREDuN/EsjgLwA5XRmlMHhSiDPrt6HxY1gTivEa/Zh7GtODoLevQ==" + }, + "multimatch": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/multimatch/-/multimatch-4.0.0.tgz", + "integrity": "sha512-lDmx79y1z6i7RNx0ZGCPq1bzJ6ZoDDKbvh7jxr9SJcWLkShMzXrHbYVpTdnhNM5MXpDUxCQ4DgqVttVXlBgiBQ==", + "dev": true, + "requires": { + "@types/minimatch": "^3.0.3", + "array-differ": "^3.0.0", + "array-union": "^2.1.0", + "arrify": "^2.0.1", + "minimatch": "^3.0.4" + } + }, + "mute-stream": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz", + "integrity": "sha512-r65nCZhrbXXb6dXOACihYApHw2Q6pV0M3V0PSxd74N0+D8nzAdEAITq2oAjA1jVnKI+tGvEBUpqiMh0+rW6zDQ==", + "dev": true + }, + "nan": { + "version": "2.14.2", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.2.tgz", + "integrity": "sha512-M2ufzIiINKCuDfBSAUr1vWQ+vuVcA9kqx8JJUsbQi6yf1uGRyb7HfpdfUr5qLXf3B/t8dPvcjhKMmlfnP47EzQ==" + }, + "nanoid": { + "version": "3.1.22", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.22.tgz", + "integrity": "sha512-/2ZUaJX2ANuLtTvqTlgqBQNJoQO398KyJgZloL0PZkC0dpysjncRUPsFe3DUPzz/y3h+u7C46np8RMuvF3jsSQ==" + }, + "nanomatch": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", + "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==", + "requires": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "fragment-cache": "^0.2.1", + "is-windows": "^1.0.2", + "kind-of": "^6.0.2", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + } + }, + "native-url": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/native-url/-/native-url-0.2.6.tgz", + "integrity": "sha512-k4bDC87WtgrdD362gZz6zoiXQrl40kYlBmpfmSjwRO1VU0V5ccwJTlxuE72F6m3V0vc1xOf6n3UCP9QyerRqmA==", + "requires": { + "querystring": "^0.2.0" + } + }, + "natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==" + }, + "nearley": { + "version": "2.20.1", + "resolved": "https://registry.npmjs.org/nearley/-/nearley-2.20.1.tgz", + "integrity": "sha512-+Mc8UaAebFzgV+KpI5n7DasuuQCHA89dmwm7JXw3TV43ukfNQ9DnBH3Mdb2g/I4Fdxc26pwimBWvjIw0UAILSQ==", + "dev": true, + "requires": { + "commander": "^2.19.0", + "moo": "^0.5.0", + "railroad-diagrams": "^1.0.0", + "randexp": "0.4.6" + }, + "dependencies": { + "commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true + } + } + }, + "negotiator": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", + "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==" + }, + "neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==" + }, + "next-tick": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz", + "integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw=" + }, + "nice-try": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", + "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==" + }, + "no-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz", + "integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==", + "requires": { + "lower-case": "^2.0.2", + "tslib": "^2.0.3" + }, + "dependencies": { + "tslib": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.2.0.tgz", + "integrity": "sha512-gS9GVHRU+RGn5KQM2rllAlR3dU6m7AcpJKdtH8gFvQiC4Otgk98XnmMU+nZenHt/+VhnBPWwgrJsyrdcw6i23w==" + } + } + }, + "node-forge": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.10.0.tgz", + "integrity": "sha512-PPmu8eEeG9saEUvI97fm4OYxXVB6bFvyNTyiUOBichBpFG8A1Ljw3bY62+5oOjDEMHRnd0Y7HQ+x7uzxOzC6JA==" + }, + "node-gyp": { + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-3.8.0.tgz", + "integrity": "sha512-3g8lYefrRRzvGeSowdJKAKyks8oUpLEd/DyPV4eMhVlhJ0aNaZqIrNUIPuEWWTAoPqyFkfGrM67MC69baqn6vA==", + "dev": true, + "requires": { + "fstream": "^1.0.0", + "glob": "^7.0.3", + "graceful-fs": "^4.1.2", + "mkdirp": "^0.5.0", + "nopt": "2 || 3", + "npmlog": "0 || 1 || 2 || 3 || 4", + "osenv": "0", + "request": "^2.87.0", + "rimraf": "2", + "semver": "~5.3.0", + "tar": "^2.0.0", + "which": "1" + }, + "dependencies": { + "rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + }, + "semver": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.3.0.tgz", + "integrity": "sha512-mfmm3/H9+67MCVix1h+IXTpDwL6710LyHuk7+cWC9T1mE0qz4iHhh6r4hU2wrIT9iTsAAC2XQRvfblL028cpLw==", + "dev": true + } + } + }, + "node-int64": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", + "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==" + }, + "node-libs-browser": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/node-libs-browser/-/node-libs-browser-2.2.1.tgz", + "integrity": "sha512-h/zcD8H9kaDZ9ALUWwlBUDo6TKF8a7qBSCSEGfjTVIYeqsioSKaAX+BN7NgiMGp6iSIXZ3PxgCu8KS3b71YK5Q==", + "requires": { + "assert": "^1.1.1", + "browserify-zlib": "^0.2.0", + "buffer": "^4.3.0", + "console-browserify": "^1.1.0", + "constants-browserify": "^1.0.0", + "crypto-browserify": "^3.11.0", + "domain-browser": "^1.1.1", + "events": "^3.0.0", + "https-browserify": "^1.0.0", + "os-browserify": "^0.3.0", + "path-browserify": "0.0.1", + "process": "^0.11.10", + "punycode": "^1.2.4", + "querystring-es3": "^0.2.0", + "readable-stream": "^2.3.3", + "stream-browserify": "^2.0.1", + "stream-http": "^2.7.2", + "string_decoder": "^1.0.0", + "timers-browserify": "^2.0.4", + "tty-browserify": "0.0.0", + "url": "^0.11.0", + "util": "^0.11.0", + "vm-browserify": "^1.0.1" + }, + "dependencies": { + "punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ==" + } + } + }, + "node-modules-regexp": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/node-modules-regexp/-/node-modules-regexp-1.0.0.tgz", + "integrity": "sha1-jZ2+KJZKSsVxLpExZCEHxx6Q7EA=" + }, + "node-notifier": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/node-notifier/-/node-notifier-8.0.2.tgz", + "integrity": "sha512-oJP/9NAdd9+x2Q+rfphB2RJCHjod70RcRLjosiPMMu5gjIfwVnOUGq2nbTjTUbmy0DJ/tFIVT30+Qe3nzl4TJg==", + "optional": true, + "requires": { + "growly": "^1.3.0", + "is-wsl": "^2.2.0", + "semver": "^7.3.2", + "shellwords": "^0.1.1", + "uuid": "^8.3.0", + "which": "^2.0.2" + }, + "dependencies": { + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "optional": true, + "requires": { + "isexe": "^2.0.0" + } + } + } + }, + "node-releases": { + "version": "1.1.71", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.71.tgz", + "integrity": "sha512-zR6HoT6LrLCRBwukmrVbHv0EpEQjksO6GmFcZQQuCAy139BEsoVKPYnf3jongYW83fAa1torLGYwxxky/p28sg==" + }, + "node-sass": { + "version": "4.14.1", + "resolved": "https://registry.npmjs.org/node-sass/-/node-sass-4.14.1.tgz", + "integrity": "sha512-sjCuOlvGyCJS40R8BscF5vhVlQjNN069NtQ1gSxyK1u9iqvn6tf7O1R4GNowVZfiZUCRt5MmMs1xd+4V/7Yr0g==", + "dev": true, + "requires": { + "async-foreach": "^0.1.3", + "chalk": "^1.1.1", + "cross-spawn": "^3.0.0", + "gaze": "^1.0.0", + "get-stdin": "^4.0.1", + "glob": "^7.0.3", + "in-publish": "^2.0.0", + "lodash": "^4.17.15", + "meow": "^3.7.0", + "mkdirp": "^0.5.1", + "nan": "^2.13.2", + "node-gyp": "^3.8.0", + "npmlog": "^4.0.0", + "request": "^2.88.0", + "sass-graph": "2.2.5", + "stdout-stream": "^1.4.0", + "true-case-path": "^1.0.2" + }, + "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", + "dev": true + }, + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha512-kmCevFghRiWM7HB5zTPULl4r9bVFSWjz62MhqizDGUrq2NWuNMQyuv4tHHoKJHs69M/MF64lEcHdYIocrdWQYA==", + "dev": true + }, + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A==", + "dev": true, + "requires": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + } + }, + "cross-spawn": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-3.0.1.tgz", + "integrity": "sha512-eZ+m1WNhSZutOa/uRblAc9Ut5MQfukFrFMtPSm3bZCA888NmMd5AWXWdgRZ80zd+pTk1P2JrGjg9pUPTvl2PWQ==", + "dev": true, + "requires": { + "lru-cache": "^4.0.1", + "which": "^1.2.9" + } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g==", + "dev": true + } + } + }, + "nopt": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz", + "integrity": "sha512-4GUt3kSEYmk4ITxzB/b9vaIDfUVWN/Ml1Fwl11IlnIG2iaJ9O6WXZ9SrYM9NLI8OCBieN2Y8SWC2oJV0RQ7qYg==", + "dev": true, + "requires": { + "abbrev": "1" + } + }, + "normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "requires": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + }, + "dependencies": { + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" + } + } + }, + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==" + }, + "normalize-range": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", + "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==" + }, + "normalize-url": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-1.9.1.tgz", + "integrity": "sha512-A48My/mtCklowHBlI8Fq2jFWK4tX4lJ5E6ytFsSOq1fzpvT0SQSgKhSg7lN5c2uYFOrUAOQp6zhhJnpp1eMloQ==", + "requires": { + "object-assign": "^4.0.1", + "prepend-http": "^1.0.0", + "query-string": "^4.1.0", + "sort-keys": "^1.0.0" + } + }, + "npm-run-path": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", + "integrity": "sha512-lJxZYlT4DW/bRUtFh1MQIWqmLwQfAxnqWG4HhEdjMlkrJYnJn0Jrr2u3mgxqaWsdiBc76TYkTG/mhrnYTuzfHw==", + "requires": { + "path-key": "^2.0.0" + } + }, + "npmlog": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", + "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", + "dev": true, + "requires": { + "are-we-there-yet": "~1.1.2", + "console-control-strings": "~1.1.0", + "gauge": "~2.7.3", + "set-blocking": "~2.0.0" + } + }, + "nth-check": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-1.0.2.tgz", + "integrity": "sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg==", + "requires": { + "boolbase": "~1.0.0" + } + }, + "null-check": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/null-check/-/null-check-1.0.0.tgz", + "integrity": "sha512-j8ZNHg19TyIQOWCGeeQJBuu6xZYIEurf8M1Qsfd8mFrGEfIZytbw18YjKWg+LcO25NowXGZXZpKAx+Ui3TFfDw==", + "dev": true + }, + "num2fraction": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/num2fraction/-/num2fraction-1.2.2.tgz", + "integrity": "sha512-Y1wZESM7VUThYY+4W+X4ySH2maqcA+p7UR+w8VWNWVAd6lwuXXWz/w/Cz43J/dI2I+PS6wD5N+bJUF+gjWvIqg==" + }, + "number-is-nan": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", + "integrity": "sha512-4jbtZXNAsfZbAHiiqjLPBiCl16dES1zI4Hpzzxw61Tk+loF+sBDBKx1ICKKKwIqQ7M0mFn1TmkN7euSncWgHiQ==", + "dev": true + }, + "nwsapi": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.0.tgz", + "integrity": "sha512-h2AatdwYH+JHiZpv7pt/gSX1XoRGb7L/qSIeuqA6GwYoF9w1vP1cw42TO0aI2pNyshRK5893hNSl+1//vHK7hQ==" + }, + "oauth-sign": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", + "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==" + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==" + }, + "object-copy": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", + "integrity": "sha512-79LYn6VAb63zgtmAteVOWo9Vdj71ZVBy3Pbse+VqxDpEP83XuujMrGqHIwAXJ5I/aM0zU7dIyIAhifVTPrNItQ==", + "requires": { + "copy-descriptor": "^0.1.0", + "define-property": "^0.2.5", + "kind-of": "^3.0.3" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==", + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "object-inspect": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.10.2.tgz", + "integrity": "sha512-gz58rdPpadwztRrPjZE9DZLOABUpTGdcANUgOwBFO1C+HZZhePoP83M65WGDmbpwFYJSWqavbl4SgDn4k8RYTA==" + }, + "object-is": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.5.tgz", + "integrity": "sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw==", + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3" + } + }, + "object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==" + }, + "object-visit": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", + "integrity": "sha512-GBaMwwAVK9qbQN3Scdo0OyvgPW7l3lnaVMj84uTOZlswkX0KpF6fyDBJhtTthf7pymztoN36/KEr1DyhF96zEA==", + "requires": { + "isobject": "^3.0.0" + } + }, + "object.assign": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz", + "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==", + "requires": { + "call-bind": "^1.0.0", + "define-properties": "^1.1.3", + "has-symbols": "^1.0.1", + "object-keys": "^1.1.1" + } + }, + "object.entries": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.3.tgz", + "integrity": "sha512-ym7h7OZebNS96hn5IJeyUmaWhaSM4SVtAPPfNLQEI2MYWCO2egsITb9nab2+i/Pwibx+R0mtn+ltKJXRSeTMGg==", + "requires": { + "call-bind": "^1.0.0", + "define-properties": "^1.1.3", + "es-abstract": "^1.18.0-next.1", + "has": "^1.0.3" + } + }, + "object.fromentries": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.4.tgz", + "integrity": "sha512-EsFBshs5RUUpQEY1D4q/m59kMfz4YJvxuNCJcv/jWwOJr34EaVnG11ZrZa0UHB3wnzV1wx8m58T4hQL8IuNXlQ==", + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.18.0-next.2", + "has": "^1.0.3" + } + }, + "object.getownpropertydescriptors": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.2.tgz", + "integrity": "sha512-WtxeKSzfBjlzL+F9b7M7hewDzMwy+C8NRssHd1YrNlzHzIDrXcXiNOMrezdAEM4UXixgV+vvnyBeN7Rygl2ttQ==", + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.18.0-next.2" + } + }, + "object.pick": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", + "integrity": "sha512-tqa/UMy/CCoYmj+H5qc07qvSL9dqcs/WZENZ1JbtWBlATP+iVOe778gE6MSijnyCnORzDuX6hU+LA4SZ09YjFQ==", + "requires": { + "isobject": "^3.0.1" + } + }, + "object.values": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.3.tgz", + "integrity": "sha512-nkF6PfDB9alkOUxpf1HNm/QlkeW3SReqL5WXeBLpEJJnlPSvRaDQpW3gQTksTN3fgJX4hL42RzKyOin6ff3tyw==", + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.18.0-next.2", + "has": "^1.0.3" + } + }, + "obuf": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz", + "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==" + }, + "on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", + "requires": { + "ee-first": "1.1.1" + } + }, + "on-headers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", + "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==" + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "requires": { + "wrappy": "1" + } + }, + "onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "requires": { + "mimic-fn": "^2.1.0" + } + }, + "open": { + "version": "7.4.2", + "resolved": "https://registry.npmjs.org/open/-/open-7.4.2.tgz", + "integrity": "sha512-MVHddDVweXZF3awtlAS+6pgKLlm/JgxZ90+/NBurBoQctVOOB/zDdVjcyPzQ+0laDGbsWgrRkflI65sQeOgT9Q==", + "requires": { + "is-docker": "^2.0.0", + "is-wsl": "^2.1.1" + } + }, + "opn": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/opn/-/opn-5.5.0.tgz", + "integrity": "sha512-PqHpggC9bLV0VeWcdKhkpxY+3JTzetLSqTCWL/z/tFIbI6G8JCjondXklT1JinczLz2Xib62sSp0T/gKT4KksA==", + "requires": { + "is-wsl": "^1.1.0" + }, + "dependencies": { + "is-wsl": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz", + "integrity": "sha512-gfygJYZ2gLTDlmbWMI0CE2MwnFzSN/2SZfkMlItC4K/JBlsWVDB0bO6XhqcY13YXE7iMcAJnzTCJjPiTeJJ0Mw==" + } + } + }, + "optimize-css-assets-webpack-plugin": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/optimize-css-assets-webpack-plugin/-/optimize-css-assets-webpack-plugin-5.0.4.tgz", + "integrity": "sha512-wqd6FdI2a5/FdoiCNNkEvLeA//lHHfG24Ln2Xm2qqdIk4aOlsR18jwpyOihqQ8849W3qu2DX8fOYxpvTMj+93A==", + "requires": { + "cssnano": "^4.1.10", + "last-call-webpack-plugin": "^3.0.0" + } + }, + "optionator": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", + "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", + "requires": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.3" + } + }, + "original": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/original/-/original-1.0.2.tgz", + "integrity": "sha512-hyBVl6iqqUOJ8FqRe+l/gS8H+kKYjrEndd5Pm1MfBtsEKA038HkkdbAl/72EAXGyonD/PFsvmVG+EvcIpliMBg==", + "requires": { + "url-parse": "^1.4.3" + } + }, + "os-browserify": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.3.0.tgz", + "integrity": "sha512-gjcpUc3clBf9+210TRaDWbf+rZZZEshZ+DlXMRCeAjp0xhTrnQsKHypIy1J3d5hKdUzj69t708EHtU8P6bUn0A==" + }, + "os-homedir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", + "integrity": "sha512-B5JU3cabzk8c67mRRd3ECmROafjYMXbuzlwtqdM8IbS8ktlTix8aFGb2bAGKrSRIlnfKwovGUUr72JUPyOb6kQ==", + "dev": true + }, + "os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==", + "dev": true + }, + "osenv": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz", + "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==", + "dev": true, + "requires": { + "os-homedir": "^1.0.0", + "os-tmpdir": "^1.0.0" + } + }, + "p-each-series": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-each-series/-/p-each-series-2.2.0.tgz", + "integrity": "sha512-ycIL2+1V32th+8scbpTvyHNaHe02z0sjgh91XXjAk+ZeXoPN4Z46DVUnzdso0aX4KckKw0FNNFHdjZ2UsZvxiA==" + }, + "p-finally": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", + "integrity": "sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==" + }, + "p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "requires": { + "p-limit": "^2.2.0" + } + }, + "p-map": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", + "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", + "requires": { + "aggregate-error": "^3.0.0" + } + }, + "p-retry": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-3.0.1.tgz", + "integrity": "sha512-XE6G4+YTTkT2a0UWb2kjZe8xNwf8bIbnqpc/IS/idOBVhyves0mK5OJgeocjx7q5pvX/6m23xuzVPYT1uGM73w==", + "requires": { + "retry": "^0.12.0" + } + }, + "p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==" + }, + "pako": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==" + }, + "parallel-transform": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/parallel-transform/-/parallel-transform-1.2.0.tgz", + "integrity": "sha512-P2vSmIu38uIlvdcU7fDkyrxj33gTUy/ABO5ZUbGowxNCopBq/OoD42bP4UmMrJoPyk4Uqf0mu3mtWBhHCZD8yg==", + "requires": { + "cyclist": "^1.0.1", + "inherits": "^2.0.3", + "readable-stream": "^2.1.5" + } + }, + "param-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/param-case/-/param-case-3.0.4.tgz", + "integrity": "sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==", + "requires": { + "dot-case": "^3.0.4", + "tslib": "^2.0.3" + }, + "dependencies": { + "tslib": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.2.0.tgz", + "integrity": "sha512-gS9GVHRU+RGn5KQM2rllAlR3dU6m7AcpJKdtH8gFvQiC4Otgk98XnmMU+nZenHt/+VhnBPWwgrJsyrdcw6i23w==" + } + } + }, + "parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "requires": { + "callsites": "^3.0.0" + } + }, + "parse-asn1": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.6.tgz", + "integrity": "sha512-RnZRo1EPU6JBnra2vGHj0yhp6ebyjBZpmUCLHWiFhxlzvBCCpAuZ7elsBp1PVAbQN0/04VD/19rfzlBSwLstMw==", + "requires": { + "asn1.js": "^5.2.0", + "browserify-aes": "^1.0.0", + "evp_bytestokey": "^1.0.0", + "pbkdf2": "^3.0.3", + "safe-buffer": "^5.1.1" + } + }, + "parse-github-repo-url": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/parse-github-repo-url/-/parse-github-repo-url-1.4.1.tgz", + "integrity": "sha1-nn2LslKmy2ukJZUGC3v23z28H1A=", + "dev": true + }, + "parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "requires": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + } + }, + "parse-passwd": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/parse-passwd/-/parse-passwd-1.0.0.tgz", + "integrity": "sha512-1Y1A//QUXEZK7YKz+rD9WydcE1+EuPr6ZBgKecAB8tmoW6UFv0NREVJe1p+jRxtThkcbbKkfwIbWJe/IeE6m2Q==", + "dev": true + }, + "parse5": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", + "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==" + }, + "parse5-htmlparser2-tree-adapter": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-6.0.1.tgz", + "integrity": "sha512-qPuWvbLgvDGilKc5BoicRovlT4MtYT6JfJyBOMDsKoiT+GiuP5qyrPCnR9HcPECIJJmZh5jRndyNThnhhb/vlA==", + "dev": true, + "requires": { + "parse5": "^6.0.1" + } + }, + "parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==" + }, + "pascal-case": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/pascal-case/-/pascal-case-3.1.2.tgz", + "integrity": "sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==", + "requires": { + "no-case": "^3.0.4", + "tslib": "^2.0.3" + }, + "dependencies": { + "tslib": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.2.0.tgz", + "integrity": "sha512-gS9GVHRU+RGn5KQM2rllAlR3dU6m7AcpJKdtH8gFvQiC4Otgk98XnmMU+nZenHt/+VhnBPWwgrJsyrdcw6i23w==" + } + } + }, + "pascalcase": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", + "integrity": "sha512-XHXfu/yOQRy9vYOtUDVMN60OEJjW013GoObG1o+xwQTpB9eYJX/BjXMsdW13ZDPruFhYYn0AG22w0xgQMwl3Nw==" + }, + "path-browserify": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.1.tgz", + "integrity": "sha512-BapA40NHICOS+USX9SN4tyhq+A2RrN/Ws5F0Z5aMHDp98Fl86lX8Oti8B7uN93L4Ifv4fHOEA+pQw87gmMO/lQ==" + }, + "path-dirname": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz", + "integrity": "sha512-ALzNPpyNq9AqXMBjeymIjFDAkAFH06mHJH/cSBHAgU0s4vfpBn6b2nf8tiRLvagKD8RbTpq2FKTBg7cl9l3c7Q==" + }, + "path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==" + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==" + }, + "path-is-inside": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", + "integrity": "sha512-DUWJr3+ULp4zXmol/SZkFf3JGsS9/SIv+Y3Rt93/UjPpDpklB5f1er4O3POIbUuUJ3FXgqte2Q7SrU6zAqwk8w==" + }, + "path-key": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==" + }, + "path-parse": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", + "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==" + }, + "path-to-regexp": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.8.0.tgz", + "integrity": "sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA==", + "requires": { + "isarray": "0.0.1" + }, + "dependencies": { + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==" + } + } + }, + "path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==" + }, + "pbkdf2": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.2.tgz", + "integrity": "sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA==", + "requires": { + "create-hash": "^1.1.2", + "create-hmac": "^1.1.4", + "ripemd160": "^2.0.1", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + } + }, + "performance-now": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==" + }, + "picomatch": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.3.tgz", + "integrity": "sha512-KpELjfwcCDUb9PeigTs2mBJzXUPzAuP2oPcA989He8Rte0+YUAjw1JVedDhuTKPkHjSYzMN3npC9luThGYEKdg==" + }, + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", + "dev": true + }, + "pinkie": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", + "integrity": "sha512-MnUuEycAemtSaeFSjXKW/aroV7akBbY+Sv+RkyqFjgAe73F+MR0TBWKBRDkmfWq/HiFmdavfZ1G7h4SPZXaCSg==" + }, + "pinkie-promise": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", + "integrity": "sha512-0Gni6D4UcLTbv9c57DfxDGdr41XfgUjqWZu492f0cIGr16zDU06BWP/RAEvOuo7CQ0CNjHaLlM59YJJFm3NWlw==", + "requires": { + "pinkie": "^2.0.0" + } + }, + "pirates": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.1.tgz", + "integrity": "sha512-WuNqLTbMI3tmfef2TKxlQmAiLHKtFhlsCZnPIpuv2Ow0RDVO8lfy1Opf4NUzlMXLjPl+Men7AuVdX6TA+s+uGA==", + "requires": { + "node-modules-regexp": "^1.0.0" + } + }, + "pkg-dir": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz", + "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==", + "requires": { + "find-up": "^3.0.0" + }, + "dependencies": { + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "requires": { + "locate-path": "^3.0.0" + } + }, + "locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "requires": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + } + }, + "p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "requires": { + "p-limit": "^2.0.0" + } + }, + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==" + } + } + }, + "pkg-up": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/pkg-up/-/pkg-up-3.1.0.tgz", + "integrity": "sha512-nDywThFk1i4BQK4twPQ6TA4RT8bDY96yeuCVBWL3ePARCiEKDRSrNGbFIgUJpLp+XeIR65v8ra7WuJOFUBtkMA==", + "requires": { + "find-up": "^3.0.0" + }, + "dependencies": { + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "requires": { + "locate-path": "^3.0.0" + } + }, + "locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "requires": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + } + }, + "p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "requires": { + "p-limit": "^2.0.0" + } + }, + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==" + } + } + }, + "please-upgrade-node": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/please-upgrade-node/-/please-upgrade-node-3.2.0.tgz", + "integrity": "sha512-gQR3WpIgNIKwBMVLkpMUeR3e1/E1y42bqDQZfql+kDeXd8COYfM8PQA4X6y7a8u9Ua9FHmsrrmirW2vHs45hWg==", + "dev": true, + "requires": { + "semver-compare": "^1.0.0" + } + }, + "pnp-webpack-plugin": { + "version": "1.6.4", + "resolved": "https://registry.npmjs.org/pnp-webpack-plugin/-/pnp-webpack-plugin-1.6.4.tgz", + "integrity": "sha512-7Wjy+9E3WwLOEL30D+m8TSTF7qJJUJLONBnwQp0518siuMxUQUbgZwssaFX+QKlZkjHZcw/IpZCt/H0srrntSg==", + "requires": { + "ts-pnp": "^1.1.6" + } + }, + "popper.js": { + "version": "1.16.1", + "resolved": "https://registry.npmjs.org/popper.js/-/popper.js-1.16.1.tgz", + "integrity": "sha512-Wb4p1J4zyFTbM+u6WuO4XstYx4Ky9Cewe4DWrel7B0w6VVICvPwdOpotjzcf6eD8TsckVnIMNONQyPIUFOUbCQ==" + }, + "portfinder": { + "version": "1.0.28", + "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.28.tgz", + "integrity": "sha512-Se+2isanIcEqf2XMHjyUKskczxbPH7dQnlMjXX6+dybayyHvAf/TCgyMRlzf/B6QDhAEFOGes0pzRo3by4AbMA==", + "requires": { + "async": "^2.6.2", + "debug": "^3.1.1", + "mkdirp": "^0.5.5" + }, + "dependencies": { + "debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "requires": { + "ms": "^2.1.1" + } + } + } + }, + "posix-character-classes": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", + "integrity": "sha512-xTgYBc3fuo7Yt7JbiuFxSYGToMoz8fLoE6TC9Wx1P/u+LfeThMOAqmuyECnlBaaJb+u1m9hHiXUEtwW4OzfUJg==" + }, + "postcss": { + "version": "7.0.35", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz", + "integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==", + "requires": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "postcss-attribute-case-insensitive": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-attribute-case-insensitive/-/postcss-attribute-case-insensitive-4.0.2.tgz", + "integrity": "sha512-clkFxk/9pcdb4Vkn0hAHq3YnxBQ2p0CGD1dy24jN+reBck+EWxMbxSUqN4Yj7t0w8csl87K6p0gxBe1utkJsYA==", + "requires": { + "postcss": "^7.0.2", + "postcss-selector-parser": "^6.0.2" + } + }, + "postcss-browser-comments": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postcss-browser-comments/-/postcss-browser-comments-3.0.0.tgz", + "integrity": "sha512-qfVjLfq7HFd2e0HW4s1dvU8X080OZdG46fFbIBFjW7US7YPDcWfRvdElvwMJr2LI6hMmD+7LnH2HcmXTs+uOig==", + "requires": { + "postcss": "^7" + } + }, + "postcss-calc": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/postcss-calc/-/postcss-calc-7.0.5.tgz", + "integrity": "sha512-1tKHutbGtLtEZF6PT4JSihCHfIVldU72mZ8SdZHIYriIZ9fh9k9aWSppaT8rHsyI3dX+KSR+W+Ix9BMY3AODrg==", + "requires": { + "postcss": "^7.0.27", + "postcss-selector-parser": "^6.0.2", + "postcss-value-parser": "^4.0.2" + } + }, + "postcss-color-functional-notation": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/postcss-color-functional-notation/-/postcss-color-functional-notation-2.0.1.tgz", + "integrity": "sha512-ZBARCypjEDofW4P6IdPVTLhDNXPRn8T2s1zHbZidW6rPaaZvcnCS2soYFIQJrMZSxiePJ2XIYTlcb2ztr/eT2g==", + "requires": { + "postcss": "^7.0.2", + "postcss-values-parser": "^2.0.0" + } + }, + "postcss-color-gray": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/postcss-color-gray/-/postcss-color-gray-5.0.0.tgz", + "integrity": "sha512-q6BuRnAGKM/ZRpfDascZlIZPjvwsRye7UDNalqVz3s7GDxMtqPY6+Q871liNxsonUw8oC61OG+PSaysYpl1bnw==", + "requires": { + "@csstools/convert-colors": "^1.4.0", + "postcss": "^7.0.5", + "postcss-values-parser": "^2.0.0" + } + }, + "postcss-color-hex-alpha": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/postcss-color-hex-alpha/-/postcss-color-hex-alpha-5.0.3.tgz", + "integrity": "sha512-PF4GDel8q3kkreVXKLAGNpHKilXsZ6xuu+mOQMHWHLPNyjiUBOr75sp5ZKJfmv1MCus5/DWUGcK9hm6qHEnXYw==", + "requires": { + "postcss": "^7.0.14", + "postcss-values-parser": "^2.0.1" + } + }, + "postcss-color-mod-function": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/postcss-color-mod-function/-/postcss-color-mod-function-3.0.3.tgz", + "integrity": "sha512-YP4VG+xufxaVtzV6ZmhEtc+/aTXH3d0JLpnYfxqTvwZPbJhWqp8bSY3nfNzNRFLgB4XSaBA82OE4VjOOKpCdVQ==", + "requires": { + "@csstools/convert-colors": "^1.4.0", + "postcss": "^7.0.2", + "postcss-values-parser": "^2.0.0" + } + }, + "postcss-color-rebeccapurple": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-color-rebeccapurple/-/postcss-color-rebeccapurple-4.0.1.tgz", + "integrity": "sha512-aAe3OhkS6qJXBbqzvZth2Au4V3KieR5sRQ4ptb2b2O8wgvB3SJBsdG+jsn2BZbbwekDG8nTfcCNKcSfe/lEy8g==", + "requires": { + "postcss": "^7.0.2", + "postcss-values-parser": "^2.0.0" + } + }, + "postcss-colormin": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-4.0.3.tgz", + "integrity": "sha512-WyQFAdDZpExQh32j0U0feWisZ0dmOtPl44qYmJKkq9xFWY3p+4qnRzCHeNrkeRhwPHz9bQ3mo0/yVkaply0MNw==", + "requires": { + "browserslist": "^4.0.0", + "color": "^3.0.0", + "has": "^1.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "dependencies": { + "postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" + } + } + }, + "postcss-convert-values": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-4.0.1.tgz", + "integrity": "sha512-Kisdo1y77KUC0Jmn0OXU/COOJbzM8cImvw1ZFsBgBgMgb1iL23Zs/LXRe3r+EZqM3vGYKdQ2YJVQ5VkJI+zEJQ==", + "requires": { + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "dependencies": { + "postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" + } + } + }, + "postcss-custom-media": { + "version": "7.0.8", + "resolved": "https://registry.npmjs.org/postcss-custom-media/-/postcss-custom-media-7.0.8.tgz", + "integrity": "sha512-c9s5iX0Ge15o00HKbuRuTqNndsJUbaXdiNsksnVH8H4gdc+zbLzr/UasOwNG6CTDpLFekVY4672eWdiiWu2GUg==", + "requires": { + "postcss": "^7.0.14" + } + }, + "postcss-custom-properties": { + "version": "8.0.11", + "resolved": "https://registry.npmjs.org/postcss-custom-properties/-/postcss-custom-properties-8.0.11.tgz", + "integrity": "sha512-nm+o0eLdYqdnJ5abAJeXp4CEU1c1k+eB2yMCvhgzsds/e0umabFrN6HoTy/8Q4K5ilxERdl/JD1LO5ANoYBeMA==", + "requires": { + "postcss": "^7.0.17", + "postcss-values-parser": "^2.0.1" + } + }, + "postcss-custom-selectors": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/postcss-custom-selectors/-/postcss-custom-selectors-5.1.2.tgz", + "integrity": "sha512-DSGDhqinCqXqlS4R7KGxL1OSycd1lydugJ1ky4iRXPHdBRiozyMHrdu0H3o7qNOCiZwySZTUI5MV0T8QhCLu+w==", + "requires": { + "postcss": "^7.0.2", + "postcss-selector-parser": "^5.0.0-rc.3" + }, + "dependencies": { + "cssesc": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-2.0.0.tgz", + "integrity": "sha512-MsCAG1z9lPdoO/IUMLSBWBSVxVtJ1395VGIQ+Fc2gNdkQ1hNDnQdw3YhA71WJCBW1vdwA0cAnk/DnW6bqoEUYg==" + }, + "postcss-selector-parser": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-5.0.0.tgz", + "integrity": "sha512-w+zLE5Jhg6Liz8+rQOWEAwtwkyqpfnmsinXjXg6cY7YIONZZtgvE0v2O0uhQBs0peNomOJwWRKt6JBfTdTd3OQ==", + "requires": { + "cssesc": "^2.0.0", + "indexes-of": "^1.0.1", + "uniq": "^1.0.1" + } + } + } + }, + "postcss-dir-pseudo-class": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/postcss-dir-pseudo-class/-/postcss-dir-pseudo-class-5.0.0.tgz", + "integrity": "sha512-3pm4oq8HYWMZePJY+5ANriPs3P07q+LW6FAdTlkFH2XqDdP4HeeJYMOzn0HYLhRSjBO3fhiqSwwU9xEULSrPgw==", + "requires": { + "postcss": "^7.0.2", + "postcss-selector-parser": "^5.0.0-rc.3" + }, + "dependencies": { + "cssesc": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-2.0.0.tgz", + "integrity": "sha512-MsCAG1z9lPdoO/IUMLSBWBSVxVtJ1395VGIQ+Fc2gNdkQ1hNDnQdw3YhA71WJCBW1vdwA0cAnk/DnW6bqoEUYg==" + }, + "postcss-selector-parser": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-5.0.0.tgz", + "integrity": "sha512-w+zLE5Jhg6Liz8+rQOWEAwtwkyqpfnmsinXjXg6cY7YIONZZtgvE0v2O0uhQBs0peNomOJwWRKt6JBfTdTd3OQ==", + "requires": { + "cssesc": "^2.0.0", + "indexes-of": "^1.0.1", + "uniq": "^1.0.1" + } + } + } + }, + "postcss-discard-comments": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-4.0.2.tgz", + "integrity": "sha512-RJutN259iuRf3IW7GZyLM5Sw4GLTOH8FmsXBnv8Ab/Tc2k4SR4qbV4DNbyyY4+Sjo362SyDmW2DQ7lBSChrpkg==", + "requires": { + "postcss": "^7.0.0" + } + }, + "postcss-discard-duplicates": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-4.0.2.tgz", + "integrity": "sha512-ZNQfR1gPNAiXZhgENFfEglF93pciw0WxMkJeVmw8eF+JZBbMD7jp6C67GqJAXVZP2BWbOztKfbsdmMp/k8c6oQ==", + "requires": { + "postcss": "^7.0.0" + } + }, + "postcss-discard-empty": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-4.0.1.tgz", + "integrity": "sha512-B9miTzbznhDjTfjvipfHoqbWKwd0Mj+/fL5s1QOz06wufguil+Xheo4XpOnc4NqKYBCNqqEzgPv2aPBIJLox0w==", + "requires": { + "postcss": "^7.0.0" + } + }, + "postcss-discard-overridden": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-4.0.1.tgz", + "integrity": "sha512-IYY2bEDD7g1XM1IDEsUT4//iEYCxAmP5oDSFMVU/JVvT7gh+l4fmjciLqGgwjdWpQIdb0Che2VX00QObS5+cTg==", + "requires": { + "postcss": "^7.0.0" + } + }, + "postcss-double-position-gradients": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/postcss-double-position-gradients/-/postcss-double-position-gradients-1.0.0.tgz", + "integrity": "sha512-G+nV8EnQq25fOI8CH/B6krEohGWnF5+3A6H/+JEpOncu5dCnkS1QQ6+ct3Jkaepw1NGVqqOZH6lqrm244mCftA==", + "requires": { + "postcss": "^7.0.5", + "postcss-values-parser": "^2.0.0" + } + }, + "postcss-env-function": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/postcss-env-function/-/postcss-env-function-2.0.2.tgz", + "integrity": "sha512-rwac4BuZlITeUbiBq60h/xbLzXY43qOsIErngWa4l7Mt+RaSkT7QBjXVGTcBHupykkblHMDrBFh30zchYPaOUw==", + "requires": { + "postcss": "^7.0.2", + "postcss-values-parser": "^2.0.0" + } + }, + "postcss-flexbugs-fixes": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/postcss-flexbugs-fixes/-/postcss-flexbugs-fixes-4.2.1.tgz", + "integrity": "sha512-9SiofaZ9CWpQWxOwRh1b/r85KD5y7GgvsNt1056k6OYLvWUun0czCvogfJgylC22uJTwW1KzY3Gz65NZRlvoiQ==", + "requires": { + "postcss": "^7.0.26" + } + }, + "postcss-focus-visible": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-focus-visible/-/postcss-focus-visible-4.0.0.tgz", + "integrity": "sha512-Z5CkWBw0+idJHSV6+Bgf2peDOFf/x4o+vX/pwcNYrWpXFrSfTkQ3JQ1ojrq9yS+upnAlNRHeg8uEwFTgorjI8g==", + "requires": { + "postcss": "^7.0.2" + } + }, + "postcss-focus-within": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postcss-focus-within/-/postcss-focus-within-3.0.0.tgz", + "integrity": "sha512-W0APui8jQeBKbCGZudW37EeMCjDeVxKgiYfIIEo8Bdh5SpB9sxds/Iq8SEuzS0Q4YFOlG7EPFulbbxujpkrV2w==", + "requires": { + "postcss": "^7.0.2" + } + }, + "postcss-font-variant": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-font-variant/-/postcss-font-variant-4.0.1.tgz", + "integrity": "sha512-I3ADQSTNtLTTd8uxZhtSOrTCQ9G4qUVKPjHiDk0bV75QSxXjVWiJVJ2VLdspGUi9fbW9BcjKJoRvxAH1pckqmA==", + "requires": { + "postcss": "^7.0.2" + } + }, + "postcss-gap-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/postcss-gap-properties/-/postcss-gap-properties-2.0.0.tgz", + "integrity": "sha512-QZSqDaMgXCHuHTEzMsS2KfVDOq7ZFiknSpkrPJY6jmxbugUPTuSzs/vuE5I3zv0WAS+3vhrlqhijiprnuQfzmg==", + "requires": { + "postcss": "^7.0.2" + } + }, + "postcss-image-set-function": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/postcss-image-set-function/-/postcss-image-set-function-3.0.1.tgz", + "integrity": "sha512-oPTcFFip5LZy8Y/whto91L9xdRHCWEMs3e1MdJxhgt4jy2WYXfhkng59fH5qLXSCPN8k4n94p1Czrfe5IOkKUw==", + "requires": { + "postcss": "^7.0.2", + "postcss-values-parser": "^2.0.0" + } + }, + "postcss-initial": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/postcss-initial/-/postcss-initial-3.0.4.tgz", + "integrity": "sha512-3RLn6DIpMsK1l5UUy9jxQvoDeUN4gP939tDcKUHD/kM8SGSKbFAnvkpFpj3Bhtz3HGk1jWY5ZNWX6mPta5M9fg==", + "requires": { + "postcss": "^7.0.2" + } + }, + "postcss-lab-function": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/postcss-lab-function/-/postcss-lab-function-2.0.1.tgz", + "integrity": "sha512-whLy1IeZKY+3fYdqQFuDBf8Auw+qFuVnChWjmxm/UhHWqNHZx+B99EwxTvGYmUBqe3Fjxs4L1BoZTJmPu6usVg==", + "requires": { + "@csstools/convert-colors": "^1.4.0", + "postcss": "^7.0.2", + "postcss-values-parser": "^2.0.0" + } + }, + "postcss-load-config": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-2.1.2.tgz", + "integrity": "sha512-/rDeGV6vMUo3mwJZmeHfEDvwnTKKqQ0S7OHUi/kJvvtx3aWtyWG2/0ZWnzCt2keEclwN6Tf0DST2v9kITdOKYw==", + "requires": { + "cosmiconfig": "^5.0.0", + "import-cwd": "^2.0.0" + }, + "dependencies": { + "cosmiconfig": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-5.2.1.tgz", + "integrity": "sha512-H65gsXo1SKjf8zmrJ67eJk8aIRKV5ff2D4uKZIBZShbhGSpEmsQOPW/SKMKYhSTrqR7ufy6RP69rPogdaPh/kA==", + "requires": { + "import-fresh": "^2.0.0", + "is-directory": "^0.3.1", + "js-yaml": "^3.13.1", + "parse-json": "^4.0.0" + } + }, + "import-fresh": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-2.0.0.tgz", + "integrity": "sha512-eZ5H8rcgYazHbKC3PG4ClHNykCSxtAhxSSEM+2mb+7evD2CKF5V7c0dNum7AdpDh0ZdICwZY9sRSn8f+KH96sg==", + "requires": { + "caller-path": "^2.0.0", + "resolve-from": "^3.0.0" + } + }, + "parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==", + "requires": { + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" + } + }, + "resolve-from": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz", + "integrity": "sha512-GnlH6vxLymXJNMBo7XP1fJIzBFbdYt49CuTwmB/6N53t+kMPRMFKz783LlQ4tv28XoQfMWinAJX6WCGf2IlaIw==" + } + } + }, + "postcss-loader": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-3.0.0.tgz", + "integrity": "sha512-cLWoDEY5OwHcAjDnkyRQzAXfs2jrKjXpO/HQFcc5b5u/r7aa471wdmChmwfnv7x2u840iat/wi0lQ5nbRgSkUA==", + "requires": { + "loader-utils": "^1.1.0", + "postcss": "^7.0.0", + "postcss-load-config": "^2.0.0", + "schema-utils": "^1.0.0" + }, + "dependencies": { + "json5": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", + "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", + "requires": { + "minimist": "^1.2.0" + } + }, + "loader-utils": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz", + "integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==", + "requires": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^1.0.1" + } + }, + "schema-utils": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz", + "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==", + "requires": { + "ajv": "^6.1.0", + "ajv-errors": "^1.0.0", + "ajv-keywords": "^3.1.0" + } + } + } + }, + "postcss-logical": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postcss-logical/-/postcss-logical-3.0.0.tgz", + "integrity": "sha512-1SUKdJc2vuMOmeItqGuNaC+N8MzBWFWEkAnRnLpFYj1tGGa7NqyVBujfRtgNa2gXR+6RkGUiB2O5Vmh7E2RmiA==", + "requires": { + "postcss": "^7.0.2" + } + }, + "postcss-media-minmax": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-media-minmax/-/postcss-media-minmax-4.0.0.tgz", + "integrity": "sha512-fo9moya6qyxsjbFAYl97qKO9gyre3qvbMnkOZeZwlsW6XYFsvs2DMGDlchVLfAd8LHPZDxivu/+qW2SMQeTHBw==", + "requires": { + "postcss": "^7.0.2" + } + }, + "postcss-merge-longhand": { + "version": "4.0.11", + "resolved": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-4.0.11.tgz", + "integrity": "sha512-alx/zmoeXvJjp7L4mxEMjh8lxVlDFX1gqWHzaaQewwMZiVhLo42TEClKaeHbRf6J7j82ZOdTJ808RtN0ZOZwvw==", + "requires": { + "css-color-names": "0.0.4", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0", + "stylehacks": "^4.0.0" + }, + "dependencies": { + "postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" + } + } + }, + "postcss-merge-rules": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-4.0.3.tgz", + "integrity": "sha512-U7e3r1SbvYzO0Jr3UT/zKBVgYYyhAz0aitvGIYOYK5CPmkNih+WDSsS5tvPrJ8YMQYlEMvsZIiqmn7HdFUaeEQ==", + "requires": { + "browserslist": "^4.0.0", + "caniuse-api": "^3.0.0", + "cssnano-util-same-parent": "^4.0.0", + "postcss": "^7.0.0", + "postcss-selector-parser": "^3.0.0", + "vendors": "^1.0.0" + }, + "dependencies": { + "postcss-selector-parser": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-3.1.2.tgz", + "integrity": "sha512-h7fJ/5uWuRVyOtkO45pnt1Ih40CEleeyCHzipqAZO2e5H20g25Y48uYnFUiShvY4rZWNJ/Bib/KVPmanaCtOhA==", + "requires": { + "dot-prop": "^5.2.0", + "indexes-of": "^1.0.1", + "uniq": "^1.0.1" + } + } + } + }, + "postcss-minify-font-values": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-minify-font-values/-/postcss-minify-font-values-4.0.2.tgz", + "integrity": "sha512-j85oO6OnRU9zPf04+PZv1LYIYOprWm6IA6zkXkrJXyRveDEuQggG6tvoy8ir8ZwjLxLuGfNkCZEQG7zan+Hbtg==", + "requires": { + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "dependencies": { + "postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" + } + } + }, + "postcss-minify-gradients": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-4.0.2.tgz", + "integrity": "sha512-qKPfwlONdcf/AndP1U8SJ/uzIJtowHlMaSioKzebAXSG4iJthlWC9iSWznQcX4f66gIWX44RSA841HTHj3wK+Q==", + "requires": { + "cssnano-util-get-arguments": "^4.0.0", + "is-color-stop": "^1.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "dependencies": { + "postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" + } + } + }, + "postcss-minify-params": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-4.0.2.tgz", + "integrity": "sha512-G7eWyzEx0xL4/wiBBJxJOz48zAKV2WG3iZOqVhPet/9geefm/Px5uo1fzlHu+DOjT+m0Mmiz3jkQzVHe6wxAWg==", + "requires": { + "alphanum-sort": "^1.0.0", + "browserslist": "^4.0.0", + "cssnano-util-get-arguments": "^4.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0", + "uniqs": "^2.0.0" + }, + "dependencies": { + "postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" + } + } + }, + "postcss-minify-selectors": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-4.0.2.tgz", + "integrity": "sha512-D5S1iViljXBj9kflQo4YutWnJmwm8VvIsU1GeXJGiG9j8CIg9zs4voPMdQDUmIxetUOh60VilsNzCiAFTOqu3g==", + "requires": { + "alphanum-sort": "^1.0.0", + "has": "^1.0.0", + "postcss": "^7.0.0", + "postcss-selector-parser": "^3.0.0" + }, + "dependencies": { + "postcss-selector-parser": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-3.1.2.tgz", + "integrity": "sha512-h7fJ/5uWuRVyOtkO45pnt1Ih40CEleeyCHzipqAZO2e5H20g25Y48uYnFUiShvY4rZWNJ/Bib/KVPmanaCtOhA==", + "requires": { + "dot-prop": "^5.2.0", + "indexes-of": "^1.0.1", + "uniq": "^1.0.1" + } + } + } + }, + "postcss-modules-extract-imports": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-2.0.0.tgz", + "integrity": "sha512-LaYLDNS4SG8Q5WAWqIJgdHPJrDDr/Lv775rMBFUbgjTz6j34lUznACHcdRWroPvXANP2Vj7yNK57vp9eFqzLWQ==", + "requires": { + "postcss": "^7.0.5" + } + }, + "postcss-modules-local-by-default": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-3.0.3.tgz", + "integrity": "sha512-e3xDq+LotiGesympRlKNgaJ0PCzoUIdpH0dj47iWAui/kyTgh3CiAr1qP54uodmJhl6p9rN6BoNcdEDVJx9RDw==", + "requires": { + "icss-utils": "^4.1.1", + "postcss": "^7.0.32", + "postcss-selector-parser": "^6.0.2", + "postcss-value-parser": "^4.1.0" + } + }, + "postcss-modules-scope": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-2.2.0.tgz", + "integrity": "sha512-YyEgsTMRpNd+HmyC7H/mh3y+MeFWevy7V1evVhJWewmMbjDHIbZbOXICC2y+m1xI1UVfIT1HMW/O04Hxyu9oXQ==", + "requires": { + "postcss": "^7.0.6", + "postcss-selector-parser": "^6.0.0" + } + }, + "postcss-modules-values": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-3.0.0.tgz", + "integrity": "sha512-1//E5jCBrZ9DmRX+zCtmQtRSV6PV42Ix7Bzj9GbwJceduuf7IqP8MgeTXuRDHOWj2m0VzZD5+roFWDuU8RQjcg==", + "requires": { + "icss-utils": "^4.0.0", + "postcss": "^7.0.6" + } + }, + "postcss-nesting": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/postcss-nesting/-/postcss-nesting-7.0.1.tgz", + "integrity": "sha512-FrorPb0H3nuVq0Sff7W2rnc3SmIcruVC6YwpcS+k687VxyxO33iE1amna7wHuRVzM8vfiYofXSBHNAZ3QhLvYg==", + "requires": { + "postcss": "^7.0.2" + } + }, + "postcss-normalize": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/postcss-normalize/-/postcss-normalize-8.0.1.tgz", + "integrity": "sha512-rt9JMS/m9FHIRroDDBGSMsyW1c0fkvOJPy62ggxSHUldJO7B195TqFMqIf+lY5ezpDcYOV4j86aUp3/XbxzCCQ==", + "requires": { + "@csstools/normalize.css": "^10.1.0", + "browserslist": "^4.6.2", + "postcss": "^7.0.17", + "postcss-browser-comments": "^3.0.0", + "sanitize.css": "^10.0.0" + } + }, + "postcss-normalize-charset": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-4.0.1.tgz", + "integrity": "sha512-gMXCrrlWh6G27U0hF3vNvR3w8I1s2wOBILvA87iNXaPvSNo5uZAMYsZG7XjCUf1eVxuPfyL4TJ7++SGZLc9A3g==", + "requires": { + "postcss": "^7.0.0" + } + }, + "postcss-normalize-display-values": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-normalize-display-values/-/postcss-normalize-display-values-4.0.2.tgz", + "integrity": "sha512-3F2jcsaMW7+VtRMAqf/3m4cPFhPD3EFRgNs18u+k3lTJJlVe7d0YPO+bnwqo2xg8YiRpDXJI2u8A0wqJxMsQuQ==", + "requires": { + "cssnano-util-get-match": "^4.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "dependencies": { + "postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" + } + } + }, + "postcss-normalize-positions": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-normalize-positions/-/postcss-normalize-positions-4.0.2.tgz", + "integrity": "sha512-Dlf3/9AxpxE+NF1fJxYDeggi5WwV35MXGFnnoccP/9qDtFrTArZ0D0R+iKcg5WsUd8nUYMIl8yXDCtcrT8JrdA==", + "requires": { + "cssnano-util-get-arguments": "^4.0.0", + "has": "^1.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "dependencies": { + "postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" + } + } + }, + "postcss-normalize-repeat-style": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-4.0.2.tgz", + "integrity": "sha512-qvigdYYMpSuoFs3Is/f5nHdRLJN/ITA7huIoCyqqENJe9PvPmLhNLMu7QTjPdtnVf6OcYYO5SHonx4+fbJE1+Q==", + "requires": { + "cssnano-util-get-arguments": "^4.0.0", + "cssnano-util-get-match": "^4.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "dependencies": { + "postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" + } + } + }, + "postcss-normalize-string": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-normalize-string/-/postcss-normalize-string-4.0.2.tgz", + "integrity": "sha512-RrERod97Dnwqq49WNz8qo66ps0swYZDSb6rM57kN2J+aoyEAJfZ6bMx0sx/F9TIEX0xthPGCmeyiam/jXif0eA==", + "requires": { + "has": "^1.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "dependencies": { + "postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" + } + } + }, + "postcss-normalize-timing-functions": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-4.0.2.tgz", + "integrity": "sha512-acwJY95edP762e++00Ehq9L4sZCEcOPyaHwoaFOhIwWCDfik6YvqsYNxckee65JHLKzuNSSmAdxwD2Cud1Z54A==", + "requires": { + "cssnano-util-get-match": "^4.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "dependencies": { + "postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" + } + } + }, + "postcss-normalize-unicode": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-unicode/-/postcss-normalize-unicode-4.0.1.tgz", + "integrity": "sha512-od18Uq2wCYn+vZ/qCOeutvHjB5jm57ToxRaMeNuf0nWVHaP9Hua56QyMF6fs/4FSUnVIw0CBPsU0K4LnBPwYwg==", + "requires": { + "browserslist": "^4.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "dependencies": { + "postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" + } + } + }, + "postcss-normalize-url": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-url/-/postcss-normalize-url-4.0.1.tgz", + "integrity": "sha512-p5oVaF4+IHwu7VpMan/SSpmpYxcJMtkGppYf0VbdH5B6hN8YNmVyJLuY9FmLQTzY3fag5ESUUHDqM+heid0UVA==", + "requires": { + "is-absolute-url": "^2.0.0", + "normalize-url": "^3.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "dependencies": { + "normalize-url": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-3.3.0.tgz", + "integrity": "sha512-U+JJi7duF1o+u2pynbp2zXDW2/PADgC30f0GsHZtRh+HOcXHnw137TrNlyxxRvWW5fjKd3bcLHPxofWuCjaeZg==" + }, + "postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" + } + } + }, + "postcss-normalize-whitespace": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-normalize-whitespace/-/postcss-normalize-whitespace-4.0.2.tgz", + "integrity": "sha512-tO8QIgrsI3p95r8fyqKV+ufKlSHh9hMJqACqbv2XknufqEDhDvbguXGBBqxw9nsQoXWf0qOqppziKJKHMD4GtA==", + "requires": { + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "dependencies": { + "postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" + } + } + }, + "postcss-ordered-values": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/postcss-ordered-values/-/postcss-ordered-values-4.1.2.tgz", + "integrity": "sha512-2fCObh5UanxvSxeXrtLtlwVThBvHn6MQcu4ksNT2tsaV2Fg76R2CV98W7wNSlX+5/pFwEyaDwKLLoEV7uRybAw==", + "requires": { + "cssnano-util-get-arguments": "^4.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "dependencies": { + "postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" + } + } + }, + "postcss-overflow-shorthand": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/postcss-overflow-shorthand/-/postcss-overflow-shorthand-2.0.0.tgz", + "integrity": "sha512-aK0fHc9CBNx8jbzMYhshZcEv8LtYnBIRYQD5i7w/K/wS9c2+0NSR6B3OVMu5y0hBHYLcMGjfU+dmWYNKH0I85g==", + "requires": { + "postcss": "^7.0.2" + } + }, + "postcss-page-break": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/postcss-page-break/-/postcss-page-break-2.0.0.tgz", + "integrity": "sha512-tkpTSrLpfLfD9HvgOlJuigLuk39wVTbbd8RKcy8/ugV2bNBUW3xU+AIqyxhDrQr1VUj1RmyJrBn1YWrqUm9zAQ==", + "requires": { + "postcss": "^7.0.2" + } + }, + "postcss-place": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-place/-/postcss-place-4.0.1.tgz", + "integrity": "sha512-Zb6byCSLkgRKLODj/5mQugyuj9bvAAw9LqJJjgwz5cYryGeXfFZfSXoP1UfveccFmeq0b/2xxwcTEVScnqGxBg==", + "requires": { + "postcss": "^7.0.2", + "postcss-values-parser": "^2.0.0" + } + }, + "postcss-preset-env": { + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/postcss-preset-env/-/postcss-preset-env-6.7.0.tgz", + "integrity": "sha512-eU4/K5xzSFwUFJ8hTdTQzo2RBLbDVt83QZrAvI07TULOkmyQlnYlpwep+2yIK+K+0KlZO4BvFcleOCCcUtwchg==", + "requires": { + "autoprefixer": "^9.6.1", + "browserslist": "^4.6.4", + "caniuse-lite": "^1.0.30000981", + "css-blank-pseudo": "^0.1.4", + "css-has-pseudo": "^0.10.0", + "css-prefers-color-scheme": "^3.1.1", + "cssdb": "^4.4.0", + "postcss": "^7.0.17", + "postcss-attribute-case-insensitive": "^4.0.1", + "postcss-color-functional-notation": "^2.0.1", + "postcss-color-gray": "^5.0.0", + "postcss-color-hex-alpha": "^5.0.3", + "postcss-color-mod-function": "^3.0.3", + "postcss-color-rebeccapurple": "^4.0.1", + "postcss-custom-media": "^7.0.8", + "postcss-custom-properties": "^8.0.11", + "postcss-custom-selectors": "^5.1.2", + "postcss-dir-pseudo-class": "^5.0.0", + "postcss-double-position-gradients": "^1.0.0", + "postcss-env-function": "^2.0.2", + "postcss-focus-visible": "^4.0.0", + "postcss-focus-within": "^3.0.0", + "postcss-font-variant": "^4.0.0", + "postcss-gap-properties": "^2.0.0", + "postcss-image-set-function": "^3.0.1", + "postcss-initial": "^3.0.0", + "postcss-lab-function": "^2.0.1", + "postcss-logical": "^3.0.0", + "postcss-media-minmax": "^4.0.0", + "postcss-nesting": "^7.0.0", + "postcss-overflow-shorthand": "^2.0.0", + "postcss-page-break": "^2.0.0", + "postcss-place": "^4.0.1", + "postcss-pseudo-class-any-link": "^6.0.0", + "postcss-replace-overflow-wrap": "^3.0.0", + "postcss-selector-matches": "^4.0.0", + "postcss-selector-not": "^4.0.0" + } + }, + "postcss-pseudo-class-any-link": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/postcss-pseudo-class-any-link/-/postcss-pseudo-class-any-link-6.0.0.tgz", + "integrity": "sha512-lgXW9sYJdLqtmw23otOzrtbDXofUdfYzNm4PIpNE322/swES3VU9XlXHeJS46zT2onFO7V1QFdD4Q9LiZj8mew==", + "requires": { + "postcss": "^7.0.2", + "postcss-selector-parser": "^5.0.0-rc.3" + }, + "dependencies": { + "cssesc": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-2.0.0.tgz", + "integrity": "sha512-MsCAG1z9lPdoO/IUMLSBWBSVxVtJ1395VGIQ+Fc2gNdkQ1hNDnQdw3YhA71WJCBW1vdwA0cAnk/DnW6bqoEUYg==" + }, + "postcss-selector-parser": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-5.0.0.tgz", + "integrity": "sha512-w+zLE5Jhg6Liz8+rQOWEAwtwkyqpfnmsinXjXg6cY7YIONZZtgvE0v2O0uhQBs0peNomOJwWRKt6JBfTdTd3OQ==", + "requires": { + "cssesc": "^2.0.0", + "indexes-of": "^1.0.1", + "uniq": "^1.0.1" + } + } + } + }, + "postcss-reduce-initial": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-4.0.3.tgz", + "integrity": "sha512-gKWmR5aUulSjbzOfD9AlJiHCGH6AEVLaM0AV+aSioxUDd16qXP1PCh8d1/BGVvpdWn8k/HiK7n6TjeoXN1F7DA==", + "requires": { + "browserslist": "^4.0.0", + "caniuse-api": "^3.0.0", + "has": "^1.0.0", + "postcss": "^7.0.0" + } + }, + "postcss-reduce-transforms": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-reduce-transforms/-/postcss-reduce-transforms-4.0.2.tgz", + "integrity": "sha512-EEVig1Q2QJ4ELpJXMZR8Vt5DQx8/mo+dGWSR7vWXqcob2gQLyQGsionYcGKATXvQzMPn6DSN1vTN7yFximdIAg==", + "requires": { + "cssnano-util-get-match": "^4.0.0", + "has": "^1.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "dependencies": { + "postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" + } + } + }, + "postcss-replace-overflow-wrap": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postcss-replace-overflow-wrap/-/postcss-replace-overflow-wrap-3.0.0.tgz", + "integrity": "sha512-2T5hcEHArDT6X9+9dVSPQdo7QHzG4XKclFT8rU5TzJPDN7RIRTbO9c4drUISOVemLj03aezStHCR2AIcr8XLpw==", + "requires": { + "postcss": "^7.0.2" + } + }, + "postcss-safe-parser": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/postcss-safe-parser/-/postcss-safe-parser-5.0.2.tgz", + "integrity": "sha512-jDUfCPJbKOABhwpUKcqCVbbXiloe/QXMcbJ6Iipf3sDIihEzTqRCeMBfRaOHxhBuTYqtASrI1KJWxzztZU4qUQ==", + "requires": { + "postcss": "^8.1.0" + }, + "dependencies": { + "postcss": { + "version": "8.2.13", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.2.13.tgz", + "integrity": "sha512-FCE5xLH+hjbzRdpbRb1IMCvPv9yZx2QnDarBEYSN0N0HYk+TcXsEhwdFcFb+SRWOKzKGErhIEbBK2ogyLdTtfQ==", + "requires": { + "colorette": "^1.2.2", + "nanoid": "^3.1.22", + "source-map": "^0.6.1" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + } + } + }, + "postcss-selector-matches": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-selector-matches/-/postcss-selector-matches-4.0.0.tgz", + "integrity": "sha512-LgsHwQR/EsRYSqlwdGzeaPKVT0Ml7LAT6E75T8W8xLJY62CE4S/l03BWIt3jT8Taq22kXP08s2SfTSzaraoPww==", + "requires": { + "balanced-match": "^1.0.0", + "postcss": "^7.0.2" + } + }, + "postcss-selector-not": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-selector-not/-/postcss-selector-not-4.0.1.tgz", + "integrity": "sha512-YolvBgInEK5/79C+bdFMyzqTg6pkYqDbzZIST/PDMqa/o3qtXenD05apBG2jLgT0/BQ77d4U2UK12jWpilqMAQ==", + "requires": { + "balanced-match": "^1.0.0", + "postcss": "^7.0.2" + } + }, + "postcss-selector-parser": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.5.tgz", + "integrity": "sha512-aFYPoYmXbZ1V6HZaSvat08M97A8HqO6Pjz+PiNpw/DhuRrC72XWAdp3hL6wusDCN31sSmcZyMGa2hZEuX+Xfhg==", + "requires": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + } + }, + "postcss-svgo": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/postcss-svgo/-/postcss-svgo-4.0.3.tgz", + "integrity": "sha512-NoRbrcMWTtUghzuKSoIm6XV+sJdvZ7GZSc3wdBN0W19FTtp2ko8NqLsgoh/m9CzNhU3KLPvQmjIwtaNFkaFTvw==", + "requires": { + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0", + "svgo": "^1.0.0" + }, + "dependencies": { + "postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" + } + } + }, + "postcss-unique-selectors": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-4.0.1.tgz", + "integrity": "sha512-+JanVaryLo9QwZjKrmJgkI4Fn8SBgRO6WXQBJi7KiAVPlmxikB5Jzc4EvXMT2H0/m0RjrVVm9rGNhZddm/8Spg==", + "requires": { + "alphanum-sort": "^1.0.0", + "postcss": "^7.0.0", + "uniqs": "^2.0.0" + } + }, + "postcss-value-parser": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.1.0.tgz", + "integrity": "sha512-97DXOFbQJhk71ne5/Mt6cOu6yxsSfM0QGQyl0L25Gca4yGWEGJaig7l7gbCX623VqTBNGLRLaVUCnNkcedlRSQ==" + }, + "postcss-values-parser": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/postcss-values-parser/-/postcss-values-parser-2.0.1.tgz", + "integrity": "sha512-2tLuBsA6P4rYTNKCXYG/71C7j1pU6pK503suYOmn4xYrQIzW+opD+7FAFNuGSdZC/3Qfy334QbeMu7MEb8gOxg==", + "requires": { + "flatten": "^1.0.2", + "indexes-of": "^1.0.1", + "uniq": "^1.0.1" + } + }, + "prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==" + }, + "prepend-http": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-1.0.4.tgz", + "integrity": "sha512-PhmXi5XmoyKw1Un4E+opM2KcsJInDvKyuOumcjjw3waw86ZNjHwVUOOWLc4bCzLdcKNaWBH9e99sbWzDQsVaYg==" + }, + "prettier": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.2.1.tgz", + "integrity": "sha512-PqyhM2yCjg/oKkFPtTGUojv7gnZAoG80ttl45O6x2Ug/rMJw4wcc9k6aaf2hibP7BGVCCM33gZoGjyvt9mm16Q==", + "dev": true + }, + "prettier-linter-helpers": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz", + "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==", + "dev": true, + "requires": { + "fast-diff": "^1.1.2" + } + }, + "pretty-bytes": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.6.0.tgz", + "integrity": "sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg==" + }, + "pretty-error": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/pretty-error/-/pretty-error-2.1.2.tgz", + "integrity": "sha512-EY5oDzmsX5wvuynAByrmY0P0hcp+QpnAKbJng2A2MPjVKXCxrDSUkzghVJ4ZGPIv+JC4gX8fPUWscC0RtjsWGw==", + "requires": { + "lodash": "^4.17.20", + "renderkid": "^2.0.4" + } + }, + "pretty-format": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-26.6.2.tgz", + "integrity": "sha512-7AeGuCYNGmycyQbCqd/3PWH4eOoX/OiCa0uphp57NVTeAGdJGaAliecxwBDHYQCIvrW7aDBZCYeNTP/WX69mkg==", + "requires": { + "@jest/types": "^26.6.2", + "ansi-regex": "^5.0.0", + "ansi-styles": "^4.0.0", + "react-is": "^17.0.1" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "requires": { + "color-convert": "^2.0.1" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "react-is": { + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", + "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==" + } + } + }, + "pretty-quick": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/pretty-quick/-/pretty-quick-3.1.0.tgz", + "integrity": "sha512-DtxIxksaUWCgPFN7E1ZZk4+Aav3CCuRdhrDSFZENb404sYMtuo9Zka823F+Mgeyt8Zt3bUiCjFzzWYE9LYqkmQ==", + "dev": true, + "requires": { + "chalk": "^3.0.0", + "execa": "^4.0.0", + "find-up": "^4.1.0", + "ignore": "^5.1.4", + "mri": "^1.1.5", + "multimatch": "^4.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", + "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "requires": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + } + }, + "execa": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-4.1.0.tgz", + "integrity": "sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA==", + "dev": true, + "requires": { + "cross-spawn": "^7.0.0", + "get-stream": "^5.0.0", + "human-signals": "^1.1.1", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.0", + "onetime": "^5.1.0", + "signal-exit": "^3.0.2", + "strip-final-newline": "^2.0.0" + } + }, + "get-stream": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "dev": true, + "requires": { + "pump": "^3.0.0" + } + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "is-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz", + "integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==", + "dev": true + }, + "npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "requires": { + "path-key": "^3.0.0" + } + }, + "path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true + }, + "shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "requires": { + "shebang-regex": "^3.0.0" + } + }, + "shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + } + } + }, + "printj": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/printj/-/printj-1.1.2.tgz", + "integrity": "sha512-zA2SmoLaxZyArQTOPj5LXecR+RagfPSU5Kw1qP+jkWeNlrq+eJZyY2oS68SU1Z/7/myXM4lo9716laOFAVStCQ==" + }, + "process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==" + }, + "process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" + }, + "progress": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==" + }, + "promise": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/promise/-/promise-8.1.0.tgz", + "integrity": "sha512-W04AqnILOL/sPRXziNicCjSNRruLAuIHEOVBazepu0545DDNGYHz7ar9ZgZ1fMU8/MA4mVxp5rkBWRi6OXIy3Q==", + "requires": { + "asap": "~2.0.6" + } + }, + "promise-inflight": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", + "integrity": "sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g==" + }, + "prompts": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.0.tgz", + "integrity": "sha512-awZAKrk3vN6CroQukBL+R9051a4R3zCZBlJm/HBfrSZ8iTpYix3VX1vU4mveiLpiwmOJT4wokTF9m6HUk4KqWQ==", + "requires": { + "kleur": "^3.0.3", + "sisteransi": "^1.0.5" + } + }, + "prop-types": { + "version": "15.7.2", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz", + "integrity": "sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==", + "requires": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.8.1" + } + }, + "prop-types-exact": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/prop-types-exact/-/prop-types-exact-1.2.0.tgz", + "integrity": "sha512-K+Tk3Kd9V0odiXFP9fwDHUYRyvK3Nun3GVyPapSIs5OBkITAm15W0CPFD/YKTkMUAbc0b9CUwRQp2ybiBIq+eA==", + "dev": true, + "requires": { + "has": "^1.0.3", + "object.assign": "^4.1.0", + "reflect.ownkeys": "^0.2.0" + } + }, + "proxy-addr": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz", + "integrity": "sha512-dh/frvCBVmSsDYzw6n926jv974gddhkFPfiN8hPOi30Wax25QZyZEGveluCgliBnqmuM+UJmBErbAUFIoDbjOw==", + "requires": { + "forwarded": "~0.1.2", + "ipaddr.js": "1.9.1" + } + }, + "prr": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", + "integrity": "sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw==" + }, + "pseudomap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", + "integrity": "sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ==", + "dev": true + }, + "psl": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz", + "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==" + }, + "public-encrypt": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz", + "integrity": "sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==", + "requires": { + "bn.js": "^4.1.0", + "browserify-rsa": "^4.0.0", + "create-hash": "^1.1.0", + "parse-asn1": "^5.0.0", + "randombytes": "^2.0.1", + "safe-buffer": "^5.1.2" + }, + "dependencies": { + "bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" + } + } + }, + "pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "pumpify": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/pumpify/-/pumpify-1.5.1.tgz", + "integrity": "sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ==", + "requires": { + "duplexify": "^3.6.0", + "inherits": "^2.0.3", + "pump": "^2.0.0" + }, + "dependencies": { + "pump": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pump/-/pump-2.0.1.tgz", + "integrity": "sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==", + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + } + } + }, + "punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" + }, + "q": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz", + "integrity": "sha512-kV/CThkXo6xyFEZUugw/+pIOywXcDbFYgSct5cT3gqlbkBE1SJdwy6UQoZvodiWF/ckQLZyDE/Bu1M6gVu5lVw==" + }, + "qs": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", + "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==" + }, + "query-string": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/query-string/-/query-string-4.3.4.tgz", + "integrity": "sha512-O2XLNDBIg1DnTOa+2XrIwSiXEV8h2KImXUnjhhn2+UsvZ+Es2uyd5CCRTNQlDGbzUQOW3aYCBx9rVA6dzsiY7Q==", + "requires": { + "object-assign": "^4.1.0", + "strict-uri-encode": "^1.0.0" + } + }, + "querystring": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.1.tgz", + "integrity": "sha512-wkvS7mL/JMugcup3/rMitHmd9ecIGd2lhFhK9N3UUQ450h66d1r3Y9nvXzQAW1Lq+wyx61k/1pfKS5KuKiyEbg==" + }, + "querystring-es3": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/querystring-es3/-/querystring-es3-0.2.1.tgz", + "integrity": "sha512-773xhDQnZBMFobEiztv8LIl70ch5MSF/jUQVlhwFyBILqq96anmoctVIYz+ZRp0qbCKATTn6ev02M3r7Ga5vqA==" + }, + "querystringify": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", + "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==" + }, + "queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==" + }, + "quick-lru": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-4.0.1.tgz", + "integrity": "sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g==", + "dev": true + }, + "raf": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/raf/-/raf-3.4.1.tgz", + "integrity": "sha512-Sq4CW4QhwOHE8ucn6J34MqtZCeWFP2aQSmrlroYgqAV1PjStIhJXxYuTgUIfkEk7zTLjmIjLmU5q+fbD1NnOJA==", + "requires": { + "performance-now": "^2.1.0" + } + }, + "railroad-diagrams": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/railroad-diagrams/-/railroad-diagrams-1.0.0.tgz", + "integrity": "sha512-cz93DjNeLY0idrCNOH6PviZGRN9GJhsdm9hpn1YCS879fj4W+x5IFJhhkRZcwVgMmFF7R82UA/7Oh+R8lLZg6A==", + "dev": true + }, + "randexp": { + "version": "0.4.6", + "resolved": "https://registry.npmjs.org/randexp/-/randexp-0.4.6.tgz", + "integrity": "sha512-80WNmd9DA0tmZrw9qQa62GPPWfuXJknrmVmLcxvq4uZBdYqb1wYoKTmnlGUchvVWe0XiLupYkBoXVOxz3C8DYQ==", + "dev": true, + "requires": { + "discontinuous-range": "1.0.0", + "ret": "~0.1.10" + } + }, + "randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "requires": { + "safe-buffer": "^5.1.0" + } + }, + "randomfill": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/randomfill/-/randomfill-1.0.4.tgz", + "integrity": "sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==", + "requires": { + "randombytes": "^2.0.5", + "safe-buffer": "^5.1.0" + } + }, + "range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==" + }, + "raw-body": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz", + "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==", + "requires": { + "bytes": "3.1.0", + "http-errors": "1.7.2", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + }, + "dependencies": { + "bytes": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", + "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==" + } + } + }, + "react": { + "version": "16.14.0", + "resolved": "https://registry.npmjs.org/react/-/react-16.14.0.tgz", + "integrity": "sha512-0X2CImDkJGApiAlcf0ODKIneSwBPhqJawOa5wCtKbu7ZECrmS26NvtSILynQ66cgkT/RJ4LidJOc3bUESwmU8g==", + "requires": { + "loose-envify": "^1.1.0", + "object-assign": "^4.1.1", + "prop-types": "^15.6.2" + } + }, + "react-accessible-accordion": { + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/react-accessible-accordion/-/react-accessible-accordion-3.3.4.tgz", + "integrity": "sha512-EUq+KmVRIIG5u1fR5XIbb2JU7w7NouLjuyfuPvnhuDIfNNWNYap1I8ijn2rdA6OQi1gGSRGbVzHs+3wxH/M0Sw==" + }, + "react-app-polyfill": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/react-app-polyfill/-/react-app-polyfill-1.0.6.tgz", + "integrity": "sha512-OfBnObtnGgLGfweORmdZbyEz+3dgVePQBb3zipiaDsMHV1NpWm0rDFYIVXFV/AK+x4VIIfWHhrdMIeoTLyRr2g==", + "requires": { + "core-js": "^3.5.0", + "object-assign": "^4.1.1", + "promise": "^8.0.3", + "raf": "^3.4.1", + "regenerator-runtime": "^0.13.3", + "whatwg-fetch": "^3.0.0" + } + }, + "react-datepicker": { + "version": "2.16.0", + "resolved": "https://registry.npmjs.org/react-datepicker/-/react-datepicker-2.16.0.tgz", + "integrity": "sha512-TvcmSY27rn0JKvuJuIXNNS+niGQNdgtuG/CsBttVYhPOA9KmSw7c2PvQBPVEvzkyV+QPNJ8jN/KVJNj9uvopqA==", + "requires": { + "classnames": "^2.2.6", + "date-fns": "^2.0.1", + "prop-types": "^15.7.2", + "react-onclickoutside": "^6.9.0", + "react-popper": "^1.3.4" + } + }, + "react-dev-utils": { + "version": "11.0.4", + "resolved": "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-11.0.4.tgz", + "integrity": "sha512-dx0LvIGHcOPtKbeiSUM4jqpBl3TcY7CDjZdfOIcKeznE7BWr9dg0iPG90G5yfVQ+p/rGNMXdbfStvzQZEVEi4A==", + "requires": { + "@babel/code-frame": "7.10.4", + "address": "1.1.2", + "browserslist": "4.14.2", + "chalk": "2.4.2", + "cross-spawn": "7.0.3", + "detect-port-alt": "1.1.6", + "escape-string-regexp": "2.0.0", + "filesize": "6.1.0", + "find-up": "4.1.0", + "fork-ts-checker-webpack-plugin": "4.1.6", + "global-modules": "2.0.0", + "globby": "11.0.1", + "gzip-size": "5.1.1", + "immer": "8.0.1", + "is-root": "2.1.0", + "loader-utils": "2.0.0", + "open": "^7.0.2", + "pkg-up": "3.1.0", + "prompts": "2.4.0", + "react-error-overlay": "^6.0.9", + "recursive-readdir": "2.2.2", + "shell-quote": "1.7.2", + "strip-ansi": "6.0.0", + "text-table": "0.2.0" + }, + "dependencies": { + "@babel/code-frame": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.4.tgz", + "integrity": "sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg==", + "requires": { + "@babel/highlight": "^7.10.4" + } + }, + "browserslist": { + "version": "4.14.2", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.14.2.tgz", + "integrity": "sha512-HI4lPveGKUR0x2StIz+2FXfDk9SfVMrxn6PLh1JeGUwcuoDkdKZebWiyLRJ68iIPDpMI4JLVDf7S7XzslgWOhw==", + "requires": { + "caniuse-lite": "^1.0.30001125", + "electron-to-chromium": "^1.3.564", + "escalade": "^3.0.2", + "node-releases": "^1.1.61" + } + }, + "cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "requires": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + } + }, + "escape-string-regexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==" + }, + "globby": { + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.0.1.tgz", + "integrity": "sha512-iH9RmgwCmUJHi2z5o2l3eTtGBtXek1OYlHrbcxOYugyHLmAsZrPj43OtHThd62Buh/Vv6VyCBD2bdyWcGNQqoQ==", + "requires": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.1.1", + "ignore": "^5.1.4", + "merge2": "^1.3.0", + "slash": "^3.0.0" + } + }, + "path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==" + }, + "shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "requires": { + "shebang-regex": "^3.0.0" + } + }, + "shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==" + }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "requires": { + "isexe": "^2.0.0" + } + } + } + }, + "react-dom": { + "version": "16.14.0", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.14.0.tgz", + "integrity": "sha512-1gCeQXDLoIqMgqD3IO2Ah9bnf0w9kzhwN5q4FGnHZ67hBm9yePzB5JJAIQCc8x3pFnNlwFq4RidZggNAAkzWWw==", + "requires": { + "loose-envify": "^1.1.0", + "object-assign": "^4.1.1", + "prop-types": "^15.6.2", + "scheduler": "^0.19.1" + } + }, + "react-error-overlay": { + "version": "6.0.9", + "resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.9.tgz", + "integrity": "sha512-nQTTcUu+ATDbrSD1BZHr5kgSD4oF8OFjxun8uAaL8RwPBacGBNPf/yAuVVdx17N8XNzRDMrZ9XcKZHCjPW+9ew==" + }, + "react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" + }, + "react-lifecycles-compat": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz", + "integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==" + }, + "react-multi-select-component": { + "version": "2.0.14", + "resolved": "https://registry.npmjs.org/react-multi-select-component/-/react-multi-select-component-2.0.14.tgz", + "integrity": "sha512-oqDsf/EGzOMUQ0rvGWYV/3wTbsXEteZP2C19nsksj1iUkCC7IhZWqaTlj5ChjzMVmTLyrQrtMYLmzFj3Meo15w==", + "requires": { + "@rooks/use-outside-click": "^3.6.0", + "goober": "^1.8.0" + } + }, + "react-onclickoutside": { + "version": "6.10.0", + "resolved": "https://registry.npmjs.org/react-onclickoutside/-/react-onclickoutside-6.10.0.tgz", + "integrity": "sha512-7i2L3ef+0ILXpL6P+Hg304eCQswh4jl3ynwR71BSlMU49PE2uk31k8B2GkP6yE9s2D4jTGKnzuSpzWxu4YxfQQ==" + }, + "react-popper": { + "version": "1.3.11", + "resolved": "https://registry.npmjs.org/react-popper/-/react-popper-1.3.11.tgz", + "integrity": "sha512-VSA/bS+pSndSF2fiasHK/PTEEAyOpX60+H5EPAjoArr8JGm+oihu4UbrqcEBpQibJxBVCpYyjAX7abJ+7DoYVg==", + "requires": { + "@babel/runtime": "^7.1.2", + "@hypnosphi/create-react-context": "^0.3.1", + "deep-equal": "^1.1.1", + "popper.js": "^1.14.4", + "prop-types": "^15.6.1", + "typed-styles": "^0.0.7", + "warning": "^4.0.2" + } + }, + "react-redux": { + "version": "7.2.4", + "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-7.2.4.tgz", + "integrity": "sha512-hOQ5eOSkEJEXdpIKbnRyl04LhaWabkDPV+Ix97wqQX3T3d2NQ8DUblNXXtNMavc7DpswyQM6xfaN4HQDKNY2JA==", + "requires": { + "@babel/runtime": "^7.12.1", + "@types/react-redux": "^7.1.16", + "hoist-non-react-statics": "^3.3.2", + "loose-envify": "^1.4.0", + "prop-types": "^15.7.2", + "react-is": "^16.13.1" + } + }, + "react-refresh": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.8.3.tgz", + "integrity": "sha512-X8jZHc7nCMjaCqoU+V2I0cOhNW+QMBwSUkeXnTi8IPe6zaRWfn60ZzvFDZqWPfmSJfjub7dDW1SP0jaHWLu/hg==" + }, + "react-router": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-5.2.0.tgz", + "integrity": "sha512-smz1DUuFHRKdcJC0jobGo8cVbhO3x50tCL4icacOlcwDOEQPq4TMqwx3sY1TP+DvtTgz4nm3thuo7A+BK2U0Dw==", + "requires": { + "@babel/runtime": "^7.1.2", + "history": "^4.9.0", + "hoist-non-react-statics": "^3.1.0", + "loose-envify": "^1.3.1", + "mini-create-react-context": "^0.4.0", + "path-to-regexp": "^1.7.0", + "prop-types": "^15.6.2", + "react-is": "^16.6.0", + "tiny-invariant": "^1.0.2", + "tiny-warning": "^1.0.0" + } + }, + "react-router-dom": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-5.2.0.tgz", + "integrity": "sha512-gxAmfylo2QUjcwxI63RhQ5G85Qqt4voZpUXSEqCwykV0baaOTQDR1f0PmY8AELqIyVc0NEZUj0Gov5lNGcXgsA==", + "requires": { + "@babel/runtime": "^7.1.2", + "history": "^4.9.0", + "loose-envify": "^1.3.1", + "prop-types": "^15.6.2", + "react-router": "5.2.0", + "tiny-invariant": "^1.0.2", + "tiny-warning": "^1.0.0" + } + }, + "react-scripts": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/react-scripts/-/react-scripts-4.0.3.tgz", + "integrity": "sha512-S5eO4vjUzUisvkIPB7jVsKtuH2HhWcASREYWHAQ1FP5HyCv3xgn+wpILAEWkmy+A+tTNbSZClhxjT3qz6g4L1A==", + "requires": { + "@babel/core": "7.12.3", + "@pmmmwh/react-refresh-webpack-plugin": "0.4.3", + "@svgr/webpack": "5.5.0", + "@typescript-eslint/eslint-plugin": "^4.5.0", + "@typescript-eslint/parser": "^4.5.0", + "babel-eslint": "^10.1.0", + "babel-jest": "^26.6.0", + "babel-loader": "8.1.0", + "babel-plugin-named-asset-import": "^0.3.7", + "babel-preset-react-app": "^10.0.0", + "bfj": "^7.0.2", + "camelcase": "^6.1.0", + "case-sensitive-paths-webpack-plugin": "2.3.0", + "css-loader": "4.3.0", + "dotenv": "8.2.0", + "dotenv-expand": "5.1.0", + "eslint": "^7.11.0", + "eslint-config-react-app": "^6.0.0", + "eslint-plugin-flowtype": "^5.2.0", + "eslint-plugin-import": "^2.22.1", + "eslint-plugin-jest": "^24.1.0", + "eslint-plugin-jsx-a11y": "^6.3.1", + "eslint-plugin-react": "^7.21.5", + "eslint-plugin-react-hooks": "^4.2.0", + "eslint-plugin-testing-library": "^3.9.2", + "eslint-webpack-plugin": "^2.5.2", + "file-loader": "6.1.1", + "fs-extra": "^9.0.1", + "fsevents": "^2.1.3", + "html-webpack-plugin": "4.5.0", + "identity-obj-proxy": "3.0.0", + "jest": "26.6.0", + "jest-circus": "26.6.0", + "jest-resolve": "26.6.0", + "jest-watch-typeahead": "0.6.1", + "mini-css-extract-plugin": "0.11.3", + "optimize-css-assets-webpack-plugin": "5.0.4", + "pnp-webpack-plugin": "1.6.4", + "postcss-flexbugs-fixes": "4.2.1", + "postcss-loader": "3.0.0", + "postcss-normalize": "8.0.1", + "postcss-preset-env": "6.7.0", + "postcss-safe-parser": "5.0.2", + "prompts": "2.4.0", + "react-app-polyfill": "^2.0.0", + "react-dev-utils": "^11.0.3", + "react-refresh": "^0.8.3", + "resolve": "1.18.1", + "resolve-url-loader": "^3.1.2", + "sass-loader": "^10.0.5", + "semver": "7.3.2", + "style-loader": "1.3.0", + "terser-webpack-plugin": "4.2.3", + "ts-pnp": "1.2.0", + "url-loader": "4.1.1", + "webpack": "4.44.2", + "webpack-dev-server": "3.11.1", + "webpack-manifest-plugin": "2.2.0", + "workbox-webpack-plugin": "5.1.4" + }, + "dependencies": { + "react-app-polyfill": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/react-app-polyfill/-/react-app-polyfill-2.0.0.tgz", + "integrity": "sha512-0sF4ny9v/B7s6aoehwze9vJNWcmCemAUYBVasscVr92+UYiEqDXOxfKjXN685mDaMRNF3WdhHQs76oTODMocFA==", + "requires": { + "core-js": "^3.6.5", + "object-assign": "^4.1.1", + "promise": "^8.1.0", + "raf": "^3.4.1", + "regenerator-runtime": "^0.13.7", + "whatwg-fetch": "^3.4.1" + } + } + } + }, + "react-select": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/react-select/-/react-select-5.2.1.tgz", + "integrity": "sha512-OOyNzfKrhOcw/BlembyGWgdlJ2ObZRaqmQppPFut1RptJO423j+Y+JIsmxkvsZ4D/3CpOmwIlCvWbbAWEdh12A==", + "requires": { + "@babel/runtime": "^7.12.0", + "@emotion/cache": "^11.4.0", + "@emotion/react": "^11.1.1", + "@types/react-transition-group": "^4.4.0", + "memoize-one": "^5.0.0", + "prop-types": "^15.6.0", + "react-transition-group": "^4.3.0" + } + }, + "react-test-renderer": { + "version": "16.14.0", + "resolved": "https://registry.npmjs.org/react-test-renderer/-/react-test-renderer-16.14.0.tgz", + "integrity": "sha512-L8yPjqPE5CZO6rKsKXRO/rVPiaCOy0tQQJbC+UjPNlobl5mad59lvPjwFsQHTvL03caVDIVr9x9/OSgDe6I5Eg==", + "dev": true, + "requires": { + "object-assign": "^4.1.1", + "prop-types": "^15.6.2", + "react-is": "^16.8.6", + "scheduler": "^0.19.1" + } + }, + "react-toastify": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/react-toastify/-/react-toastify-6.2.0.tgz", + "integrity": "sha512-XpjFrcBhQ0/nBOL4syqgP/TywFnOyxmstYLWgSQWcj39qpp+WU4vPt3C/ayIDx7RFyxRWfzWTdR2qOcDGo7G0w==", + "requires": { + "clsx": "^1.1.1", + "prop-types": "^15.7.2", + "react-transition-group": "^4.4.1" + } + }, + "react-toggle": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/react-toggle/-/react-toggle-4.1.2.tgz", + "integrity": "sha512-4Ohw31TuYQdhWfA6qlKafeXx3IOH7t4ZHhmRdwsm1fQREwOBGxJT+I22sgHqR/w8JRdk+AeMCJXPImEFSrNXow==", + "requires": { + "classnames": "^2.2.5" + } + }, + "react-transition-group": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.1.tgz", + "integrity": "sha512-Djqr7OQ2aPUiYurhPalTrVy9ddmFCCzwhqQmtN+J3+3DzLO209Fdr70QrN8Z3DsglWql6iY1lDWAfpFiBtuKGw==", + "requires": { + "@babel/runtime": "^7.5.5", + "dom-helpers": "^5.0.1", + "loose-envify": "^1.4.0", + "prop-types": "^15.6.2" + } + }, + "reactstrap": { + "version": "8.9.0", + "resolved": "https://registry.npmjs.org/reactstrap/-/reactstrap-8.9.0.tgz", + "integrity": "sha512-pmf33YjpNZk1IfrjqpWCUMq9hk6GzSnMWBAofTBNIRJQB1zQ0Au2kzv3lPUAFsBYgWEuI9iYa/xKXHaboSiMkQ==", + "requires": { + "@babel/runtime": "^7.12.5", + "classnames": "^2.2.3", + "prop-types": "^15.5.8", + "react-popper": "^1.3.6", + "react-transition-group": "^2.3.1" + }, + "dependencies": { + "dom-helpers": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-3.4.0.tgz", + "integrity": "sha512-LnuPJ+dwqKDIyotW1VzmOZ5TONUN7CwkCR5hrgawTUbkBGYdeoNLZo6nNfGkCrjtE1nXXaj7iMMpDa8/d9WoIA==", + "requires": { + "@babel/runtime": "^7.1.2" + } + }, + "react-transition-group": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-2.9.0.tgz", + "integrity": "sha512-+HzNTCHpeQyl4MJ/bdE0u6XRMe9+XG/+aL4mCxVN4DnPBQ0/5bfHWPDuOZUzYdMj94daZaZdCCc1Dzt9R/xSSg==", + "requires": { + "dom-helpers": "^3.4.0", + "loose-envify": "^1.4.0", + "prop-types": "^15.6.2", + "react-lifecycles-compat": "^3.0.4" + } + } + } + }, + "read-pkg": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz", + "integrity": "sha512-eFIBOPW7FGjzBuk3hdXEuNSiTZS/xEMlH49HxMyzb0hyPfu4EhVjT2DH32K1hSSmVq4sebAWnZuuY5auISUTGA==", + "requires": { + "load-json-file": "^2.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^2.0.0" + }, + "dependencies": { + "path-type": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-2.0.0.tgz", + "integrity": "sha512-dUnb5dXUf+kzhC/W/F4e5/SkluXIFf5VUHolW1Eg1irn1hGWjPGdsRcvYJ1nD6lhk8Ir7VM0bHJKsYTx8Jx9OQ==", + "requires": { + "pify": "^2.0.0" + } + }, + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=" + } + } + }, + "read-pkg-up": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-2.0.0.tgz", + "integrity": "sha512-1orxQfbWGUiTn9XsPlChs6rLie/AV9jwZTGmu2NZw/CUDJQchXJFYE0Fq5j7+n558T1JhDWLdhyd1Zj+wLY//w==", + "requires": { + "find-up": "^2.0.0", + "read-pkg": "^2.0.0" + }, + "dependencies": { + "find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha512-NWzkk0jSJtTt08+FBFMvXoeZnOJD+jTtsRmBYbAIzJdX6l7dLgR7CTubCM5/eDdPUBvLCeVasP1brfVR/9/EZQ==", + "requires": { + "locate-path": "^2.0.0" + } + }, + "locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha512-NCI2kiDkyR7VeEKm27Kda/iQHyKJe1Bu0FlTbYp3CqJu+9IFe9bLyAjMxf5ZDDbEg+iMPzB5zYyUTSm8wVTKmA==", + "requires": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + } + }, + "p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "requires": { + "p-try": "^1.0.0" + } + }, + "p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha512-nQja7m7gSKuewoVRen45CtVfODR3crN3goVQ0DDZ9N3yHxgpkuBhZqsaiotSQRrADUrne346peY7kT3TSACykg==", + "requires": { + "p-limit": "^1.1.0" + } + }, + "p-try": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "integrity": "sha512-U1etNYuMJoIz3ZXSrrySFjsXQTWOx2/jdi86L+2pRvph/qMKL6sbcCYdH23fqsbm8TH2Gn0OybpT4eSFlCVHww==" + }, + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==" + } + } + }, + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "readdirp": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.5.0.tgz", + "integrity": "sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ==", + "optional": true, + "requires": { + "picomatch": "^2.2.1" + } + }, + "rechoir": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz", + "integrity": "sha1-hSBLVNuoLVdC4oyWdW70OvUOM4Q=", + "dev": true, + "requires": { + "resolve": "^1.1.6" + } + }, + "recursive-readdir": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/recursive-readdir/-/recursive-readdir-2.2.2.tgz", + "integrity": "sha512-nRCcW9Sj7NuZwa2XvH9co8NPeXUBhZP7CRKJtU+cS6PW9FpCIFoI5ib0NT1ZrbNuPoRy0ylyCaUL8Gih4LSyFg==", + "requires": { + "minimatch": "3.0.4" + } + }, + "redent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", + "integrity": "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==", + "dev": true, + "requires": { + "indent-string": "^4.0.0", + "strip-indent": "^3.0.0" + }, + "dependencies": { + "indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "dev": true + } + } + }, + "redux": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/redux/-/redux-4.1.0.tgz", + "integrity": "sha512-uI2dQN43zqLWCt6B/BMGRMY6db7TTY4qeHHfGeKb3EOhmOKjU3KdWvNLJyqaHRksv/ErdNH7cFZWg9jXtewy4g==", + "requires": { + "@babel/runtime": "^7.9.2" + } + }, + "redux-devtools-extension": { + "version": "2.13.9", + "resolved": "https://registry.npmjs.org/redux-devtools-extension/-/redux-devtools-extension-2.13.9.tgz", + "integrity": "sha512-cNJ8Q/EtjhQaZ71c8I9+BPySIBVEKssbPpskBfsXqb8HJ002A3KRVHfeRzwRo6mGPqsm7XuHTqNSNeS1Khig0A==", + "dev": true + }, + "redux-thunk": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-2.3.0.tgz", + "integrity": "sha512-km6dclyFnmcvxhAcrQV2AkZmPQjzPDjgVlQtR0EQjxZPyJ0BnMf3in1ryuR8A2qU0HldVRfxYXbFSKlI3N7Slw==" + }, + "reflect.ownkeys": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/reflect.ownkeys/-/reflect.ownkeys-0.2.0.tgz", + "integrity": "sha512-qOLsBKHCpSOFKK1NUOCGC5VyeufB6lEsFe92AL2bhIJsacZS1qdoOZSbPk3MYKuT2cFlRDnulKXuuElIrMjGUg==", + "dev": true + }, + "regenerate": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", + "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==" + }, + "regenerate-unicode-properties": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-8.2.0.tgz", + "integrity": "sha512-F9DjY1vKLo/tPePDycuH3dn9H1OTPIkVD9Kz4LODu+F2C75mgjAJ7x/gwy6ZcSNRAAkhNlJSOHRe8k3p+K9WhA==", + "requires": { + "regenerate": "^1.4.0" + } + }, + "regenerator-runtime": { + "version": "0.13.7", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz", + "integrity": "sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==" + }, + "regenerator-transform": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.14.5.tgz", + "integrity": "sha512-eOf6vka5IO151Jfsw2NO9WpGX58W6wWmefK3I1zEGr0lOD0u8rwPaNqQL1aRxUaxLeKO3ArNh3VYg1KbaD+FFw==", + "requires": { + "@babel/runtime": "^7.8.4" + } + }, + "regex-not": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", + "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==", + "requires": { + "extend-shallow": "^3.0.2", + "safe-regex": "^1.1.0" + } + }, + "regex-parser": { + "version": "2.2.11", + "resolved": "https://registry.npmjs.org/regex-parser/-/regex-parser-2.2.11.tgz", + "integrity": "sha512-jbD/FT0+9MBU2XAZluI7w2OBs1RBi6p9M83nkoZayQXXU9e8Robt69FcZc7wU4eJD/YFTjn1JdCk3rbMJajz8Q==" + }, + "regexp.prototype.flags": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.3.1.tgz", + "integrity": "sha512-JiBdRBq91WlY7uRJ0ds7R+dU02i6LKi8r3BuQhNXn+kmeLN+EfHhfjqMRis1zJxnlu88hq/4dx0P2OP3APRTOA==", + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3" + } + }, + "regexpp": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.1.0.tgz", + "integrity": "sha512-ZOIzd8yVsQQA7j8GCSlPGXwg5PfmA1mrq0JP4nGhh54LaKN3xdai/vHUDu74pKwV8OxseMS65u2NImosQcSD0Q==" + }, + "regexpu-core": { + "version": "4.7.1", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-4.7.1.tgz", + "integrity": "sha512-ywH2VUraA44DZQuRKzARmw6S66mr48pQVva4LBeRhcOltJ6hExvWly5ZjFLYo67xbIxb6W1q4bAGtgfEl20zfQ==", + "requires": { + "regenerate": "^1.4.0", + "regenerate-unicode-properties": "^8.2.0", + "regjsgen": "^0.5.1", + "regjsparser": "^0.6.4", + "unicode-match-property-ecmascript": "^1.0.4", + "unicode-match-property-value-ecmascript": "^1.2.0" + } + }, + "regjsgen": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.5.2.tgz", + "integrity": "sha512-OFFT3MfrH90xIW8OOSyUrk6QHD5E9JOTeGodiJeBS3J6IwlgzJMNE/1bZklWz5oTg+9dCMyEetclvCVXOPoN3A==" + }, + "regjsparser": { + "version": "0.6.9", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.6.9.tgz", + "integrity": "sha512-ZqbNRz1SNjLAiYuwY0zoXW8Ne675IX5q+YHioAGbCw4X96Mjl2+dcX9B2ciaeyYjViDAfvIjFpQjJgLttTEERQ==", + "requires": { + "jsesc": "~0.5.0" + }, + "dependencies": { + "jsesc": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", + "integrity": "sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=" + } + } + }, + "relateurl": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz", + "integrity": "sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog==" + }, + "remove-trailing-separator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", + "integrity": "sha512-/hS+Y0u3aOfIETiaiirUFwDBDzmXPvO+jAfKTitUngIPzdKc6Z0LoFjM/CK5PL4C+eKwHohlHAb6H0VFfmmUsw==" + }, + "renderkid": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/renderkid/-/renderkid-2.0.5.tgz", + "integrity": "sha512-ccqoLg+HLOHq1vdfYNm4TBeaCDIi1FLt3wGojTDSvdewUv65oTmI3cnT2E4hRjl1gzKZIPK+KZrXzlUYKnR+vQ==", + "requires": { + "css-select": "^2.0.2", + "dom-converter": "^0.2", + "htmlparser2": "^3.10.1", + "lodash": "^4.17.20", + "strip-ansi": "^3.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "requires": { + "ansi-regex": "^2.0.0" + } + } + } + }, + "repeat-element": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.4.tgz", + "integrity": "sha512-LFiNfRcSu7KK3evMyYOuCzv3L10TW7yC1G2/+StMjK8Y6Vqd2MG7r/Qjw4ghtuCOjFvlnms/iMmLqpvW/ES/WQ==" + }, + "repeat-string": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", + "integrity": "sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w==" + }, + "repeating": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz", + "integrity": "sha512-ZqtSMuVybkISo2OWvqvm7iHSWngvdaW3IpsT9/uP8v4gMi591LY6h35wdOfvQdWCKFWZWm2Y1Opp4kV7vQKT6A==", + "dev": true, + "requires": { + "is-finite": "^1.0.0" + } + }, + "request": { + "version": "2.88.2", + "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", + "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", + "requires": { + "aws-sign2": "~0.7.0", + "aws4": "^1.8.0", + "caseless": "~0.12.0", + "combined-stream": "~1.0.6", + "extend": "~3.0.2", + "forever-agent": "~0.6.1", + "form-data": "~2.3.2", + "har-validator": "~5.1.3", + "http-signature": "~1.2.0", + "is-typedarray": "~1.0.0", + "isstream": "~0.1.2", + "json-stringify-safe": "~5.0.1", + "mime-types": "~2.1.19", + "oauth-sign": "~0.9.0", + "performance-now": "^2.1.0", + "qs": "~6.5.2", + "safe-buffer": "^5.1.2", + "tough-cookie": "~2.5.0", + "tunnel-agent": "^0.6.0", + "uuid": "^3.3.2" + }, + "dependencies": { + "tough-cookie": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", + "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", + "requires": { + "psl": "^1.1.28", + "punycode": "^2.1.1" + } + }, + "uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==" + } + } + }, + "request-promise-core": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/request-promise-core/-/request-promise-core-1.1.4.tgz", + "integrity": "sha512-TTbAfBBRdWD7aNNOoVOBH4pN/KigV6LyapYNNlAPA8JwbovRti1E88m3sYAwsLi5ryhPKsE9APwnjFTgdUjTpw==", + "requires": { + "lodash": "^4.17.19" + } + }, + "request-promise-native": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/request-promise-native/-/request-promise-native-1.0.9.tgz", + "integrity": "sha512-wcW+sIUiWnKgNY0dqCpOZkUbF/I+YPi+f09JZIDa39Ec+q82CpSYniDp+ISgTTbKmnpJWASeJBPZmoxH84wt3g==", + "requires": { + "request-promise-core": "1.1.4", + "stealthy-require": "^1.1.1", + "tough-cookie": "^2.3.3" + }, + "dependencies": { + "tough-cookie": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", + "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", + "requires": { + "psl": "^1.1.28", + "punycode": "^2.1.1" + } + } + } + }, + "require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==" + }, + "require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==" + }, + "require-main-filename": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==" + }, + "requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==" + }, + "reselect": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/reselect/-/reselect-4.0.0.tgz", + "integrity": "sha512-qUgANli03jjAyGlnbYVAV5vvnOmJnODyABz51RdBN7M4WaVu8mecZWgyQNkG8Yqe3KRGRt0l4K4B3XVEULC4CA==" + }, + "resolve": { + "version": "1.18.1", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.18.1.tgz", + "integrity": "sha512-lDfCPaMKfOJXjy0dPayzPdF1phampNWr3qFCjAu+rw/qbQmr5jWH5xN2hwh9QKfw9E5v4hwV7A+jrCmL8yjjqA==", + "requires": { + "is-core-module": "^2.0.0", + "path-parse": "^1.0.6" + } + }, + "resolve-cwd": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", + "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", + "requires": { + "resolve-from": "^5.0.0" + }, + "dependencies": { + "resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==" + } + } + }, + "resolve-dir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/resolve-dir/-/resolve-dir-1.0.1.tgz", + "integrity": "sha512-R7uiTjECzvOsWSfdM0QKFNBVFcK27aHOUwdvK53BcW8zqnGdYp0Fbj82cy54+2A4P2tFM22J5kRfe1R+lM/1yg==", + "dev": true, + "requires": { + "expand-tilde": "^2.0.0", + "global-modules": "^1.0.0" + }, + "dependencies": { + "global-modules": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-1.0.0.tgz", + "integrity": "sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg==", + "dev": true, + "requires": { + "global-prefix": "^1.0.1", + "is-windows": "^1.0.1", + "resolve-dir": "^1.0.0" + } + }, + "global-prefix": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-1.0.2.tgz", + "integrity": "sha512-5lsx1NUDHtSjfg0eHlmYvZKv8/nVqX4ckFbM+FrGcQ+04KWcWFo9P5MxPZYSzUvyzmdTbI7Eix8Q4IbELDqzKg==", + "dev": true, + "requires": { + "expand-tilde": "^2.0.2", + "homedir-polyfill": "^1.0.1", + "ini": "^1.3.4", + "is-windows": "^1.0.1", + "which": "^1.2.14" + } + } + } + }, + "resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==" + }, + "resolve-global": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/resolve-global/-/resolve-global-1.0.0.tgz", + "integrity": "sha512-zFa12V4OLtT5XUX/Q4VLvTfBf+Ok0SPc1FNGM/z9ctUdiU618qwKpWnd0CHs3+RqROfyEg/DhuHbMWYqcgljEw==", + "dev": true, + "optional": true, + "requires": { + "global-dirs": "^0.1.1" + } + }, + "resolve-pathname": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-pathname/-/resolve-pathname-3.0.0.tgz", + "integrity": "sha512-C7rARubxI8bXFNB/hqcp/4iUeIXJhJZvFPFPiSPRnhU5UPxzMFIl+2E6yY6c4k9giDJAhtV+enfA+G89N6Csng==" + }, + "resolve-url": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", + "integrity": "sha512-ZuF55hVUQaaczgOIwqWzkEcEidmlD/xl44x1UZnhOXcYuFN2S6+rcxpG+C1N3So0wvNI3DmJICUFfu2SxhBmvg==" + }, + "resolve-url-loader": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/resolve-url-loader/-/resolve-url-loader-3.1.2.tgz", + "integrity": "sha512-QEb4A76c8Mi7I3xNKXlRKQSlLBwjUV/ULFMP+G7n3/7tJZ8MG5wsZ3ucxP1Jz8Vevn6fnJsxDx9cIls+utGzPQ==", + "requires": { + "adjust-sourcemap-loader": "3.0.0", + "camelcase": "5.3.1", + "compose-function": "3.0.3", + "convert-source-map": "1.7.0", + "es6-iterator": "2.0.3", + "loader-utils": "1.2.3", + "postcss": "7.0.21", + "rework": "1.0.1", + "rework-visit": "1.0.0", + "source-map": "0.6.1" + }, + "dependencies": { + "camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==" + }, + "emojis-list": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-2.1.0.tgz", + "integrity": "sha1-TapNnbAPmBmIDHn6RXrlsJof04k=" + }, + "json5": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", + "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", + "requires": { + "minimist": "^1.2.0" + } + }, + "loader-utils": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.2.3.tgz", + "integrity": "sha512-fkpz8ejdnEMG3s37wGL07iSBDg99O9D5yflE9RGNH3hRdx9SOwYfnGYdZOUIZitN8E+E2vkq3MUMYMvPYl5ZZA==", + "requires": { + "big.js": "^5.2.2", + "emojis-list": "^2.0.0", + "json5": "^1.0.1" + } + }, + "postcss": { + "version": "7.0.21", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.21.tgz", + "integrity": "sha512-uIFtJElxJo29QC753JzhidoAhvp/e/Exezkdhfmt8AymWT6/5B7W1WmponYWkHk2eg6sONyTch0A3nkMPun3SQ==", + "requires": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "restore-cursor": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz", + "integrity": "sha512-6IzJLuGi4+R14vwagDHX+JrXmPVtPpn4mffDJ1UdR7/Edm87fl6yi8mMBIVvFtJaNTUvjughmW4hwLhRG7gC1Q==", + "dev": true, + "requires": { + "onetime": "^2.0.0", + "signal-exit": "^3.0.2" + }, + "dependencies": { + "mimic-fn": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", + "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==", + "dev": true + }, + "onetime": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz", + "integrity": "sha512-oyyPpiMaKARvvcgip+JV+7zci5L8D1W9RZIz2l1o08AM3pfspitVWnPt3mzHcBPp12oYMTy0pqrFs/C+m3EwsQ==", + "dev": true, + "requires": { + "mimic-fn": "^1.0.0" + } + } + } + }, + "ret": { + "version": "0.1.15", + "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", + "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==" + }, + "retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", + "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==" + }, + "reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==" + }, + "rework": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/rework/-/rework-1.0.1.tgz", + "integrity": "sha512-eEjL8FdkdsxApd0yWVZgBGzfCQiT8yqSc2H1p4jpZpQdtz7ohETiDMoje5PlM8I9WgkqkreVxFUKYOiJdVWDXw==", + "requires": { + "convert-source-map": "^0.3.3", + "css": "^2.0.0" + }, + "dependencies": { + "convert-source-map": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-0.3.5.tgz", + "integrity": "sha512-+4nRk0k3oEpwUB7/CalD7xE2z4VmtEnnq0GO2IPTkrooTrAhEsWvuLF5iWP1dXrwluki/azwXV1ve7gtYuPldg==" + } + } + }, + "rework-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/rework-visit/-/rework-visit-1.0.0.tgz", + "integrity": "sha512-W6V2fix7nCLUYX1v6eGPrBOZlc03/faqzP4sUxMAJMBMOPYhfV/RyLegTufn5gJKaOITyi+gvf0LXDZ9NzkHnQ==" + }, + "rgb-regex": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/rgb-regex/-/rgb-regex-1.0.1.tgz", + "integrity": "sha512-gDK5mkALDFER2YLqH6imYvK6g02gpNGM4ILDZ472EwWfXZnC2ZEpoB2ECXTyOVUKuk/bPJZMzwQPBYICzP+D3w==" + }, + "rgba-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/rgba-regex/-/rgba-regex-1.0.0.tgz", + "integrity": "sha512-zgn5OjNQXLUTdq8m17KdaicF6w89TZs8ZU8y0AYENIU6wG8GG6LLm0yLSiPY8DmaYmHdgRW8rnApjoT0fQRfMg==" + }, + "right-pad": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/right-pad/-/right-pad-1.0.1.tgz", + "integrity": "sha512-bYBjgxmkvTAfgIYy328fmkwhp39v8lwVgWhhrzxPV3yHtcSqyYKe9/XOhvW48UFjATg3VuJbpsp5822ACNvkmw==", + "dev": true + }, + "rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "requires": { + "glob": "^7.1.3" + } + }, + "ripemd160": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz", + "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==", + "requires": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1" + } + }, + "rollup": { + "version": "1.32.1", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-1.32.1.tgz", + "integrity": "sha512-/2HA0Ec70TvQnXdzynFffkjA6XN+1e2pEv/uKS5Ulca40g2L7KuOE3riasHoNVHOsFD5KKZgDsMk1CP3Tw9s+A==", + "requires": { + "@types/estree": "*", + "@types/node": "*", + "acorn": "^7.1.0" + } + }, + "rollup-plugin-babel": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/rollup-plugin-babel/-/rollup-plugin-babel-4.4.0.tgz", + "integrity": "sha512-Lek/TYp1+7g7I+uMfJnnSJ7YWoD58ajo6Oarhlex7lvUce+RCKRuGRSgztDO3/MF/PuGKmUL5iTHKf208UNszw==", + "requires": { + "@babel/helper-module-imports": "^7.0.0", + "rollup-pluginutils": "^2.8.1" + } + }, + "rollup-plugin-terser": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/rollup-plugin-terser/-/rollup-plugin-terser-5.3.1.tgz", + "integrity": "sha512-1pkwkervMJQGFYvM9nscrUoncPwiKR/K+bHdjv6PFgRo3cgPHoRT83y2Aa3GvINj4539S15t/tpFPb775TDs6w==", + "requires": { + "@babel/code-frame": "^7.5.5", + "jest-worker": "^24.9.0", + "rollup-pluginutils": "^2.8.2", + "serialize-javascript": "^4.0.0", + "terser": "^4.6.2" + }, + "dependencies": { + "jest-worker": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-24.9.0.tgz", + "integrity": "sha512-51PE4haMSXcHohnSMdM42anbvZANYTqMrr52tVKPqqsPJMzoP6FYYDVqahX/HrAoKEKz3uUPzSvKs9A3qR4iVw==", + "requires": { + "merge-stream": "^2.0.0", + "supports-color": "^6.1.0" + } + }, + "serialize-javascript": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-4.0.0.tgz", + "integrity": "sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw==", + "requires": { + "randombytes": "^2.1.0" + } + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "rollup-pluginutils": { + "version": "2.8.2", + "resolved": "https://registry.npmjs.org/rollup-pluginutils/-/rollup-pluginutils-2.8.2.tgz", + "integrity": "sha512-EEp9NhnUkwY8aif6bxgovPHMoMoNr2FulJziTndpt5H9RdwC47GSGuII9XxpSdzVGM0GWrNPHV6ie1LTNJPaLQ==", + "requires": { + "estree-walker": "^0.6.1" + }, + "dependencies": { + "estree-walker": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-0.6.1.tgz", + "integrity": "sha512-SqmZANLWS0mnatqbSfRP5g8OXZC12Fgg1IwNtLsyHDzJizORW4khDfjPqJZsemPWBB2uqykUah5YpQ6epsqC/w==" + } + } + }, + "rst-selector-parser": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/rst-selector-parser/-/rst-selector-parser-2.2.3.tgz", + "integrity": "sha512-nDG1rZeP6oFTLN6yNDV/uiAvs1+FS/KlrEwh7+y7dpuApDBy6bI2HTBcc0/V8lv9OTqfyD34eF7au2pm8aBbhA==", + "dev": true, + "requires": { + "lodash.flattendeep": "^4.4.0", + "nearley": "^2.7.10" + } + }, + "rsvp": { + "version": "4.8.5", + "resolved": "https://registry.npmjs.org/rsvp/-/rsvp-4.8.5.tgz", + "integrity": "sha512-nfMOlASu9OnRJo1mbEk2cz0D56a1MBNrJ7orjRZQG10XDyuvwksKbuXNp6qa+kbn839HwjwhBzhFmdsaEAfauA==" + }, + "run-async": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", + "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==", + "dev": true + }, + "run-node": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/run-node/-/run-node-1.0.0.tgz", + "integrity": "sha512-kc120TBlQ3mih1LSzdAJXo4xn/GWS2ec0l3S+syHDXP9uRr0JAT8Qd3mdMuyjqCzeZktgP3try92cEgf9Nks8A==", + "dev": true + }, + "run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "requires": { + "queue-microtask": "^1.2.2" + } + }, + "run-queue": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/run-queue/-/run-queue-1.0.3.tgz", + "integrity": "sha512-ntymy489o0/QQplUDnpYAYUsO50K9SBrIVaKCWDOJzYJts0f9WH9RFJkyagebkw5+y1oi00R7ynNW/d12GBumg==", + "requires": { + "aproba": "^1.1.1" + } + }, + "rxjs": { + "version": "6.6.7", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", + "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", + "dev": true, + "requires": { + "tslib": "^1.9.0" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "safe-regex": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", + "integrity": "sha512-aJXcif4xnaNUzvUuC5gcb46oTS7zvg4jpMTnuqtrEPlR3vFr4pxtdTwaF1Qs3Enjn9HK+ZlwQui+a7z0SywIzg==", + "requires": { + "ret": "~0.1.10" + } + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "sane": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/sane/-/sane-4.1.0.tgz", + "integrity": "sha512-hhbzAgTIX8O7SHfp2c8/kREfEn4qO/9q8C9beyY6+tvZ87EpoZ3i1RIEvp27YBswnNbY9mWd6paKVmKbAgLfZA==", + "requires": { + "@cnakazawa/watch": "^1.0.3", + "anymatch": "^2.0.0", + "capture-exit": "^2.0.0", + "exec-sh": "^0.3.2", + "execa": "^1.0.0", + "fb-watchman": "^2.0.0", + "micromatch": "^3.1.4", + "minimist": "^1.1.1", + "walker": "~1.0.5" + }, + "dependencies": { + "anymatch": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", + "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", + "requires": { + "micromatch": "^3.1.4", + "normalize-path": "^2.1.1" + } + }, + "braces": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "requires": { + "arr-flatten": "^1.1.0", + "array-unique": "^0.3.2", + "extend-shallow": "^2.0.1", + "fill-range": "^4.0.0", + "isobject": "^3.0.1", + "repeat-element": "^1.1.2", + "snapdragon": "^0.8.1", + "snapdragon-node": "^2.0.1", + "split-string": "^3.0.2", + "to-regex": "^3.0.1" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha512-VcpLTWqWDiTerugjj8e3+esbg+skS3M9e54UuR3iCeIDMXCLTsAH8hTSzDQU/X6/6t3eYkOKoZSef2PlU6U1XQ==", + "requires": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha512-4cboCqIpliH+mAvFNegjZQ4kgKc3ZUhQVr3HvWbSh5q3WH2v82ct+T2Y1hdU5Gdtorx/cLifQjqCbL7bpznLTg==", + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", + "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "requires": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "braces": "^2.3.1", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "extglob": "^2.0.4", + "fragment-cache": "^0.2.1", + "kind-of": "^6.0.2", + "nanomatch": "^1.2.9", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.2" + } + }, + "normalize-path": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", + "integrity": "sha512-3pKJwH184Xo/lnH6oyP1q2pMd7HcypqqmRs91/6/i2CGtWwIKGCkOOMTm/zXbgTEWHw1uNpNi/igc3ePOYHb6w==", + "requires": { + "remove-trailing-separator": "^1.0.1" + } + }, + "to-regex-range": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", + "integrity": "sha512-ZZWNfCjUokXXDGXFpZehJIkZqq91BcULFq/Pi7M5i4JnxXdhMKAK682z8bCW3o8Hj1wuuzoKcW3DfVzaP6VuNg==", + "requires": { + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" + } + } + } + }, + "sanitize.css": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/sanitize.css/-/sanitize.css-10.0.0.tgz", + "integrity": "sha512-vTxrZz4dX5W86M6oVWVdOVe72ZiPs41Oi7Z6Km4W5Turyz28mrXSJhhEBZoRtzJWIv3833WKVwLSDWWkEfupMg==" + }, + "sass-graph": { + "version": "2.2.5", + "resolved": "https://registry.npmjs.org/sass-graph/-/sass-graph-2.2.5.tgz", + "integrity": "sha512-VFWDAHOe6mRuT4mZRd4eKE+d8Uedrk6Xnh7Sh9b4NGufQLQjOrvf/MQoOdx+0s92L89FeyUUNfU597j/3uNpag==", + "dev": true, + "requires": { + "glob": "^7.0.0", + "lodash": "^4.0.0", + "scss-tokenizer": "^0.2.3", + "yargs": "^13.3.2" + }, + "dependencies": { + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "dev": true + }, + "camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true + }, + "cliui": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", + "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", + "dev": true, + "requires": { + "string-width": "^3.1.0", + "strip-ansi": "^5.2.0", + "wrap-ansi": "^5.1.0" + } + }, + "emoji-regex": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", + "dev": true + }, + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dev": true, + "requires": { + "locate-path": "^3.0.0" + } + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==", + "dev": true + }, + "locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dev": true, + "requires": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + } + }, + "p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "dev": true, + "requires": { + "p-limit": "^2.0.0" + } + }, + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", + "dev": true + }, + "string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "dev": true, + "requires": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + } + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "requires": { + "ansi-regex": "^4.1.0" + } + }, + "wrap-ansi": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", + "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.0", + "string-width": "^3.0.0", + "strip-ansi": "^5.0.0" + } + }, + "yargs": { + "version": "13.3.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz", + "integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==", + "dev": true, + "requires": { + "cliui": "^5.0.0", + "find-up": "^3.0.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^3.0.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^13.1.2" + } + }, + "yargs-parser": { + "version": "13.1.2", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz", + "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==", + "dev": true, + "requires": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + } + } + } + }, + "sass-loader": { + "version": "10.1.1", + "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-10.1.1.tgz", + "integrity": "sha512-W6gVDXAd5hR/WHsPicvZdjAWHBcEJ44UahgxcIE196fW2ong0ZHMPO1kZuI5q0VlvMQZh32gpv69PLWQm70qrw==", + "requires": { + "klona": "^2.0.4", + "loader-utils": "^2.0.0", + "neo-async": "^2.6.2", + "schema-utils": "^3.0.0", + "semver": "^7.3.2" + }, + "dependencies": { + "schema-utils": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.0.0.tgz", + "integrity": "sha512-6D82/xSzO094ajanoOSbe4YvXWMfn2A//8Y1+MUqFAJul5Bs+yn36xbK9OtNDcRVSBJ9jjeoXftM6CfztsjOAA==", + "requires": { + "@types/json-schema": "^7.0.6", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + } + } + } + }, + "sax": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" + }, + "saxes": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/saxes/-/saxes-5.0.1.tgz", + "integrity": "sha512-5LBh1Tls8c9xgGjw3QrMwETmTMVk0oFgvrFSvWx62llR2hcEInrKNZ2GZCCuuy2lvWrdl5jhbpeqc5hRYKFOcw==", + "requires": { + "xmlchars": "^2.2.0" + } + }, + "scheduler": { + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.19.1.tgz", + "integrity": "sha512-n/zwRWRYSUj0/3g/otKDRPMh6qv2SYMWNq85IEa8iZyAv8od9zDYpGSnpBEjNgcMNq6Scbu5KfIPxNF72R/2EA==", + "requires": { + "loose-envify": "^1.1.0", + "object-assign": "^4.1.1" + } + }, + "schema-utils": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.1.tgz", + "integrity": "sha512-SHiNtMOUGWBQJwzISiVYKu82GiV4QYGePp3odlY1tuKO7gPtphAT5R/py0fA6xtbgLL/RvtJZnU9b8s0F1q0Xg==", + "requires": { + "@types/json-schema": "^7.0.5", + "ajv": "^6.12.4", + "ajv-keywords": "^3.5.2" + } + }, + "scss-tokenizer": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/scss-tokenizer/-/scss-tokenizer-0.2.3.tgz", + "integrity": "sha512-dYE8LhncfBUar6POCxMTm0Ln+erjeczqEvCJib5/7XNkdw1FkUGgwMPY360FY0FgPWQxHWCx29Jl3oejyGLM9Q==", + "dev": true, + "requires": { + "js-base64": "^2.1.8", + "source-map": "^0.4.2" + }, + "dependencies": { + "source-map": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz", + "integrity": "sha512-Y8nIfcb1s/7DcobUz1yOO1GSp7gyL+D9zLHDehT7iRESqGSxjJ448Sg7rvfgsRJCnKLdSl11uGf0s9X80cH0/A==", + "dev": true, + "requires": { + "amdefine": ">=0.0.4" + } + } + } + }, + "seamless-immutable": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/seamless-immutable/-/seamless-immutable-7.1.4.tgz", + "integrity": "sha512-XiUO1QP4ki4E2PHegiGAlu6r82o5A+6tRh7IkGGTVg/h+UoeX4nFBeCGPOhb4CYjvkqsfm/TUtvOMYC1xmV30A==" + }, + "select-hose": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz", + "integrity": "sha512-mEugaLK+YfkijB4fx0e6kImuJdCIt2LxCRcbEYPqRGCs4F2ogyfZU5IAZRdjCP8JPq2AtdNoC/Dux63d9Kiryg==" + }, + "selfsigned": { + "version": "1.10.8", + "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-1.10.8.tgz", + "integrity": "sha512-2P4PtieJeEwVgTU9QEcwIRDQ/mXJLX8/+I3ur+Pg16nS8oNbrGxEso9NyYWy8NAmXiNl4dlAp5MwoNeCWzON4w==", + "requires": { + "node-forge": "^0.10.0" + } + }, + "semver": { + "version": "7.3.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.2.tgz", + "integrity": "sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ==" + }, + "semver-compare": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/semver-compare/-/semver-compare-1.0.0.tgz", + "integrity": "sha512-YM3/ITh2MJ5MtzaM429anh+x2jiLVjqILF4m4oyQB18W7Ggea7BfqdH/wGMK7dDiMghv/6WG7znWMwUDzJiXow==", + "dev": true + }, + "semver-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/semver-regex/-/semver-regex-1.0.0.tgz", + "integrity": "sha512-1vZcoRC+LPtHFkLUPyrabsATDSHerxW+hJBN8h04HZOZBuewbXaNROtUVdEPrTdZsWNq6sfsXDhd48GB2xTG4g==", + "dev": true + }, + "send": { + "version": "0.17.1", + "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz", + "integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==", + "requires": { + "debug": "2.6.9", + "depd": "~1.1.2", + "destroy": "~1.0.4", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "~1.7.2", + "mime": "1.6.0", + "ms": "2.1.1", + "on-finished": "~2.3.0", + "range-parser": "~1.2.1", + "statuses": "~1.5.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + }, + "dependencies": { + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + } + } + }, + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" + } + } + }, + "serialize-javascript": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-5.0.1.tgz", + "integrity": "sha512-SaaNal9imEO737H2c05Og0/8LUXG7EnsZyMa8MzkmuHoELfT6txuj0cMqRj6zfPKnmQ1yasR4PCJc8x+M4JSPA==", + "requires": { + "randombytes": "^2.1.0" + } + }, + "serve-index": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz", + "integrity": "sha512-pXHfKNP4qujrtteMrSBb0rc8HJ9Ms/GrXwcUtUtD5s4ewDJI8bT3Cz2zTVRMKtri49pLx2e0Ya8ziP5Ya2pZZw==", + "requires": { + "accepts": "~1.3.4", + "batch": "0.6.1", + "debug": "2.6.9", + "escape-html": "~1.0.3", + "http-errors": "~1.6.2", + "mime-types": "~2.1.17", + "parseurl": "~1.3.2" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "http-errors": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", + "integrity": "sha512-lks+lVC8dgGyh97jxvxeYTWQFvh4uw4yC12gVl63Cg30sjPX4wuGcdkICVXDAESr6OJGjqGA8Iz5mkeN6zlD7A==", + "requires": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.0", + "statuses": ">= 1.4.0 < 2" + } + }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==" + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "setprototypeof": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", + "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==" + } + } + }, + "serve-static": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz", + "integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==", + "requires": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.17.1" + } + }, + "set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==" + }, + "set-value": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz", + "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==", + "requires": { + "extend-shallow": "^2.0.1", + "is-extendable": "^0.1.1", + "is-plain-object": "^2.0.3", + "split-string": "^3.0.1" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==" + }, + "setprototypeof": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", + "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==" + }, + "sha.js": { + "version": "2.4.11", + "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", + "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", + "requires": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "shebang-command": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==", + "requires": { + "shebang-regex": "^1.0.0" + } + }, + "shebang-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==" + }, + "shell-quote": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.7.2.tgz", + "integrity": "sha512-mRz/m/JVscCrkMyPqHc/bczi3OQHkLTqXHEFu0zDhK/qfv3UcOA4SVmRCLmos4bhjr9ekVQubj/R7waKapmiQg==" + }, + "shelljs": { + "version": "0.8.4", + "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.8.4.tgz", + "integrity": "sha512-7gk3UZ9kOfPLIAbslLzyWeGiEqx9e3rxwZM0KE6EL8GlGwjym9Mrlx5/p33bWTu9YG6vcS4MBxYZDHYr5lr8BQ==", + "dev": true, + "requires": { + "glob": "^7.0.0", + "interpret": "^1.0.0", + "rechoir": "^0.6.2" + } + }, + "shellwords": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/shellwords/-/shellwords-0.1.1.tgz", + "integrity": "sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww==", + "optional": true + }, + "side-channel": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", + "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "requires": { + "call-bind": "^1.0.0", + "get-intrinsic": "^1.0.2", + "object-inspect": "^1.9.0" + } + }, + "signal-exit": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", + "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==" + }, + "simple-swizzle": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", + "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==", + "requires": { + "is-arrayish": "^0.3.1" + }, + "dependencies": { + "is-arrayish": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", + "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==" + } + } + }, + "sisteransi": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", + "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==" + }, + "slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==" + }, + "slice-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", + "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", + "requires": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "requires": { + "color-convert": "^2.0.1" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + } + } + }, + "snapdragon": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", + "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==", + "requires": { + "base": "^0.11.1", + "debug": "^2.2.0", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "map-cache": "^0.2.2", + "source-map": "^0.5.6", + "source-map-resolve": "^0.5.0", + "use": "^3.1.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==", + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "requires": { + "is-extendable": "^0.1.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + } + } + }, + "snapdragon-node": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz", + "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==", + "requires": { + "define-property": "^1.0.0", + "isobject": "^3.0.0", + "snapdragon-util": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha512-cZTYKFWspt9jZsMscWo8sc/5lbPC9Q0N5nBLgb+Yd915iL3udB1uFgS3B8YCx66UVHq018DAVFoee7x+gxggeA==", + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } + } + }, + "snapdragon-util": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz", + "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", + "requires": { + "kind-of": "^3.2.0" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "sockjs": { + "version": "0.3.21", + "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.21.tgz", + "integrity": "sha512-DhbPFGpxjc6Z3I+uX07Id5ZO2XwYsWOrYjaSeieES78cq+JaJvVe5q/m1uvjIQhXinhIeCFRH6JgXe+mvVMyXw==", + "requires": { + "faye-websocket": "^0.11.3", + "uuid": "^3.4.0", + "websocket-driver": "^0.7.4" + }, + "dependencies": { + "uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==" + } + } + }, + "sockjs-client": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/sockjs-client/-/sockjs-client-1.5.1.tgz", + "integrity": "sha512-VnVAb663fosipI/m6pqRXakEOw7nvd7TUgdr3PlR/8V2I95QIdwT8L4nMxhyU8SmDBHYXU1TOElaKOmKLfYzeQ==", + "requires": { + "debug": "^3.2.6", + "eventsource": "^1.0.7", + "faye-websocket": "^0.11.3", + "inherits": "^2.0.4", + "json3": "^3.3.3", + "url-parse": "^1.5.1" + }, + "dependencies": { + "debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "requires": { + "ms": "^2.1.1" + } + } + } + }, + "sort-keys": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/sort-keys/-/sort-keys-1.1.2.tgz", + "integrity": "sha512-vzn8aSqKgytVik0iwdBEi+zevbTYZogewTUM6dtpmGwEcdzbub/TX4bCzRhebDCRC3QzXgJsLRKB2V/Oof7HXg==", + "requires": { + "is-plain-obj": "^1.0.0" + } + }, + "source-list-map": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.1.tgz", + "integrity": "sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw==" + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==" + }, + "source-map-resolve": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.3.tgz", + "integrity": "sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw==", + "requires": { + "atob": "^2.1.2", + "decode-uri-component": "^0.2.0", + "resolve-url": "^0.2.1", + "source-map-url": "^0.4.0", + "urix": "^0.1.0" + } + }, + "source-map-support": { + "version": "0.5.19", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz", + "integrity": "sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==", + "requires": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + } + } + }, + "source-map-url": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.1.tgz", + "integrity": "sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw==" + }, + "sourcemap-codec": { + "version": "1.4.8", + "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz", + "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==" + }, + "spdx-correct": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz", + "integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==", + "requires": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-exceptions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", + "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==" + }, + "spdx-expression-parse": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "requires": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-license-ids": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.7.tgz", + "integrity": "sha512-U+MTEOO0AiDzxwFvoa4JVnMV6mZlJKk2sBLt90s7G0Gd0Mlknc7kxEn3nuDPNZRta7O2uy8oLcZLVT+4sqNZHQ==" + }, + "spdy": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/spdy/-/spdy-4.0.2.tgz", + "integrity": "sha512-r46gZQZQV+Kl9oItvl1JZZqJKGr+oEkB08A6BzkiR7593/7IbtuncXHd2YoYeTsG4157ZssMu9KYvUHLcjcDoA==", + "requires": { + "debug": "^4.1.0", + "handle-thing": "^2.0.0", + "http-deceiver": "^1.2.7", + "select-hose": "^2.0.0", + "spdy-transport": "^3.0.0" + } + }, + "spdy-transport": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/spdy-transport/-/spdy-transport-3.0.0.tgz", + "integrity": "sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw==", + "requires": { + "debug": "^4.1.0", + "detect-node": "^2.0.4", + "hpack.js": "^2.1.6", + "obuf": "^1.1.2", + "readable-stream": "^3.0.6", + "wbuf": "^1.7.3" + }, + "dependencies": { + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + } + } + }, + "split": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/split/-/split-1.0.1.tgz", + "integrity": "sha512-mTyOoPbrivtXnwnIxZRFYRrPNtEFKlpB2fvjSnCQUiAA6qAZzqwna5envK4uk6OIeP17CsdF3rSBGYVBsU0Tkg==", + "dev": true, + "requires": { + "through": "2" + } + }, + "split-string": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", + "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", + "requires": { + "extend-shallow": "^3.0.0" + } + }, + "split2": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/split2/-/split2-3.2.2.tgz", + "integrity": "sha512-9NThjpgZnifTkJpzTZ7Eue85S49QwpNhZTq6GRJwObb6jnLFNGB7Qm73V5HewTROPyxD0C29xqmaI68bQtV+hg==", + "dev": true, + "requires": { + "readable-stream": "^3.0.0" + }, + "dependencies": { + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + } + } + }, + "sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==" + }, + "ssf": { + "version": "0.11.2", + "resolved": "https://registry.npmjs.org/ssf/-/ssf-0.11.2.tgz", + "integrity": "sha512-+idbmIXoYET47hH+d7dfm2epdOMUDjqcB4648sTZ+t2JwoyBFL/insLfB/racrDmsKB3diwsDA696pZMieAC5g==", + "requires": { + "frac": "~1.1.2" + } + }, + "sshpk": { + "version": "1.16.1", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz", + "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==", + "requires": { + "asn1": "~0.2.3", + "assert-plus": "^1.0.0", + "bcrypt-pbkdf": "^1.0.0", + "dashdash": "^1.12.0", + "ecc-jsbn": "~0.1.1", + "getpass": "^0.1.1", + "jsbn": "~0.1.0", + "safer-buffer": "^2.0.2", + "tweetnacl": "~0.14.0" + } + }, + "ssri": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-8.0.1.tgz", + "integrity": "sha512-97qShzy1AiyxvPNIkLWoGua7xoQzzPjQ0HAH4B0rWKo7SZ6USuPcrUiAFrws0UH8RrbWmgq3LMTObhPIHbbBeQ==", + "requires": { + "minipass": "^3.1.1" + } + }, + "stable": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/stable/-/stable-0.1.8.tgz", + "integrity": "sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==" + }, + "stack-utils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.3.tgz", + "integrity": "sha512-gL//fkxfWUsIlFL2Tl42Cl6+HFALEaB1FU76I/Fy+oZjRreP7OPMXFlGbxM7NQsI0ZpUfw76sHnv0WNYuTb7Iw==", + "requires": { + "escape-string-regexp": "^2.0.0" + }, + "dependencies": { + "escape-string-regexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==" + } + } + }, + "stackframe": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/stackframe/-/stackframe-1.2.0.tgz", + "integrity": "sha512-GrdeshiRmS1YLMYgzF16olf2jJ/IzxXY9lhKOskuVziubpTYcYqyOwYeJKzQkwy7uN0fYSsbsC4RQaXf9LCrYA==" + }, + "standard-version": { + "version": "9.2.0", + "resolved": "https://registry.npmjs.org/standard-version/-/standard-version-9.2.0.tgz", + "integrity": "sha512-utJcqjk/wR4sePSwDoRcc5CzJ6S+kec5Hd0+1TJI+j1TRYuuptweAnEUdkkjGf2vYoGab2ezefyVtW065HZ1Uw==", + "dev": true, + "requires": { + "chalk": "^2.4.2", + "conventional-changelog": "3.1.24", + "conventional-changelog-config-spec": "2.1.0", + "conventional-changelog-conventionalcommits": "4.5.0", + "conventional-recommended-bump": "6.1.0", + "detect-indent": "^6.0.0", + "detect-newline": "^3.1.0", + "dotgitignore": "^2.1.0", + "figures": "^3.1.0", + "find-up": "^5.0.0", + "fs-access": "^1.0.1", + "git-semver-tags": "^4.0.0", + "semver": "^7.1.1", + "stringify-package": "^1.0.1", + "yargs": "^16.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, + "requires": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "figures": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", + "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", + "dev": true, + "requires": { + "escape-string-regexp": "^1.0.5" + } + }, + "find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "requires": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + } + }, + "locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "requires": { + "p-locate": "^5.0.0" + } + }, + "p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "requires": { + "yocto-queue": "^0.1.0" + } + }, + "p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "requires": { + "p-limit": "^3.0.2" + } + }, + "wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + } + }, + "y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true + }, + "yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dev": true, + "requires": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + } + }, + "yargs-parser": { + "version": "20.2.7", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.7.tgz", + "integrity": "sha512-FiNkvbeHzB/syOjIUxFDCnhSfzAL8R5vs40MgLFBorXACCOAEaWu0gRZl14vG8MR9AOJIZbmkjhusqBYZ3HTHw==", + "dev": true + } + } + }, + "static-extend": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", + "integrity": "sha512-72E9+uLc27Mt718pMHt9VMNiAL4LMsmDbBva8mxWUCkT07fSzEGMYUCk0XWY6lp0j6RBAG4cJ3mWuZv2OE3s0g==", + "requires": { + "define-property": "^0.2.5", + "object-copy": "^0.1.0" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==", + "requires": { + "is-descriptor": "^0.1.0" + } + } + } + }, + "statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=" + }, + "stdout-stream": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/stdout-stream/-/stdout-stream-1.4.1.tgz", + "integrity": "sha512-j4emi03KXqJWcIeF8eIXkjMFN1Cmb8gUlDYGeBALLPo5qdyTfA9bOtl8m33lRoC+vFMkP3gl0WsDr6+gzxbbTA==", + "dev": true, + "requires": { + "readable-stream": "^2.0.1" + } + }, + "stealthy-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/stealthy-require/-/stealthy-require-1.1.1.tgz", + "integrity": "sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks=" + }, + "stream-browserify": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-2.0.2.tgz", + "integrity": "sha512-nX6hmklHs/gr2FuxYDltq8fJA1GDlxKQCz8O/IM4atRqBH8OORmBNgfvW5gG10GT/qQ9u0CzIvr2X5Pkt6ntqg==", + "requires": { + "inherits": "~2.0.1", + "readable-stream": "^2.0.2" + } + }, + "stream-each": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/stream-each/-/stream-each-1.2.3.tgz", + "integrity": "sha512-vlMC2f8I2u/bZGqkdfLQW/13Zihpej/7PmSiMQsbYddxuTsJp8vRe2x2FvVExZg7FaOds43ROAuFJwPR4MTZLw==", + "requires": { + "end-of-stream": "^1.1.0", + "stream-shift": "^1.0.0" + } + }, + "stream-http": { + "version": "2.8.3", + "resolved": "https://registry.npmjs.org/stream-http/-/stream-http-2.8.3.tgz", + "integrity": "sha512-+TSkfINHDo4J+ZobQLWiMouQYB+UVYFttRA94FpEzzJ7ZdqcL4uUUQ7WkdkI4DSozGmgBUE/a47L+38PenXhUw==", + "requires": { + "builtin-status-codes": "^3.0.0", + "inherits": "^2.0.1", + "readable-stream": "^2.3.6", + "to-arraybuffer": "^1.0.0", + "xtend": "^4.0.0" + } + }, + "stream-shift": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.1.tgz", + "integrity": "sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ==" + }, + "strict-uri-encode": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz", + "integrity": "sha512-R3f198pcvnB+5IpnBlRkphuE9n46WyVl8I39W/ZUTZLz4nqSP/oLYUrcnJrw462Ds8he4YKMov2efsTIw1BDGQ==" + }, + "string-length": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", + "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", + "requires": { + "char-regex": "^1.0.2", + "strip-ansi": "^6.0.0" + } + }, + "string-natural-compare": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/string-natural-compare/-/string-natural-compare-3.0.1.tgz", + "integrity": "sha512-n3sPwynL1nwKi3WJ6AIsClwBMa0zTi54fn2oLU6ndfTSIO05xaznjSf15PcBZU6FNWbmN5Q6cxT4V5hGvB4taw==" + }, + "string-width": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", + "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" + } + }, + "string.prototype.matchall": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.4.tgz", + "integrity": "sha512-pknFIWVachNcyqRfaQSeu/FUfpvJTe4uskUSZ9Wc1RijsPuzbZ8TyYT8WCNnntCjUEqQ3vUHMAfVj2+wLAisPQ==", + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.18.0-next.2", + "has-symbols": "^1.0.1", + "internal-slot": "^1.0.3", + "regexp.prototype.flags": "^1.3.1", + "side-channel": "^1.0.4" + } + }, + "string.prototype.trim": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.4.tgz", + "integrity": "sha512-hWCk/iqf7lp0/AgTF7/ddO1IWtSNPASjlzCicV5irAVdE1grjsneK26YG6xACMBEdCvO8fUST0UzDMh/2Qy+9Q==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.18.0-next.2" + } + }, + "string.prototype.trimend": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz", + "integrity": "sha512-y9xCjw1P23Awk8EvTpcyL2NIr1j7wJ39f+k6lvRnSMz+mz9CGz9NYPelDk42kOz6+ql8xjfK8oYzy3jAP5QU5A==", + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3" + } + }, + "string.prototype.trimstart": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz", + "integrity": "sha512-jh6e984OBfvxS50tdY2nRZnoC5/mLFKOREQfw8t5yytkoUsJRNxvI/E39qu1sD0OtWI3OC0XgKSmcWwziwYuZw==", + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + } + }, + "stringify-object": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/stringify-object/-/stringify-object-3.3.0.tgz", + "integrity": "sha512-rHqiFh1elqCQ9WPLIC8I0Q/g/wj5J1eMkyoiD6eoQApWHP0FtlK7rqnhmabL5VUY9JQCcqwwvlOaSuutekgyrw==", + "requires": { + "get-own-enumerable-property-symbols": "^3.0.0", + "is-obj": "^1.0.1", + "is-regexp": "^1.0.0" + }, + "dependencies": { + "is-obj": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz", + "integrity": "sha512-l4RyHgRqGN4Y3+9JHVrNqO+tN0rV5My76uW5/nuO4K1b6vw5G8d/cmFjP9tRfEsdhZNt0IFdZuK/c2Vr4Nb+Qg==" + } + } + }, + "stringify-package": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/stringify-package/-/stringify-package-1.0.1.tgz", + "integrity": "sha512-sa4DUQsYciMP1xhKWGuFM04fB0LG/9DlluZoSVywUMRNvzid6XucHK0/90xGxRoHrAaROrcHK1aPKaijCtSrhg==", + "dev": true + }, + "strip-ansi": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", + "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "requires": { + "ansi-regex": "^5.0.0" + } + }, + "strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==" + }, + "strip-comments": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/strip-comments/-/strip-comments-1.0.2.tgz", + "integrity": "sha512-kL97alc47hoyIQSV165tTt9rG5dn4w1dNnBhOQ3bOU1Nc1hel09jnXANaHJ7vzHLd4Ju8kseDGzlev96pghLFw==", + "requires": { + "babel-extract-comments": "^1.0.0", + "babel-plugin-transform-object-rest-spread": "^6.26.0" + } + }, + "strip-eof": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", + "integrity": "sha512-7FCwGGmx8mD5xQd3RPUvnSpUXHM3BWuzjtpD4TXsfcZ9EL4azvVVUscFYwD9nx8Kh+uCBC00XBtAykoMHwTh8Q==" + }, + "strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==" + }, + "strip-indent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", + "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==", + "dev": true, + "requires": { + "min-indent": "^1.0.0" + } + }, + "strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==" + }, + "style-loader": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-1.3.0.tgz", + "integrity": "sha512-V7TCORko8rs9rIqkSrlMfkqA63DfoGBBJmK1kKGCcSi+BWb4cqz0SRsnp4l6rU5iwOEd0/2ePv68SV22VXon4Q==", + "requires": { + "loader-utils": "^2.0.0", + "schema-utils": "^2.7.0" + } + }, + "stylehacks": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-4.0.3.tgz", + "integrity": "sha512-7GlLk9JwlElY4Y6a/rmbH2MhVlTyVmiJd1PfTCqFaIBEGMYNsrO/v3SeGTdhBThLg4Z+NbOk/qFMwCa+J+3p/g==", + "requires": { + "browserslist": "^4.0.0", + "postcss": "^7.0.0", + "postcss-selector-parser": "^3.0.0" + }, + "dependencies": { + "postcss-selector-parser": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-3.1.2.tgz", + "integrity": "sha512-h7fJ/5uWuRVyOtkO45pnt1Ih40CEleeyCHzipqAZO2e5H20g25Y48uYnFUiShvY4rZWNJ/Bib/KVPmanaCtOhA==", + "requires": { + "dot-prop": "^5.2.0", + "indexes-of": "^1.0.1", + "uniq": "^1.0.1" + } + } + } + }, + "stylis": { + "version": "4.0.13", + "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.0.13.tgz", + "integrity": "sha512-xGPXiFVl4YED9Jh7Euv2V220mriG9u4B2TA6Ybjc1catrstKD2PpIdU3U0RKpkVBC2EhmL/F0sPCr9vrFTNRag==" + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "requires": { + "has-flag": "^3.0.0" + } + }, + "supports-hyperlinks": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-2.2.0.tgz", + "integrity": "sha512-6sXEzV5+I5j8Bmq9/vUphGRM/RJNT9SCURJLjwfOg51heRtguGWDzcaBlgAzKhQa0EVNpPEKzQuBwZ8S8WaCeQ==", + "requires": { + "has-flag": "^4.0.0", + "supports-color": "^7.0.0" + }, + "dependencies": { + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "svg-parser": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/svg-parser/-/svg-parser-2.0.4.tgz", + "integrity": "sha512-e4hG1hRwoOdRb37cIMSgzNsxyzKfayW6VOflrwvR+/bzrkyxY/31WkbgnQpgtrNp1SdpJvpUAGTa/ZoiPNDuRQ==" + }, + "svgo": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/svgo/-/svgo-1.3.2.tgz", + "integrity": "sha512-yhy/sQYxR5BkC98CY7o31VGsg014AKLEPxdfhora76l36hD9Rdy5NZA/Ocn6yayNPgSamYdtX2rFJdcv07AYVw==", + "requires": { + "chalk": "^2.4.1", + "coa": "^2.0.2", + "css-select": "^2.0.0", + "css-select-base-adapter": "^0.1.1", + "css-tree": "1.0.0-alpha.37", + "csso": "^4.0.2", + "js-yaml": "^3.13.1", + "mkdirp": "~0.5.1", + "object.values": "^1.1.0", + "sax": "~1.2.4", + "stable": "^0.1.8", + "unquote": "~1.1.1", + "util.promisify": "~1.0.0" + } + }, + "symbol-tree": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", + "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==" + }, + "table": { + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/table/-/table-6.6.0.tgz", + "integrity": "sha512-iZMtp5tUvcnAdtHpZTWLPF0M7AgiQsURR2DwmxnJwSy8I3+cY+ozzVvYha3BOLG2TB+L0CqjIz+91htuj6yCXg==", + "requires": { + "ajv": "^8.0.1", + "lodash.clonedeep": "^4.5.0", + "lodash.flatten": "^4.4.0", + "lodash.truncate": "^4.4.2", + "slice-ansi": "^4.0.0", + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0" + }, + "dependencies": { + "ajv": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.1.0.tgz", + "integrity": "sha512-B/Sk2Ix7A36fs/ZkuGLIR86EdjbgR6fsAcbx9lOP/QBSXujDNbVmIS/U4Itz5k8fPFDeVZl/zQ/gJW4Jrq6XjQ==", + "requires": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + } + }, + "json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + } + } + }, + "tapable": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz", + "integrity": "sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==" + }, + "tar": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/tar/-/tar-2.2.2.tgz", + "integrity": "sha512-FCEhQ/4rE1zYv9rYXJw/msRqsnmlje5jHP6huWeBZ704jUTy02c5AZyWujpMR1ax6mVw9NyJMfuK2CMDWVIfgA==", + "dev": true, + "requires": { + "block-stream": "*", + "fstream": "^1.0.12", + "inherits": "2" + } + }, + "temp-dir": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/temp-dir/-/temp-dir-1.0.0.tgz", + "integrity": "sha512-xZFXEGbG7SNC3itwBzI3RYjq/cEhBkx2hJuKGIUOcEULmkQExXiHat2z/qkISYsuR+IKumhEfKKbV5qXmhICFQ==" + }, + "tempy": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/tempy/-/tempy-0.3.0.tgz", + "integrity": "sha512-WrH/pui8YCwmeiAoxV+lpRH9HpRtgBhSR2ViBPgpGb/wnYDzp21R4MN45fsCGvLROvY67o3byhJRYRONJyImVQ==", + "requires": { + "temp-dir": "^1.0.0", + "type-fest": "^0.3.1", + "unique-string": "^1.0.0" + }, + "dependencies": { + "type-fest": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.3.1.tgz", + "integrity": "sha512-cUGJnCdr4STbePCgqNFbpVNCepa+kAVohJs1sLhxzdH+gnEoOd8VhbYa7pD3zZYGiURWM2xzEII3fQcRizDkYQ==" + } + } + }, + "terminal-link": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/terminal-link/-/terminal-link-2.1.1.tgz", + "integrity": "sha512-un0FmiRUQNr5PJqy9kP7c40F5BOfpGlYTrxonDChEZB7pzZxRNp/bt+ymiy9/npwXya9KH99nJ/GXFIiUkYGFQ==", + "requires": { + "ansi-escapes": "^4.2.1", + "supports-hyperlinks": "^2.0.0" + } + }, + "terser": { + "version": "4.8.0", + "resolved": "https://registry.npmjs.org/terser/-/terser-4.8.0.tgz", + "integrity": "sha512-EAPipTNeWsb/3wLPeup1tVPaXfIaU68xMnVdPafIL1TV05OhASArYyIfFvnvJCNrR2NIOvDVNNTFRa+Re2MWyw==", + "requires": { + "commander": "^2.20.0", + "source-map": "~0.6.1", + "source-map-support": "~0.5.12" + }, + "dependencies": { + "commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + } + } + }, + "terser-webpack-plugin": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-4.2.3.tgz", + "integrity": "sha512-jTgXh40RnvOrLQNgIkwEKnQ8rmHjHK4u+6UBEi+W+FPmvb+uo+chJXntKe7/3lW5mNysgSWD60KyesnhW8D6MQ==", + "requires": { + "cacache": "^15.0.5", + "find-cache-dir": "^3.3.1", + "jest-worker": "^26.5.0", + "p-limit": "^3.0.2", + "schema-utils": "^3.0.0", + "serialize-javascript": "^5.0.1", + "source-map": "^0.6.1", + "terser": "^5.3.4", + "webpack-sources": "^1.4.3" + }, + "dependencies": { + "commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" + }, + "find-cache-dir": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.1.tgz", + "integrity": "sha512-t2GDMt3oGC/v+BMwzmllWDuJF/xcDtE5j/fCGbqDD7OLuJkj0cfh1YSA5VKPvwMeLFLNDBkwOKZ2X85jGLVftQ==", + "requires": { + "commondir": "^1.0.1", + "make-dir": "^3.0.2", + "pkg-dir": "^4.1.0" + } + }, + "make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "requires": { + "semver": "^6.0.0" + } + }, + "p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "requires": { + "yocto-queue": "^0.1.0" + } + }, + "pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "requires": { + "find-up": "^4.0.0" + } + }, + "schema-utils": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.0.0.tgz", + "integrity": "sha512-6D82/xSzO094ajanoOSbe4YvXWMfn2A//8Y1+MUqFAJul5Bs+yn36xbK9OtNDcRVSBJ9jjeoXftM6CfztsjOAA==", + "requires": { + "@types/json-schema": "^7.0.6", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + } + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + }, + "terser": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.7.0.tgz", + "integrity": "sha512-HP5/9hp2UaZt5fYkuhNBR8YyRcT8juw8+uFbAme53iN9hblvKnLUTKkmwJG6ocWpIKf8UK4DoeWG4ty0J6S6/g==", + "requires": { + "commander": "^2.20.0", + "source-map": "~0.7.2", + "source-map-support": "~0.5.19" + }, + "dependencies": { + "source-map": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", + "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==" + } + } + } + } + }, + "test-exclude": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", + "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", + "requires": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + } + }, + "text-extensions": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/text-extensions/-/text-extensions-1.9.0.tgz", + "integrity": "sha512-wiBrwC1EhBelW12Zy26JeOUkQ5mRu+5o8rpsJk5+2t+Y5vE7e842qtZDQ2g1NpX/29HdyFeJ4nSIhI47ENSxlQ==", + "dev": true + }, + "text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==" + }, + "throat": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/throat/-/throat-5.0.0.tgz", + "integrity": "sha512-fcwX4mndzpLQKBS1DVYhGAcYaYt7vsHNIvQV+WXMvnow5cgjPphq5CaayLaGsjRdSCKZFNGt7/GYAuXaNOiYCA==" + }, + "through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", + "dev": true + }, + "through2": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", + "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", + "requires": { + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" + } + }, + "thunky": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz", + "integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==" + }, + "timers-browserify": { + "version": "2.0.12", + "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-2.0.12.tgz", + "integrity": "sha512-9phl76Cqm6FhSX9Xe1ZUAMLtm1BLkKj2Qd5ApyWkXzsMRaA7dgr81kf4wJmQf/hAvg8EEyJxDo3du/0KlhPiKQ==", + "requires": { + "setimmediate": "^1.0.4" + } + }, + "timsort": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/timsort/-/timsort-0.3.0.tgz", + "integrity": "sha512-qsdtZH+vMoCARQtyod4imc2nIJwg9Cc7lPRrw9CzF8ZKR0khdr8+2nX80PBhET3tcyTtJDxAffGh2rXH4tyU8A==" + }, + "tiny-invariant": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.1.0.tgz", + "integrity": "sha512-ytxQvrb1cPc9WBEI/HSeYYoGD0kWnGEOR8RY6KomWLBVhqz0RgTwVO9dLrGz7dC+nN9llyI7OKAgRq8Vq4ZBSw==" + }, + "tiny-warning": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/tiny-warning/-/tiny-warning-1.0.3.tgz", + "integrity": "sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==" + }, + "tmp": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "dev": true, + "requires": { + "os-tmpdir": "~1.0.2" + } + }, + "tmpl": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.4.tgz", + "integrity": "sha1-I2QN17QtAEM5ERQIIOXPRA5SHdE=" + }, + "to-arraybuffer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz", + "integrity": "sha512-okFlQcoGTi4LQBG/PgSYblw9VOyptsz2KJZqc6qtgGdes8VktzUQkj4BI2blit072iS8VODNcMA+tvnS9dnuMA==" + }, + "to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==" + }, + "to-object-path": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", + "integrity": "sha512-9mWHdnGRuh3onocaHzukyvCZhzvr6tiflAy/JRFXcJX0TjgfWA9pk9t8CMbzmBE4Jfw58pXbkngtBtqYxzNEyg==", + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "to-regex": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz", + "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==", + "requires": { + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "regex-not": "^1.0.2", + "safe-regex": "^1.1.0" + } + }, + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "requires": { + "is-number": "^7.0.0" + } + }, + "toidentifier": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", + "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==" + }, + "tough-cookie": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.0.0.tgz", + "integrity": "sha512-tHdtEpQCMrc1YLrMaqXXcj6AxhYi/xgit6mZu1+EDWUn+qhUf8wMQoFIy9NXuq23zAwtcB0t/MjACGR18pcRbg==", + "requires": { + "psl": "^1.1.33", + "punycode": "^2.1.1", + "universalify": "^0.1.2" + }, + "dependencies": { + "universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==" + } + } + }, + "tr46": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-2.0.2.tgz", + "integrity": "sha512-3n1qG+/5kg+jrbTzwAykB5yRYtQCTqOGKq5U5PE3b0a1/mzo6snDhjGS0zJVJunO0NrT3Dg1MLy5TjWP/UJppg==", + "requires": { + "punycode": "^2.1.1" + } + }, + "trim-newlines": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-1.0.0.tgz", + "integrity": "sha512-Nm4cF79FhSTzrLKGDMi3I4utBtFv8qKy4sq1enftf2gMdpqI8oVQTAfySkTz5r49giVzDj88SVZXP4CeYQwjaw==", + "dev": true + }, + "trim-off-newlines": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/trim-off-newlines/-/trim-off-newlines-1.0.1.tgz", + "integrity": "sha1-n5up2e+odkw4dpi8v+sshI8RrbM=", + "dev": true + }, + "true-case-path": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/true-case-path/-/true-case-path-1.0.3.tgz", + "integrity": "sha512-m6s2OdQe5wgpFMC+pAJ+q9djG82O2jcHPOI6RNg1yy9rCYR+WD6Nbpl32fDpfC56nirdRy+opFa/Vk7HYhqaew==", + "dev": true, + "requires": { + "glob": "^7.1.2" + } + }, + "tryer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/tryer/-/tryer-1.0.1.tgz", + "integrity": "sha512-c3zayb8/kWWpycWYg87P71E1S1ZL6b6IJxfb5fvsUgsf0S2MVGaDhDXXjDMpdCpfWXqptc+4mXwmiy1ypXqRAA==" + }, + "ts-jest": { + "version": "26.5.5", + "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-26.5.5.tgz", + "integrity": "sha512-7tP4m+silwt1NHqzNRAPjW1BswnAhopTdc2K3HEkRZjF0ZG2F/e/ypVH0xiZIMfItFtD3CX0XFbwPzp9fIEUVg==", + "dev": true, + "requires": { + "bs-logger": "0.x", + "buffer-from": "1.x", + "fast-json-stable-stringify": "2.x", + "jest-util": "^26.1.0", + "json5": "2.x", + "lodash": "4.x", + "make-error": "1.x", + "mkdirp": "1.x", + "semver": "7.x", + "yargs-parser": "20.x" + }, + "dependencies": { + "mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "dev": true + }, + "yargs-parser": { + "version": "20.2.7", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.7.tgz", + "integrity": "sha512-FiNkvbeHzB/syOjIUxFDCnhSfzAL8R5vs40MgLFBorXACCOAEaWu0gRZl14vG8MR9AOJIZbmkjhusqBYZ3HTHw==", + "dev": true + } + } + }, + "ts-pnp": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/ts-pnp/-/ts-pnp-1.2.0.tgz", + "integrity": "sha512-csd+vJOb/gkzvcCHgTGSChYpy5f1/XKNsmvBGO4JXS+z1v2HobugDz4s1IeFXM3wZB44uczs+eazB5Q/ccdhQw==" + }, + "tsconfig-paths": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.9.0.tgz", + "integrity": "sha512-dRcuzokWhajtZWkQsDVKbWyY+jgcLC5sqJhg2PSgf4ZkH2aHPvaOY8YWGhmjb68b5qqTfasSsDO9k7RUiEmZAw==", + "requires": { + "@types/json5": "^0.0.29", + "json5": "^1.0.1", + "minimist": "^1.2.0", + "strip-bom": "^3.0.0" + }, + "dependencies": { + "json5": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", + "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", + "requires": { + "minimist": "^1.2.0" + } + } + } + }, + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + }, + "tsutils": { + "version": "3.21.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", + "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", + "requires": { + "tslib": "^1.8.1" + } + }, + "tty-browserify": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz", + "integrity": "sha512-JVa5ijo+j/sOoHGjw0sxw734b1LhBkQ3bvUGNdxnVXDCX81Yx7TFgnZygxrIIWn23hbfTaMYLwRmAxFyDuFmIw==" + }, + "tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", + "requires": { + "safe-buffer": "^5.0.1" + } + }, + "tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==" + }, + "type": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz", + "integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==" + }, + "type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "requires": { + "prelude-ls": "^1.2.1" + } + }, + "type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==" + }, + "type-fest": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==" + }, + "type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "requires": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + } + }, + "typed-styles": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/typed-styles/-/typed-styles-0.0.7.tgz", + "integrity": "sha512-pzP0PWoZUhsECYjABgCGQlRGL1n7tOHsgwYv3oIiEpJwGhFTuty/YNeduxQYzXXa3Ge5BdT6sHYIQYpl4uJ+5Q==" + }, + "typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==" + }, + "typedarray-to-buffer": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", + "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", + "requires": { + "is-typedarray": "^1.0.0" + } + }, + "typescript": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.2.4.tgz", + "integrity": "sha512-V+evlYHZnQkaz8TRBuxTA92yZBPotr5H+WhQ7bD3hZUndx5tGOa1fuCgeSjxAzM1RiN5IzvadIXTVefuuwZCRg==" + }, + "uglify-js": { + "version": "3.13.4", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.13.4.tgz", + "integrity": "sha512-kv7fCkIXyQIilD5/yQy8O+uagsYIOt5cZvs890W40/e/rvjMSzJw81o9Bg0tkURxzZBROtDQhW2LFjOGoK3RZw==", + "dev": true, + "optional": true + }, + "unbox-primitive": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.1.tgz", + "integrity": "sha512-tZU/3NqK3dA5gpE1KtyiJUrEB0lxnGkMFHptJ7q6ewdZ8s12QrODwNbhIJStmJkd1QDXa1NRA8aF2A1zk/Ypyw==", + "requires": { + "function-bind": "^1.1.1", + "has-bigints": "^1.0.1", + "has-symbols": "^1.0.2", + "which-boxed-primitive": "^1.0.2" + } + }, + "unicode-canonical-property-names-ecmascript": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz", + "integrity": "sha512-jDrNnXWHd4oHiTZnx/ZG7gtUTVp+gCcTTKr8L0HjlwphROEW3+Him+IpvC+xcJEFegapiMZyZe02CyuOnRmbnQ==" + }, + "unicode-match-property-ecmascript": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-1.0.4.tgz", + "integrity": "sha512-L4Qoh15vTfntsn4P1zqnHulG0LdXgjSO035fEpdtp6YxXhMT51Q6vgM5lYdG/5X3MjS+k/Y9Xw4SFCY9IkR0rg==", + "requires": { + "unicode-canonical-property-names-ecmascript": "^1.0.4", + "unicode-property-aliases-ecmascript": "^1.0.4" + } + }, + "unicode-match-property-value-ecmascript": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-1.2.0.tgz", + "integrity": "sha512-wjuQHGQVofmSJv1uVISKLE5zO2rNGzM/KCYZch/QQvez7C1hUhBIuZ701fYXExuufJFMPhv2SyL8CyoIfMLbIQ==" + }, + "unicode-property-aliases-ecmascript": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-1.1.0.tgz", + "integrity": "sha512-PqSoPh/pWetQ2phoj5RLiaqIk4kCNwoV3CI+LfGmWLKI3rE3kl1h59XpX2BjgDrmbxD9ARtQobPGU1SguCYuQg==" + }, + "union-value": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz", + "integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==", + "requires": { + "arr-union": "^3.1.0", + "get-value": "^2.0.6", + "is-extendable": "^0.1.1", + "set-value": "^2.0.1" + } + }, + "uniq": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/uniq/-/uniq-1.0.1.tgz", + "integrity": "sha512-Gw+zz50YNKPDKXs+9d+aKAjVwpjNwqzvNpLigIruT4HA9lMZNdMqs9x07kKHB/L9WRzqp4+DlTU5s4wG2esdoA==" + }, + "uniqs": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/uniqs/-/uniqs-2.0.0.tgz", + "integrity": "sha512-mZdDpf3vBV5Efh29kMw5tXoup/buMgxLzOt/XKFKcVmi+15ManNQWr6HfZ2aiZTYlYixbdNJ0KFmIZIv52tHSQ==" + }, + "unique-filename": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz", + "integrity": "sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ==", + "requires": { + "unique-slug": "^2.0.0" + } + }, + "unique-slug": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.2.tgz", + "integrity": "sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w==", + "requires": { + "imurmurhash": "^0.1.4" + } + }, + "unique-string": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-1.0.0.tgz", + "integrity": "sha512-ODgiYu03y5g76A1I9Gt0/chLCzQjvzDy7DsZGsLOE/1MrF6wriEskSncj1+/C58Xk/kPZDppSctDybCwOSaGAg==", + "requires": { + "crypto-random-string": "^1.0.0" + } + }, + "universalify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", + "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==" + }, + "unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==" + }, + "unquote": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/unquote/-/unquote-1.1.1.tgz", + "integrity": "sha512-vRCqFv6UhXpWxZPyGDh/F3ZpNv8/qo7w6iufLpQg9aKnQ71qM4B5KiI7Mia9COcjEhrO9LueHpMYjYzsWH3OIg==" + }, + "unset-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz", + "integrity": "sha512-PcA2tsuGSF9cnySLHTLSh2qrQiJ70mn+r+Glzxv2TWZblxsxCC52BDlZoPCsz7STd9pN7EZetkWZBAvk4cgZdQ==", + "requires": { + "has-value": "^0.3.1", + "isobject": "^3.0.0" + }, + "dependencies": { + "has-value": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz", + "integrity": "sha512-gpG936j8/MzaeID5Yif+577c17TxaDmhuyVgSwtnL/q8UUTySg8Mecb+8Cf1otgLoD7DDH75axp86ER7LFsf3Q==", + "requires": { + "get-value": "^2.0.3", + "has-values": "^0.1.4", + "isobject": "^2.0.0" + }, + "dependencies": { + "isobject": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", + "integrity": "sha512-+OUdGJlgjOBZDfxnDjYYG6zp487z0JGNQq3cYQYg5f5hKR+syHMsaztzGeml/4kGG55CSpKSpWTY+jYGgsHLgA==", + "requires": { + "isarray": "1.0.0" + } + } + } + }, + "has-values": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz", + "integrity": "sha512-J8S0cEdWuQbqD9//tlZxiMuMNmxB8PlEwvYwuxsTmR1G5RXUePEX/SJn7aD0GMLieuZYSwNH0cQuJGwnYunXRQ==" + } + } + }, + "upath": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/upath/-/upath-1.2.0.tgz", + "integrity": "sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==" + }, + "uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "requires": { + "punycode": "^2.1.0" + } + }, + "urix": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", + "integrity": "sha512-Am1ousAhSLBeB9cG/7k7r2R0zj50uDRlZHPGbazid5s9rlF1F/QKYObEKSIunSjIOkJZqwRRLpvewjEkM7pSqg==" + }, + "url": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz", + "integrity": "sha512-kbailJa29QrtXnxgq+DdCEGlbTeYM2eJUxsz6vjZavrCYPMIFHMKQmSKYAIuUK2i7hgPm28a8piX5NTUtM/LKQ==", + "requires": { + "punycode": "1.3.2", + "querystring": "0.2.0" + }, + "dependencies": { + "punycode": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", + "integrity": "sha512-RofWgt/7fL5wP1Y7fxE7/EmTLzQVnB0ycyibJ0OOHIlJqTNzglYFxVwETOcIoJqJmpDXJ9xImDv+Fq34F/d4Dw==" + }, + "querystring": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", + "integrity": "sha512-X/xY82scca2tau62i9mDyU9K+I+djTMUsvwf7xnUX5GLvVzgJybOJf4Y6o9Zx3oJK/LSXg5tTZBjwzqVPaPO2g==" + } + } + }, + "url-loader": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/url-loader/-/url-loader-4.1.1.tgz", + "integrity": "sha512-3BTV812+AVHHOJQO8O5MkWgZ5aosP7GnROJwvzLS9hWDj00lZ6Z0wNak423Lp9PBZN05N+Jk/N5Si8jRAlGyWA==", + "requires": { + "loader-utils": "^2.0.0", + "mime-types": "^2.1.27", + "schema-utils": "^3.0.0" + }, + "dependencies": { + "schema-utils": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.0.0.tgz", + "integrity": "sha512-6D82/xSzO094ajanoOSbe4YvXWMfn2A//8Y1+MUqFAJul5Bs+yn36xbK9OtNDcRVSBJ9jjeoXftM6CfztsjOAA==", + "requires": { + "@types/json-schema": "^7.0.6", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + } + } + } + }, + "url-parse": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.1.tgz", + "integrity": "sha512-HOfCOUJt7iSYzEx/UqgtwKRMC6EU91NFhsCHMv9oM03VJcVo2Qrp8T8kI9D7amFf1cu+/3CEhgb3rF9zL7k85Q==", + "requires": { + "querystringify": "^2.1.1", + "requires-port": "^1.0.0" + } + }, + "use": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", + "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==" + }, + "util": { + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/util/-/util-0.11.1.tgz", + "integrity": "sha512-HShAsny+zS2TZfaXxD9tYj4HQGlBezXZMZuM/S5PKLLoZkShZiGk9o5CzukI1LVHZvjdvZ2Sj1aW/Ndn2NB/HQ==", + "requires": { + "inherits": "2.0.3" + }, + "dependencies": { + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==" + } + } + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" + }, + "util.promisify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/util.promisify/-/util.promisify-1.0.1.tgz", + "integrity": "sha512-g9JpC/3He3bm38zsLupWryXHoEcS22YHthuPQSJdMy6KNrzIRzWqcsHzD/WUnqe45whVou4VIsPew37DoXWNrA==", + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.2", + "has-symbols": "^1.0.1", + "object.getownpropertydescriptors": "^2.1.0" + } + }, + "utila": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/utila/-/utila-0.4.0.tgz", + "integrity": "sha512-Z0DbgELS9/L/75wZbro8xAnT50pBVFQZ+hUEueGDU5FN51YSCYM+jdxsfCiHjwNP/4LCDD0i/graKpeBnOXKRA==" + }, + "utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==" + }, + "uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "optional": true + }, + "v8-compile-cache": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", + "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==" + }, + "v8-to-istanbul": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-7.1.1.tgz", + "integrity": "sha512-p0BB09E5FRjx0ELN6RgusIPsSPhtgexSRcKETybEs6IGOTXJSZqfwxp7r//55nnu0f1AxltY5VvdVqy2vZf9AA==", + "requires": { + "@types/istanbul-lib-coverage": "^2.0.1", + "convert-source-map": "^1.6.0", + "source-map": "^0.7.3" + }, + "dependencies": { + "source-map": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", + "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==" + } + } + }, + "validate-commit-msg": { + "version": "2.14.0", + "resolved": "https://registry.npmjs.org/validate-commit-msg/-/validate-commit-msg-2.14.0.tgz", + "integrity": "sha512-sETG7UmikRVseMyeayyF6L1svmNXoZiyG4TVyLYSVSYjcSuZT602T+irJKjIyE+FutGyngSLxHGLTeyFy+oyag==", + "dev": true, + "requires": { + "conventional-commit-types": "^2.0.0", + "find-parent-dir": "^0.3.0", + "findup": "0.1.5", + "semver-regex": "1.0.0" + }, + "dependencies": { + "conventional-commit-types": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/conventional-commit-types/-/conventional-commit-types-2.3.0.tgz", + "integrity": "sha512-6iB39PrcGYdz0n3z31kj6/Km6mK9hm9oMRhwcLnKxE7WNoeRKZbTAobliKrbYZ5jqyCvtcVEfjCiaEzhL3AVmQ==", + "dev": true + } + } + }, + "validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "requires": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "value-equal": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/value-equal/-/value-equal-1.0.1.tgz", + "integrity": "sha512-NOJ6JZCAWr0zlxZt+xqCHNTEKOsrks2HQd4MqhP1qy4z1SkbEP467eNx6TgDKXMvUOb+OENfJCZwM+16n7fRfw==" + }, + "vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==" + }, + "vendors": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/vendors/-/vendors-1.0.4.tgz", + "integrity": "sha512-/juG65kTL4Cy2su4P8HjtkTxk6VmJDiOPBufWniqQ6wknac6jNiXS9vU+hO3wgusiyqWlzTbVHi0dyJqRONg3w==" + }, + "verror": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", + "integrity": "sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==", + "requires": { + "assert-plus": "^1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "^1.2.0" + } + }, + "vm-browserify": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-1.1.2.tgz", + "integrity": "sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==" + }, + "vsts-npm-auth": { + "version": "0.37.0", + "resolved": "https://registry.npmjs.org/vsts-npm-auth/-/vsts-npm-auth-0.37.0.tgz", + "integrity": "sha512-ct6b0nfGH+yW7rgzKvsnfkIuXmG3GSkP78sECAwGkLR2GZ3Lrof7h7ejK5xSAlmXw+ani9MappMtVfgEdrUeRQ==", + "dev": true + }, + "w3c-hr-time": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz", + "integrity": "sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ==", + "requires": { + "browser-process-hrtime": "^1.0.0" + } + }, + "w3c-xmlserializer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-2.0.0.tgz", + "integrity": "sha512-4tzD0mF8iSiMiNs30BiLO3EpfGLZUT2MSX/G+o7ZywDzliWQ3OPtTZ0PTC3B3ca1UAf4cJMHB+2Bf56EriJuRA==", + "requires": { + "xml-name-validator": "^3.0.0" + } + }, + "wait-for-expect": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/wait-for-expect/-/wait-for-expect-3.0.2.tgz", + "integrity": "sha512-cfS1+DZxuav1aBYbaO/kE06EOS8yRw7qOFoD3XtjTkYvCvh3zUvNST8DXK/nPaeqIzIv3P3kL3lRJn8iwOiSag==", + "dev": true + }, + "walker": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.7.tgz", + "integrity": "sha1-L3+bj9ENZ3JisYqITijRlhjgKPs=", + "requires": { + "makeerror": "1.0.x" + } + }, + "warning": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz", + "integrity": "sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==", + "requires": { + "loose-envify": "^1.0.0" + } + }, + "watchpack": { + "version": "1.7.5", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-1.7.5.tgz", + "integrity": "sha512-9P3MWk6SrKjHsGkLT2KHXdQ/9SNkyoJbabxnKOoJepsvJjJG8uYTR3yTPxPQvNDI3w4Nz1xnE0TLHK4RIVe/MQ==", + "requires": { + "chokidar": "^3.4.1", + "graceful-fs": "^4.1.2", + "neo-async": "^2.5.0", + "watchpack-chokidar2": "^2.0.1" + } + }, + "watchpack-chokidar2": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/watchpack-chokidar2/-/watchpack-chokidar2-2.0.1.tgz", + "integrity": "sha512-nCFfBIPKr5Sh61s4LPpy1Wtfi0HE8isJ3d2Yb5/Ppw2P2B/3eVSEBjKfN0fmHJSK14+31KwMKmcrzs2GM4P0Ww==", + "optional": true, + "requires": { + "chokidar": "^2.1.8" + }, + "dependencies": { + "anymatch": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", + "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", + "optional": true, + "requires": { + "micromatch": "^3.1.4", + "normalize-path": "^2.1.1" + }, + "dependencies": { + "normalize-path": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", + "integrity": "sha512-3pKJwH184Xo/lnH6oyP1q2pMd7HcypqqmRs91/6/i2CGtWwIKGCkOOMTm/zXbgTEWHw1uNpNi/igc3ePOYHb6w==", + "optional": true, + "requires": { + "remove-trailing-separator": "^1.0.1" + } + } + } + }, + "binary-extensions": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz", + "integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==", + "optional": true + }, + "braces": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "optional": true, + "requires": { + "arr-flatten": "^1.1.0", + "array-unique": "^0.3.2", + "extend-shallow": "^2.0.1", + "fill-range": "^4.0.0", + "isobject": "^3.0.1", + "repeat-element": "^1.1.2", + "snapdragon": "^0.8.1", + "snapdragon-node": "^2.0.1", + "split-string": "^3.0.2", + "to-regex": "^3.0.1" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "optional": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "chokidar": { + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz", + "integrity": "sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==", + "optional": true, + "requires": { + "anymatch": "^2.0.0", + "async-each": "^1.0.1", + "braces": "^2.3.2", + "fsevents": "^1.2.7", + "glob-parent": "^3.1.0", + "inherits": "^2.0.3", + "is-binary-path": "^1.0.0", + "is-glob": "^4.0.0", + "normalize-path": "^3.0.0", + "path-is-absolute": "^1.0.0", + "readdirp": "^2.2.1", + "upath": "^1.1.1" + } + }, + "fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha512-VcpLTWqWDiTerugjj8e3+esbg+skS3M9e54UuR3iCeIDMXCLTsAH8hTSzDQU/X6/6t3eYkOKoZSef2PlU6U1XQ==", + "optional": true, + "requires": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "optional": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "fsevents": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.13.tgz", + "integrity": "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==", + "optional": true, + "requires": { + "bindings": "^1.5.0", + "nan": "^2.12.1" + } + }, + "glob-parent": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", + "integrity": "sha512-E8Ak/2+dZY6fnzlR7+ueWvhsH1SjHr4jjss4YS/h4py44jY9MhK/VFdaZJAWDz6BbL21KeteKxFSFpq8OS5gVA==", + "optional": true, + "requires": { + "is-glob": "^3.1.0", + "path-dirname": "^1.0.0" + }, + "dependencies": { + "is-glob": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", + "integrity": "sha512-UFpDDrPgM6qpnFNI+rh/p3bUaq9hKLZN8bMUWzxmcnZVS3omf4IPK+BrewlnWjO1WmUsMYuSjKh4UJuV4+Lqmw==", + "optional": true, + "requires": { + "is-extglob": "^2.1.0" + } + } + } + }, + "is-binary-path": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz", + "integrity": "sha512-9fRVlXc0uCxEDj1nQzaWONSpbTfx0FmJfzHF7pwlI8DkWGoHBBea4Pg5Ky0ojwwxQmnSifgbKkI06Qv0Ljgj+Q==", + "optional": true, + "requires": { + "binary-extensions": "^1.0.0" + } + }, + "is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha512-4cboCqIpliH+mAvFNegjZQ4kgKc3ZUhQVr3HvWbSh5q3WH2v82ct+T2Y1hdU5Gdtorx/cLifQjqCbL7bpznLTg==", + "optional": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "optional": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", + "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "optional": true, + "requires": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "braces": "^2.3.1", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "extglob": "^2.0.4", + "fragment-cache": "^0.2.1", + "kind-of": "^6.0.2", + "nanomatch": "^1.2.9", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.2" + } + }, + "readdirp": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz", + "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==", + "optional": true, + "requires": { + "graceful-fs": "^4.1.11", + "micromatch": "^3.1.10", + "readable-stream": "^2.0.2" + } + }, + "to-regex-range": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", + "integrity": "sha512-ZZWNfCjUokXXDGXFpZehJIkZqq91BcULFq/Pi7M5i4JnxXdhMKAK682z8bCW3o8Hj1wuuzoKcW3DfVzaP6VuNg==", + "optional": true, + "requires": { + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" + } + } + } + }, + "wbuf": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/wbuf/-/wbuf-1.7.3.tgz", + "integrity": "sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA==", + "requires": { + "minimalistic-assert": "^1.0.0" + } + }, + "web-vitals": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/web-vitals/-/web-vitals-0.2.4.tgz", + "integrity": "sha512-6BjspCO9VriYy12z356nL6JBS0GYeEcA457YyRzD+dD6XYCQ75NKhcOHUMHentOE7OcVCIXXDvOm0jKFfQG2Gg==" + }, + "webidl-conversions": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-6.1.0.tgz", + "integrity": "sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w==" + }, + "webpack": { + "version": "4.44.2", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-4.44.2.tgz", + "integrity": "sha512-6KJVGlCxYdISyurpQ0IPTklv+DULv05rs2hseIXer6D7KrUicRDLFb4IUM1S6LUAKypPM/nSiVSuv8jHu1m3/Q==", + "requires": { + "@webassemblyjs/ast": "1.9.0", + "@webassemblyjs/helper-module-context": "1.9.0", + "@webassemblyjs/wasm-edit": "1.9.0", + "@webassemblyjs/wasm-parser": "1.9.0", + "acorn": "^6.4.1", + "ajv": "^6.10.2", + "ajv-keywords": "^3.4.1", + "chrome-trace-event": "^1.0.2", + "enhanced-resolve": "^4.3.0", + "eslint-scope": "^4.0.3", + "json-parse-better-errors": "^1.0.2", + "loader-runner": "^2.4.0", + "loader-utils": "^1.2.3", + "memory-fs": "^0.4.1", + "micromatch": "^3.1.10", + "mkdirp": "^0.5.3", + "neo-async": "^2.6.1", + "node-libs-browser": "^2.2.1", + "schema-utils": "^1.0.0", + "tapable": "^1.1.3", + "terser-webpack-plugin": "^1.4.3", + "watchpack": "^1.7.4", + "webpack-sources": "^1.4.1" + }, + "dependencies": { + "acorn": { + "version": "6.4.2", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.2.tgz", + "integrity": "sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ==" + }, + "braces": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "requires": { + "arr-flatten": "^1.1.0", + "array-unique": "^0.3.2", + "extend-shallow": "^2.0.1", + "fill-range": "^4.0.0", + "isobject": "^3.0.1", + "repeat-element": "^1.1.2", + "snapdragon": "^0.8.1", + "snapdragon-node": "^2.0.1", + "split-string": "^3.0.2", + "to-regex": "^3.0.1" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "cacache": { + "version": "12.0.4", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-12.0.4.tgz", + "integrity": "sha512-a0tMB40oefvuInr4Cwb3GerbL9xTj1D5yg0T5xrjGCGyfvbxseIXX7BAO/u/hIXdafzOI5JC3wDwHyf24buOAQ==", + "requires": { + "bluebird": "^3.5.5", + "chownr": "^1.1.1", + "figgy-pudding": "^3.5.1", + "glob": "^7.1.4", + "graceful-fs": "^4.1.15", + "infer-owner": "^1.0.3", + "lru-cache": "^5.1.1", + "mississippi": "^3.0.0", + "mkdirp": "^0.5.1", + "move-concurrently": "^1.0.1", + "promise-inflight": "^1.0.1", + "rimraf": "^2.6.3", + "ssri": "^6.0.1", + "unique-filename": "^1.1.1", + "y18n": "^4.0.0" + } + }, + "chownr": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==" + }, + "eslint-scope": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-4.0.3.tgz", + "integrity": "sha512-p7VutNr1O/QrxysMo3E45FjYDTeXBy0iTltPFNSqKAIfjDSXC+4dj+qfyuD8bfAXrW/y6lW3O76VaYNPKfpKrg==", + "requires": { + "esrecurse": "^4.1.0", + "estraverse": "^4.1.1" + } + }, + "fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha512-VcpLTWqWDiTerugjj8e3+esbg+skS3M9e54UuR3iCeIDMXCLTsAH8hTSzDQU/X6/6t3eYkOKoZSef2PlU6U1XQ==", + "requires": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha512-4cboCqIpliH+mAvFNegjZQ4kgKc3ZUhQVr3HvWbSh5q3WH2v82ct+T2Y1hdU5Gdtorx/cLifQjqCbL7bpznLTg==", + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-wsl": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz", + "integrity": "sha512-gfygJYZ2gLTDlmbWMI0CE2MwnFzSN/2SZfkMlItC4K/JBlsWVDB0bO6XhqcY13YXE7iMcAJnzTCJjPiTeJJ0Mw==" + }, + "json5": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", + "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", + "requires": { + "minimist": "^1.2.0" + } + }, + "loader-utils": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz", + "integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==", + "requires": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^1.0.1" + } + }, + "lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "requires": { + "yallist": "^3.0.2" + } + }, + "micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", + "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "requires": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "braces": "^2.3.1", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "extglob": "^2.0.4", + "fragment-cache": "^0.2.1", + "kind-of": "^6.0.2", + "nanomatch": "^1.2.9", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.2" + } + }, + "rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "requires": { + "glob": "^7.1.3" + } + }, + "schema-utils": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz", + "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==", + "requires": { + "ajv": "^6.1.0", + "ajv-errors": "^1.0.0", + "ajv-keywords": "^3.1.0" + } + }, + "serialize-javascript": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-4.0.0.tgz", + "integrity": "sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw==", + "requires": { + "randombytes": "^2.1.0" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + }, + "ssri": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-6.0.2.tgz", + "integrity": "sha512-cepbSq/neFK7xB6A50KHN0xHDotYzq58wWCa5LeWqnPrHG8GzfEjO/4O8kpmcGW+oaxkvhEJCWgbgNk4/ZV93Q==", + "requires": { + "figgy-pudding": "^3.5.1" + } + }, + "terser-webpack-plugin": { + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-1.4.5.tgz", + "integrity": "sha512-04Rfe496lN8EYruwi6oPQkG0vo8C+HT49X687FZnpPF0qMAIHONI6HEXYPKDOE8e5HjXTyKfqRd/agHtH0kOtw==", + "requires": { + "cacache": "^12.0.2", + "find-cache-dir": "^2.1.0", + "is-wsl": "^1.1.0", + "schema-utils": "^1.0.0", + "serialize-javascript": "^4.0.0", + "source-map": "^0.6.1", + "terser": "^4.1.2", + "webpack-sources": "^1.4.0", + "worker-farm": "^1.7.0" + } + }, + "to-regex-range": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", + "integrity": "sha512-ZZWNfCjUokXXDGXFpZehJIkZqq91BcULFq/Pi7M5i4JnxXdhMKAK682z8bCW3o8Hj1wuuzoKcW3DfVzaP6VuNg==", + "requires": { + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" + } + }, + "yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==" + } + } + }, + "webpack-dev-middleware": { + "version": "3.7.3", + "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-3.7.3.tgz", + "integrity": "sha512-djelc/zGiz9nZj/U7PTBi2ViorGJXEWo/3ltkPbDyxCXhhEXkW0ce99falaok4TPj+AsxLiXJR0EBOb0zh9fKQ==", + "requires": { + "memory-fs": "^0.4.1", + "mime": "^2.4.4", + "mkdirp": "^0.5.1", + "range-parser": "^1.2.1", + "webpack-log": "^2.0.0" + }, + "dependencies": { + "mime": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.5.2.tgz", + "integrity": "sha512-tqkh47FzKeCPD2PUiPB6pkbMzsCasjxAfC62/Wap5qrUWcb+sFasXUC5I3gYM5iBM8v/Qpn4UK0x+j0iHyFPDg==" + } + } + }, + "webpack-dev-server": { + "version": "3.11.1", + "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-3.11.1.tgz", + "integrity": "sha512-u4R3mRzZkbxQVa+MBWi2uVpB5W59H3ekZAJsQlKUTdl7Elcah2EhygTPLmeFXybQkf9i2+L0kn7ik9SnXa6ihQ==", + "requires": { + "ansi-html": "0.0.7", + "bonjour": "^3.5.0", + "chokidar": "^2.1.8", + "compression": "^1.7.4", + "connect-history-api-fallback": "^1.6.0", + "debug": "^4.1.1", + "del": "^4.1.1", + "express": "^4.17.1", + "html-entities": "^1.3.1", + "http-proxy-middleware": "0.19.1", + "import-local": "^2.0.0", + "internal-ip": "^4.3.0", + "ip": "^1.1.5", + "is-absolute-url": "^3.0.3", + "killable": "^1.0.1", + "loglevel": "^1.6.8", + "opn": "^5.5.0", + "p-retry": "^3.0.1", + "portfinder": "^1.0.26", + "schema-utils": "^1.0.0", + "selfsigned": "^1.10.8", + "semver": "^6.3.0", + "serve-index": "^1.9.1", + "sockjs": "^0.3.21", + "sockjs-client": "^1.5.0", + "spdy": "^4.0.2", + "strip-ansi": "^3.0.1", + "supports-color": "^6.1.0", + "url": "^0.11.0", + "webpack-dev-middleware": "^3.7.2", + "webpack-log": "^2.0.0", + "ws": "^6.2.1", + "yargs": "^13.3.2" + }, + "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==" + }, + "anymatch": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", + "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", + "requires": { + "micromatch": "^3.1.4", + "normalize-path": "^2.1.1" + }, + "dependencies": { + "normalize-path": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", + "integrity": "sha512-3pKJwH184Xo/lnH6oyP1q2pMd7HcypqqmRs91/6/i2CGtWwIKGCkOOMTm/zXbgTEWHw1uNpNi/igc3ePOYHb6w==", + "requires": { + "remove-trailing-separator": "^1.0.1" + } + } + } + }, + "binary-extensions": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz", + "integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==" + }, + "braces": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "requires": { + "arr-flatten": "^1.1.0", + "array-unique": "^0.3.2", + "extend-shallow": "^2.0.1", + "fill-range": "^4.0.0", + "isobject": "^3.0.1", + "repeat-element": "^1.1.2", + "snapdragon": "^0.8.1", + "snapdragon-node": "^2.0.1", + "split-string": "^3.0.2", + "to-regex": "^3.0.1" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==" + }, + "chokidar": { + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz", + "integrity": "sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==", + "requires": { + "anymatch": "^2.0.0", + "async-each": "^1.0.1", + "braces": "^2.3.2", + "fsevents": "^1.2.7", + "glob-parent": "^3.1.0", + "inherits": "^2.0.3", + "is-binary-path": "^1.0.0", + "is-glob": "^4.0.0", + "normalize-path": "^3.0.0", + "path-is-absolute": "^1.0.0", + "readdirp": "^2.2.1", + "upath": "^1.1.1" + } + }, + "cliui": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", + "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", + "requires": { + "string-width": "^3.1.0", + "strip-ansi": "^5.2.0", + "wrap-ansi": "^5.1.0" + }, + "dependencies": { + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==" + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "requires": { + "ansi-regex": "^4.1.0" + } + } + } + }, + "emoji-regex": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==" + }, + "fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha512-VcpLTWqWDiTerugjj8e3+esbg+skS3M9e54UuR3iCeIDMXCLTsAH8hTSzDQU/X6/6t3eYkOKoZSef2PlU6U1XQ==", + "requires": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "requires": { + "locate-path": "^3.0.0" + } + }, + "fsevents": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.13.tgz", + "integrity": "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==", + "optional": true, + "requires": { + "bindings": "^1.5.0", + "nan": "^2.12.1" + } + }, + "glob-parent": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", + "integrity": "sha512-E8Ak/2+dZY6fnzlR7+ueWvhsH1SjHr4jjss4YS/h4py44jY9MhK/VFdaZJAWDz6BbL21KeteKxFSFpq8OS5gVA==", + "requires": { + "is-glob": "^3.1.0", + "path-dirname": "^1.0.0" + }, + "dependencies": { + "is-glob": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", + "integrity": "sha512-UFpDDrPgM6qpnFNI+rh/p3bUaq9hKLZN8bMUWzxmcnZVS3omf4IPK+BrewlnWjO1WmUsMYuSjKh4UJuV4+Lqmw==", + "requires": { + "is-extglob": "^2.1.0" + } + } + } + }, + "import-local": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-2.0.0.tgz", + "integrity": "sha512-b6s04m3O+s3CGSbqDIyP4R6aAwAeYlVq9+WUWep6iHa8ETRf9yei1U48C5MmfJmV9AiLYYBKPMq/W+/WRpQmCQ==", + "requires": { + "pkg-dir": "^3.0.0", + "resolve-cwd": "^2.0.0" + } + }, + "is-absolute-url": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-absolute-url/-/is-absolute-url-3.0.3.tgz", + "integrity": "sha512-opmNIX7uFnS96NtPmhWQgQx6/NYFgsUXYMllcfzwWKUMwfo8kku1TvE6hkNcH+Q1ts5cMVrsY7j0bxXQDciu9Q==" + }, + "is-binary-path": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz", + "integrity": "sha512-9fRVlXc0uCxEDj1nQzaWONSpbTfx0FmJfzHF7pwlI8DkWGoHBBea4Pg5Ky0ojwwxQmnSifgbKkI06Qv0Ljgj+Q==", + "requires": { + "binary-extensions": "^1.0.0" + } + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==" + }, + "is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha512-4cboCqIpliH+mAvFNegjZQ4kgKc3ZUhQVr3HvWbSh5q3WH2v82ct+T2Y1hdU5Gdtorx/cLifQjqCbL7bpznLTg==", + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "requires": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + } + }, + "micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", + "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "requires": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "braces": "^2.3.1", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "extglob": "^2.0.4", + "fragment-cache": "^0.2.1", + "kind-of": "^6.0.2", + "nanomatch": "^1.2.9", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.2" + } + }, + "p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "requires": { + "p-limit": "^2.0.0" + } + }, + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==" + }, + "readdirp": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz", + "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==", + "requires": { + "graceful-fs": "^4.1.11", + "micromatch": "^3.1.10", + "readable-stream": "^2.0.2" + } + }, + "resolve-cwd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-2.0.0.tgz", + "integrity": "sha512-ccu8zQTrzVr954472aUVPLEcB3YpKSYR3cg/3lo1okzobPBM+1INXBbBZlDbnI/hbEocnf8j0QVo43hQKrbchg==", + "requires": { + "resolve-from": "^3.0.0" + } + }, + "resolve-from": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz", + "integrity": "sha512-GnlH6vxLymXJNMBo7XP1fJIzBFbdYt49CuTwmB/6N53t+kMPRMFKz783LlQ4tv28XoQfMWinAJX6WCGf2IlaIw==" + }, + "schema-utils": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz", + "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==", + "requires": { + "ajv": "^6.1.0", + "ajv-errors": "^1.0.0", + "ajv-keywords": "^3.1.0" + } + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" + }, + "string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "requires": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + }, + "dependencies": { + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==" + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "requires": { + "ansi-regex": "^4.1.0" + } + } + } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==", + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "requires": { + "has-flag": "^3.0.0" + } + }, + "to-regex-range": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", + "integrity": "sha512-ZZWNfCjUokXXDGXFpZehJIkZqq91BcULFq/Pi7M5i4JnxXdhMKAK682z8bCW3o8Hj1wuuzoKcW3DfVzaP6VuNg==", + "requires": { + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" + } + }, + "wrap-ansi": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", + "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", + "requires": { + "ansi-styles": "^3.2.0", + "string-width": "^3.0.0", + "strip-ansi": "^5.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==" + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "requires": { + "ansi-regex": "^4.1.0" + } + } + } + }, + "ws": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.1.tgz", + "integrity": "sha512-GIyAXC2cB7LjvpgMt9EKS2ldqr0MTrORaleiOno6TweZ6r3TKtoFQWay/2PceJ3RuBasOHzXNn5Lrw1X0bEjqA==", + "requires": { + "async-limiter": "~1.0.0" + } + }, + "yargs": { + "version": "13.3.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz", + "integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==", + "requires": { + "cliui": "^5.0.0", + "find-up": "^3.0.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^3.0.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^13.1.2" + } + }, + "yargs-parser": { + "version": "13.1.2", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz", + "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==", + "requires": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + } + } + } + }, + "webpack-log": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/webpack-log/-/webpack-log-2.0.0.tgz", + "integrity": "sha512-cX8G2vR/85UYG59FgkoMamwHUIkSSlV3bBMRsbxVXVUk2j6NleCKjQ/WE9eYg9WY4w25O9w8wKP4rzNZFmUcUg==", + "requires": { + "ansi-colors": "^3.0.0", + "uuid": "^3.3.2" + }, + "dependencies": { + "ansi-colors": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-3.2.4.tgz", + "integrity": "sha512-hHUXGagefjN2iRrID63xckIvotOXOojhQKWIPUZ4mNUZ9nLZW+7FMNoE1lOkEhNWYsx/7ysGIuJYCiMAA9FnrA==" + }, + "uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==" + } + } + }, + "webpack-manifest-plugin": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/webpack-manifest-plugin/-/webpack-manifest-plugin-2.2.0.tgz", + "integrity": "sha512-9S6YyKKKh/Oz/eryM1RyLVDVmy3NSPV0JXMRhZ18fJsq+AwGxUY34X54VNwkzYcEmEkDwNxuEOboCZEebJXBAQ==", + "requires": { + "fs-extra": "^7.0.0", + "lodash": ">=3.5 <5", + "object.entries": "^1.1.0", + "tapable": "^1.0.0" + }, + "dependencies": { + "fs-extra": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz", + "integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==", + "requires": { + "graceful-fs": "^4.1.2", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + } + }, + "jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", + "requires": { + "graceful-fs": "^4.1.6" + } + }, + "universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==" + } + } + }, + "webpack-sources": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.4.3.tgz", + "integrity": "sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ==", + "requires": { + "source-list-map": "^2.0.0", + "source-map": "~0.6.1" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + } + } + }, + "websocket-driver": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz", + "integrity": "sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==", + "requires": { + "http-parser-js": ">=0.5.1", + "safe-buffer": ">=5.1.0", + "websocket-extensions": ">=0.1.1" + } + }, + "websocket-extensions": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz", + "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==" + }, + "whatwg-encoding": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz", + "integrity": "sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw==", + "requires": { + "iconv-lite": "0.4.24" + } + }, + "whatwg-fetch": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.6.2.tgz", + "integrity": "sha512-bJlen0FcuU/0EMLrdbJ7zOnW6ITZLrZMIarMUVmdKtsGvZna8vxKYaexICWPfZ8qwf9fzNq+UEIZrnSaApt6RA==" + }, + "whatwg-mimetype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz", + "integrity": "sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g==" + }, + "whatwg-url": { + "version": "8.5.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-8.5.0.tgz", + "integrity": "sha512-fy+R77xWv0AiqfLl4nuGUlQ3/6b5uNfQ4WAbGQVMYshCTCCPK9psC1nWh3XHuxGVCtlcDDQPQW1csmmIQo+fwg==", + "requires": { + "lodash": "^4.7.0", + "tr46": "^2.0.2", + "webidl-conversions": "^6.1.0" + } + }, + "which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "requires": { + "isexe": "^2.0.0" + } + }, + "which-boxed-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", + "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", + "requires": { + "is-bigint": "^1.0.1", + "is-boolean-object": "^1.1.0", + "is-number-object": "^1.0.4", + "is-string": "^1.0.5", + "is-symbol": "^1.0.3" + } + }, + "which-module": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", + "integrity": "sha512-B+enWhmw6cjfVC7kS8Pj9pCrKSc5txArRyaYGe088shv/FGWH+0Rjx/xPgtsWfsUtS27FkP697E4DDhgrgoc0Q==" + }, + "wide-align": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", + "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", + "dev": true, + "requires": { + "string-width": "^1.0.2 || 2" + }, + "dependencies": { + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true + }, + "string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "dev": true, + "requires": { + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" + } + }, + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "dev": true, + "requires": { + "ansi-regex": "^3.0.0" + } + } + } + }, + "wmf": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wmf/-/wmf-1.0.2.tgz", + "integrity": "sha512-/p9K7bEh0Dj6WbXg4JG0xvLQmIadrner1bi45VMJTfnbVHsc7yIajZyoSoK60/dtVBs12Fm6WkUI5/3WAVsNMw==" + }, + "word": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/word/-/word-0.3.0.tgz", + "integrity": "sha512-OELeY0Q61OXpdUfTp+oweA/vtLVg5VDOXh+3he3PNzLGG/y0oylSOC1xRVj0+l4vQ3tj/bB1HVHv1ocXkQceFA==" + }, + "word-wrap": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", + "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==" + }, + "wordwrap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==", + "dev": true + }, + "workbox-background-sync": { + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/workbox-background-sync/-/workbox-background-sync-5.1.4.tgz", + "integrity": "sha512-AH6x5pYq4vwQvfRDWH+vfOePfPIYQ00nCEB7dJRU1e0n9+9HMRyvI63FlDvtFT2AvXVRsXvUt7DNMEToyJLpSA==", + "requires": { + "workbox-core": "^5.1.4" + } + }, + "workbox-broadcast-update": { + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/workbox-broadcast-update/-/workbox-broadcast-update-5.1.4.tgz", + "integrity": "sha512-HTyTWkqXvHRuqY73XrwvXPud/FN6x3ROzkfFPsRjtw/kGZuZkPzfeH531qdUGfhtwjmtO/ZzXcWErqVzJNdXaA==", + "requires": { + "workbox-core": "^5.1.4" + } + }, + "workbox-build": { + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/workbox-build/-/workbox-build-5.1.4.tgz", + "integrity": "sha512-xUcZn6SYU8usjOlfLb9Y2/f86Gdo+fy1fXgH8tJHjxgpo53VVsqRX0lUDw8/JuyzNmXuo8vXX14pXX2oIm9Bow==", + "requires": { + "@babel/core": "^7.8.4", + "@babel/preset-env": "^7.8.4", + "@babel/runtime": "^7.8.4", + "@hapi/joi": "^15.1.0", + "@rollup/plugin-node-resolve": "^7.1.1", + "@rollup/plugin-replace": "^2.3.1", + "@surma/rollup-plugin-off-main-thread": "^1.1.1", + "common-tags": "^1.8.0", + "fast-json-stable-stringify": "^2.1.0", + "fs-extra": "^8.1.0", + "glob": "^7.1.6", + "lodash.template": "^4.5.0", + "pretty-bytes": "^5.3.0", + "rollup": "^1.31.1", + "rollup-plugin-babel": "^4.3.3", + "rollup-plugin-terser": "^5.3.1", + "source-map": "^0.7.3", + "source-map-url": "^0.4.0", + "stringify-object": "^3.3.0", + "strip-comments": "^1.0.2", + "tempy": "^0.3.0", + "upath": "^1.2.0", + "workbox-background-sync": "^5.1.4", + "workbox-broadcast-update": "^5.1.4", + "workbox-cacheable-response": "^5.1.4", + "workbox-core": "^5.1.4", + "workbox-expiration": "^5.1.4", + "workbox-google-analytics": "^5.1.4", + "workbox-navigation-preload": "^5.1.4", + "workbox-precaching": "^5.1.4", + "workbox-range-requests": "^5.1.4", + "workbox-routing": "^5.1.4", + "workbox-strategies": "^5.1.4", + "workbox-streams": "^5.1.4", + "workbox-sw": "^5.1.4", + "workbox-window": "^5.1.4" + }, + "dependencies": { + "fs-extra": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", + "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", + "requires": { + "graceful-fs": "^4.2.0", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + } + }, + "jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", + "requires": { + "graceful-fs": "^4.1.6" + } + }, + "source-map": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", + "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==" + }, + "universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==" + } + } + }, + "workbox-cacheable-response": { + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/workbox-cacheable-response/-/workbox-cacheable-response-5.1.4.tgz", + "integrity": "sha512-0bfvMZs0Of1S5cdswfQK0BXt6ulU5kVD4lwer2CeI+03czHprXR3V4Y8lPTooamn7eHP8Iywi5QjyAMjw0qauA==", + "requires": { + "workbox-core": "^5.1.4" + } + }, + "workbox-core": { + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/workbox-core/-/workbox-core-5.1.4.tgz", + "integrity": "sha512-+4iRQan/1D8I81nR2L5vcbaaFskZC2CL17TLbvWVzQ4qiF/ytOGF6XeV54pVxAvKUtkLANhk8TyIUMtiMw2oDg==" + }, + "workbox-expiration": { + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/workbox-expiration/-/workbox-expiration-5.1.4.tgz", + "integrity": "sha512-oDO/5iC65h2Eq7jctAv858W2+CeRW5e0jZBMNRXpzp0ZPvuT6GblUiHnAsC5W5lANs1QS9atVOm4ifrBiYY7AQ==", + "requires": { + "workbox-core": "^5.1.4" + } + }, + "workbox-google-analytics": { + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/workbox-google-analytics/-/workbox-google-analytics-5.1.4.tgz", + "integrity": "sha512-0IFhKoEVrreHpKgcOoddV+oIaVXBFKXUzJVBI+nb0bxmcwYuZMdteBTp8AEDJacENtc9xbR0wa9RDCnYsCDLjA==", + "requires": { + "workbox-background-sync": "^5.1.4", + "workbox-core": "^5.1.4", + "workbox-routing": "^5.1.4", + "workbox-strategies": "^5.1.4" + } + }, + "workbox-navigation-preload": { + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/workbox-navigation-preload/-/workbox-navigation-preload-5.1.4.tgz", + "integrity": "sha512-Wf03osvK0wTflAfKXba//QmWC5BIaIZARU03JIhAEO2wSB2BDROWI8Q/zmianf54kdV7e1eLaIEZhth4K4MyfQ==", + "requires": { + "workbox-core": "^5.1.4" + } + }, + "workbox-precaching": { + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/workbox-precaching/-/workbox-precaching-5.1.4.tgz", + "integrity": "sha512-gCIFrBXmVQLFwvAzuGLCmkUYGVhBb7D1k/IL7pUJUO5xacjLcFUaLnnsoVepBGAiKw34HU1y/YuqvTKim9qAZA==", + "requires": { + "workbox-core": "^5.1.4" + } + }, + "workbox-range-requests": { + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/workbox-range-requests/-/workbox-range-requests-5.1.4.tgz", + "integrity": "sha512-1HSujLjgTeoxHrMR2muDW2dKdxqCGMc1KbeyGcmjZZAizJTFwu7CWLDmLv6O1ceWYrhfuLFJO+umYMddk2XMhw==", + "requires": { + "workbox-core": "^5.1.4" + } + }, + "workbox-routing": { + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/workbox-routing/-/workbox-routing-5.1.4.tgz", + "integrity": "sha512-8ljknRfqE1vEQtnMtzfksL+UXO822jJlHTIR7+BtJuxQ17+WPZfsHqvk1ynR/v0EHik4x2+826Hkwpgh4GKDCw==", + "requires": { + "workbox-core": "^5.1.4" + } + }, + "workbox-strategies": { + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/workbox-strategies/-/workbox-strategies-5.1.4.tgz", + "integrity": "sha512-VVS57LpaJTdjW3RgZvPwX0NlhNmscR7OQ9bP+N/34cYMDzXLyA6kqWffP6QKXSkca1OFo/v6v7hW7zrrguo6EA==", + "requires": { + "workbox-core": "^5.1.4", + "workbox-routing": "^5.1.4" + } + }, + "workbox-streams": { + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/workbox-streams/-/workbox-streams-5.1.4.tgz", + "integrity": "sha512-xU8yuF1hI/XcVhJUAfbQLa1guQUhdLMPQJkdT0kn6HP5CwiPOGiXnSFq80rAG4b1kJUChQQIGPrq439FQUNVrw==", + "requires": { + "workbox-core": "^5.1.4", + "workbox-routing": "^5.1.4" + } + }, + "workbox-sw": { + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/workbox-sw/-/workbox-sw-5.1.4.tgz", + "integrity": "sha512-9xKnKw95aXwSNc8kk8gki4HU0g0W6KXu+xks7wFuC7h0sembFnTrKtckqZxbSod41TDaGh+gWUA5IRXrL0ECRA==" + }, + "workbox-webpack-plugin": { + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/workbox-webpack-plugin/-/workbox-webpack-plugin-5.1.4.tgz", + "integrity": "sha512-PZafF4HpugZndqISi3rZ4ZK4A4DxO8rAqt2FwRptgsDx7NF8TVKP86/huHquUsRjMGQllsNdn4FNl8CD/UvKmQ==", + "requires": { + "@babel/runtime": "^7.5.5", + "fast-json-stable-stringify": "^2.0.0", + "source-map-url": "^0.4.0", + "upath": "^1.1.2", + "webpack-sources": "^1.3.0", + "workbox-build": "^5.1.4" + } + }, + "workbox-window": { + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/workbox-window/-/workbox-window-5.1.4.tgz", + "integrity": "sha512-vXQtgTeMCUq/4pBWMfQX8Ee7N2wVC4Q7XYFqLnfbXJ2hqew/cU1uMTD2KqGEgEpE4/30luxIxgE+LkIa8glBYw==", + "requires": { + "workbox-core": "^5.1.4" + } + }, + "worker-farm": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/worker-farm/-/worker-farm-1.7.0.tgz", + "integrity": "sha512-rvw3QTZc8lAxyVrqcSGVm5yP/IJ2UcB3U0graE3LCFoZ0Yn2x4EoVSqJKdB/T5M+FLcRPjz4TDacRf3OCfNUzw==", + "requires": { + "errno": "~0.1.7" + } + }, + "worker-rpc": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/worker-rpc/-/worker-rpc-0.1.1.tgz", + "integrity": "sha512-P1WjMrUB3qgJNI9jfmpZ/htmBEjFh//6l/5y8SD9hg1Ef5zTTVVoRjTrTEzPrNBQvmhMxkoTsjOXN10GWU7aCg==", + "requires": { + "microevent.ts": "~0.1.1" + } + }, + "wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "requires": { + "color-convert": "^2.0.1" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + } + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" + }, + "write-file-atomic": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", + "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", + "requires": { + "imurmurhash": "^0.1.4", + "is-typedarray": "^1.0.0", + "signal-exit": "^3.0.2", + "typedarray-to-buffer": "^3.1.5" + } + }, + "ws": { + "version": "7.4.5", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.5.tgz", + "integrity": "sha512-xzyu3hFvomRfXKH8vOFMU3OguG6oOvhXMo3xsGy3xWExqaM2dxBbVxuD99O7m3ZUFMvvscsZDqxfgMaRr/Nr1g==" + }, + "xlsx": { + "version": "0.17.4", + "resolved": "https://registry.npmjs.org/xlsx/-/xlsx-0.17.4.tgz", + "integrity": "sha512-9aKt8g9ZLP0CUdBX8L5xnoMDFwSiLI997eQnDThCaqQMYB9AEBIRzblSSNN/ICMGLYIHUO3VKaItcedZJ3ijIg==", + "requires": { + "adler-32": "~1.2.0", + "cfb": "^1.1.4", + "codepage": "~1.15.0", + "crc-32": "~1.2.0", + "ssf": "~0.11.2", + "wmf": "~1.0.1", + "word": "~0.3.0" + } + }, + "xml": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/xml/-/xml-1.0.1.tgz", + "integrity": "sha512-huCv9IH9Tcf95zuYCsQraZtWnJvBtLVE0QHMOs8bWyZAFZNDcYjsPq1nEx8jKA9y+Beo9v+7OBPRisQTjinQMw==", + "dev": true + }, + "xml-name-validator": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-3.0.0.tgz", + "integrity": "sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==" + }, + "xmlchars": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", + "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==" + }, + "xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==" + }, + "y18n": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", + "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==" + }, + "yallist": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", + "integrity": "sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A==", + "dev": true + }, + "yaml": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==" + }, + "yargs": { + "version": "15.4.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", + "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", + "requires": { + "cliui": "^6.0.0", + "decamelize": "^1.2.0", + "find-up": "^4.1.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^4.2.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^18.1.2" + } + }, + "yargs-parser": { + "version": "18.1.3", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", + "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", + "requires": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + }, + "dependencies": { + "camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==" + } + } + }, + "yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==" + } + } +} diff --git a/LCU.WebApps.Operations.IncentiveManager/package.json b/LCU.WebApps.Operations.IncentiveManager/package.json new file mode 100644 index 0000000..93eeafb --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/package.json @@ -0,0 +1,191 @@ +{ + "name": "incentive-manager", + "version": "0.1.4", + "private": true, + "homepage": "/", + "dependencies": { + "@azure/msal-browser": "^2.14.2", + "@azure/msal-react": "^1.0.0", + "@fortawesome/fontawesome-svg-core": "^1.2.36", + "@fortawesome/free-solid-svg-icons": "^5.15.4", + "@fortawesome/react-fontawesome": "^0.1.15", + "@types/node": "^12.19.1", + "@types/react": "^16.9.53", + "@types/react-datepicker": "^3.1.1", + "@types/react-dom": "^16.9.8", + "@types/react-router-dom": "^5.1.6", + "@types/react-toggle": "^4.0.2", + "@types/styled-components": "^5.1.26", + "@types/xlsx": "0.0.36", + "axios": "^0.21.1", + "bootstrap": "^4.5.3", + "connected-react-router": "^6.8.0", + "debounce": "^1.2.1", + "file-saver": "^2.0.5", + "font-awesome": "^4.7.0", + "immutable": "^3.8.1", + "jquery": "^3.5.1", + "lodash.clonedeep": "^4.5.0", + "lodash.pick": "^4.4.0", + "lodash.throttle": "^4.1.1", + "moment": "^2.29.1", + "popper.js": "^1.16.1", + "react": "^16.14.0", + "react-accessible-accordion": "^3.3.3", + "react-app-polyfill": "^1.0.6", + "react-datepicker": "^2.9.6", + "react-dom": "^16.14.0", + "react-multi-select-component": "2.0.14", + "react-redux": "^7.2.1", + "react-router-dom": "^5.2.0", + "react-scripts": "^4.0.3", + "react-select": "^5.2.1", + "react-toastify": "^6.0.9", + "react-toggle": "^4.1.1", + "reactstrap": "^8.9.0", + "redux": "^4.0.5", + "redux-thunk": "^2.3.0", + "reselect": "^4.0.0", + "seamless-immutable": "^7.1.3", + "typescript": "^4.0.1", + "web-vitals": "^0.2.4", + "xlsx": "^0.17.4" + }, + "scripts": { + "start": "react-scripts start", + "start-https": "set HTTPS=true&&set SSL_CRT_FILE=.cert/cert.pem&&set SSL_KEY_FILE=.cert/key.pem&&react-scripts start", + "build": "react-scripts build", + "test": "react-scripts test", + "test-ci": "react-scripts test --watchAll=false --reporters=default --reporters=jest-junit", + "eject": "react-scripts eject", + "postinstall": "node isDockerEnv.js || node version.js", + "initcz": "commitizen init cz-conventional-changelog --save-dev --save-exact", + "commitmsg": "validate-commit-msg", + "release": "standard-version", + "test:coverage": "set CI=true && react-scripts test --coverage --reporters ./node_modules/jest-junit", + "refreshVSToken": "vsts-npm-auth -config .npmrc" + }, + "eslintConfig": { + "extends": [ + "react-app", + "react-app/jest" + ] + }, + "browserslist": { + "production": [ + ">0.2%", + "not dead", + "not op_mini all" + ], + "development": [ + "last 1 chrome version", + "last 1 firefox version", + "last 1 safari version" + ] + }, + "devDependencies": { + "@babel/plugin-transform-arrow-functions": "^7.2.0", + "@testing-library/jest-dom": "^4.2.4", + "@testing-library/react": "^9.5.0", + "@testing-library/user-event": "^7.2.1", + "@types/debounce": "^1.2.0", + "@types/enzyme": "^3.9.3", + "@types/enzyme-adapter-react-16": "^1.0.5", + "@types/faker": "^4.1.8", + "@types/file-saver": "^2.0.2", + "@types/jest": "^26.0.15", + "@types/lodash.clonedeep": "^4.5.6", + "@types/lodash.pick": "^4.4.6", + "@types/lodash.throttle": "^4.1.6", + "@types/moxios": "^0.4.8", + "@types/react-modal": "^3.10.6", + "@types/react-redux": "^7.0.9", + "@types/redux-thunk": "^2.1.0", + "@typescript-eslint/eslint-plugin": "^4.5.0", + "@typescript-eslint/parser": "^4.5.0", + "commitizen": "^4.2.2", + "cz-conventional-changelog": "^2.1.0", + "enzyme": "^3.10.0", + "enzyme-adapter-react-16": "^1.15.5", + "eslint": "^7.12.0", + "eslint-config-airbnb": "^18.2.0", + "eslint-config-prettier": "^6.14.0", + "eslint-config-react": "^1.1.7", + "eslint-plugin-import": "^2.21.2", + "eslint-plugin-jsx-a11y": "^6.3.0", + "eslint-plugin-prettier": "^3.1.4", + "eslint-plugin-react": "^7.20.0", + "eslint-plugin-react-hooks": "^4.0.0", + "git-describe": "^4.0.4", + "husky": "^2.7.0", + "jest": "^26.6.0", + "jest-junit": "^10.0.0", + "node-sass": "^4.14.1", + "prettier": "^2.1.2", + "pretty-quick": "^3.1.0", + "redux-devtools-extension": "^2.13.8", + "standard-version": "^9.0.0", + "ts-jest": "^26.4.2", + "validate-commit-msg": "^2.14.0", + "vsts-npm-auth": "^0.37.0" + }, + "husky": { + "hooks": { + "applypatch-msg": "echo \"[Husky] applypatch-msg\"", + "pre-applypatch": "echo \"[Husky] pre-applypatch\"", + "post-applypatch": "echo \"[Husky] post-applypatch\"", + "pre-commit": "pretty-quick --staged", + "pre-merge-commit": "echo \"[Husky] pre-merge-commit\"", + "prepare-commit-msg": "echo \"[Husky] prepare-commit-msg\"", + "commit-msg": "echo \"[Husky] commit-msg\"", + "post-commit": "echo \"[Husky] post-commit\"", + "pre-rebase": "echo \"[Husky] pre-rebase\"", + "post-checkout": "echo \"[Husky] post-checkout\"", + "post-merge": "echo \"[Husky] post-merge\"", + "pre-push": "echo \"[Husky] pre-push\"", + "pre-receive": "echo \"[Husky] pre-receive\"", + "update": "echo \"[Husky] update\"", + "post-receive": "echo \"[Husky] post-receive\"", + "post-update": "echo \"[Husky] post-update\"", + "reference-transaction": "echo \"[Husky] reference-transaction\"", + "push-to-checkout": "echo \"[Husky] push-to-checkout\"", + "pre-auto-gc": "echo \"[Husky] pre-auto-gc\"", + "post-rewrite": "echo \"[Husky] post-rewrite\"", + "sendemail-validate": "echo \"[Husky] sendemail-validate\"", + "fsmonitor-watchman": "echo \"[Husky] fsmonitor-watchman\"", + "p4-changelist": "echo \"[Husky] p4-changelist\"", + "p4-prepare-changelist": "echo \"[Husky] p4-prepare-changelist\"", + "p4-post-changelist": "echo \"[Husky] p4-post-changelist\"", + "p4-pre-submit": "echo \"[Husky] p4-pre-submit\"", + "post-index-change": "echo \"[Husky] post-index-change\"" + } + }, + "config": { + "commitizen": { + "path": "./node_modules/cz-conventional-changelog" + }, + "validate-commit-msg": { + "types": [ + "feat", + "fix", + "docs", + "style", + "refactor", + "perf", + "test", + "chore", + "revert" + ], + "warnOnFail": false, + "maxSubjectLength": 100, + "subjectPattern": "^.*", + "subjectPatternErrorMsg": "Subject must be in format 'Commit message'", + "helpMessage": "" + } + }, + "jest-junit": { + "suiteName:": "jest tests", + "outputDirectory": "./junit", + "outputName": "junit.xml" + } +} diff --git a/LCU.WebApps.Operations.IncentiveManager/public/favicon.ico b/LCU.WebApps.Operations.IncentiveManager/public/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..615cb3a85a54db8a6e186f94e772bf5e561da15c GIT binary patch literal 15406 zcmeHO2~?HU7Cx6zDpL`gmMQOr)2r69tPIN{?OEi=Bob)i^qMp?OzEW}IFz$O13i&R z29a4wlo^yXz$uWNEyo;QIaH+Le&7D#zx?-dy%Tt2t=GTS=G_1M=bZhWea_kEoV_&CKi-uUuMniVmjm3M3m?ZzZsc5kWZC^Q#c6~LK$|EMzwF@Vy zI%BDnpEq|ft@jr6*)ZX&qyBFG#*{e5j<(Hxi89}DA%04hjh6D|?`ubiFn#s8Tz=@Q z>iXQ4T$fW*>i=@`0BxH7nA!C&9WzNK~}QgR-XfrX)WHTIV6=YtKK(wNg39 z`1jV2Uy#Z-a>pRh$6Q}8|Pb`~vGTw=H$A6^mlal|*iX7xyslREr0seKpoJ4y={9`@EI_En+!s3vQTnun5qO~w-B>+65Z`E!42GFR#3UllhS z`FFPe`uLYeOtcXHm1;gL{~x}|pcCb3#QTyZ`d^*C1ban0@Zz(KI~^^Iqa&DmTV}gT zdCEDb{M{P!kK3z^`H)g4J*4J;^N%`W%6z-0Mfh)DJr4W-6KejF{<(FFHjH(&2>$|{ zA6f3Q{F(oa-+!Y7ADw%$8dGb@fAa@V(vH>RHTYk6YUwe$TwfLt^ewZqwgEaVG zI=V;QPvx9b{%$S)iDTN){?x@9{Lkzyuvq_jU#rP#(1Ei4Jyx1*vHr764>axpGXL7m zO?!e;=E~o#C4X+SCTq3S->+vqE9pL76G~pXezzWf6&=WqpyE%*;2b>&XK^>2-*xN0 zgweLRBiTsUT93b=!}+nei#p)k{|`Et8%enzy`b)Ya=^X7=0*FG!)R~Pd`fwvK`*r? z|LTloxIf%YS?~6SKAr{L+k;A$j;4JZ77_2mInWX7eKgMkt;xS=(J0)Zo}r3$L3D6S zG_g)9UgC@U)Fhph{;%$>rhj;D6@B7?F@Ks$mW-ydm4TEtr5ly47%R<(w8`Cwb(E6$ zZpQsL;`}FY{*2N!f0cR3{`XM+e0R#m_*Z8vrw!vA!LKXr2oIFTU~f_=uz6$boz(LJ zce6Uzt_IJ)0yuAb{#9b&@jX%yhkL7wnuq*7!N(e7@&v|b25k)NqMrXgk2bf@XrD&W z#usushSK6sHSI43%KsW-#Qi7HTLcPyk;BDZ<`T=&5Rd=& z?`$W5iB^mUnPDeEE$LC=FWX)_^>VWB^=wxgH`nfV9s_!H3K-C%^Y{Th90Ofl94EAd zxQ*v}c7Ege9{2k7>ux{v5hvSzxXgD2Prg&LZG|zI^Kd=eo(~D9(3fo|gb%k)i5y|I zcde)W@n}D%^U<&0cd0dL-5AGnk)!MmtQu;a6a1phj6PjN5AeSSWNi+3)UkS{t8o0! zNb3mL7=FQ8y#+E>FSMlGtvD~$m5AZCF^&!nJPy3pOdbPw7hxYY%4#|=|0?6&Hg^bp zpYugCI+VK>Hm2yN{LLr>ImcB1c71Y{rt(8+E^8UtfW8uWOW*+hlvM zk{`z5$B;ob1AyOH8zYKJk1A@Icpf^sPTAh=es7yIn2wiiY_PE@=2%s1um=9RD;IG` zeFYfx-w73be*1j5)l?v29b(EHvWoGt_5*&S=6vQmedt_uDR60?O||j=_TRI#W!8XZ z^Wum-}wC+oyOkAY3-&6qh-(aEs3A^ zRKAxgbtB`ycK!#-4;v<>RdMX&F;4cD#-AVZD(y>}r(s`V{8xWEL0hpuU;XJgm51y1 zMJUJ4()e?No}y~_uoB_Rl5sHpx+|CHz@~61EnGOF&B{M(T7%6c>Giub@DqFh^*62) zr(e(P-)8Z1oI!QQO6*S^H1R7=aOqS2-e&Qq!_LL{HwHG@ws`)?PyO}llLB? z=g%$dy&Ml`itgvNVcGd3q*x8M@5t*?#$SK)2I451)`k4tP-?=$idf*t_u<3rBgKwnPkR*SHdn~st(kpG?x&oxe=9-Z-?X&+ zE25^;o`m_ZQ9XxPl)e(~ym=fC>JFb@XZ5(qc;%GsE(Kw)Nj_Tk*~MqeoJ^NbAEL9ct)x!ss>a3m*w0)L`Wk%RF?70W8|_&?hjN3TC*}Fu()c%w z?MUZ=e`8=n{B$|+pWdAZpZfwzfiHIBg!>Q^{IcYWo2ODBu#f5u-KJ(G6e{}32I+th0^BWTB( zDY%z)lx(+oxaYGz+BE$MP+y&t`F1ZkZdMtsIoYCX&NV?4xM zAJhF`0(Je(#*cZFI`Kg|_gyJ%2xziL=)$keOZ2--n)h-Z<44@`oDd`8JG3WLl8+eC z68szoQxP@G;F&?jsw)Nm8_=&afz1@(m(-PH577s0UewjDv3Z-`Z<-6n*3l~W3;N)x zEn8#WQ+z}R#55+OJ)?YhT70EB%yD+Q@{P>{_VPqOalbKmwaXC34D)XQc%3wsUn`gU zInhJ70+-2iN7(hZf0QSARo7gjEP)pY9l;eO-M2%KJ+KcS-{P1M9p800000 literal 0 HcmV?d00001 diff --git a/LCU.WebApps.Operations.IncentiveManager/public/index.html b/LCU.WebApps.Operations.IncentiveManager/public/index.html new file mode 100644 index 0000000..8b0ad2d --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/public/index.html @@ -0,0 +1,42 @@ + + + + + + + + + + + + + Incentive Manager + + + +
+ + diff --git a/LCU.WebApps.Operations.IncentiveManager/public/logo192.png b/LCU.WebApps.Operations.IncentiveManager/public/logo192.png new file mode 100644 index 0000000000000000000000000000000000000000..fc44b0a3796c0e0a64c3d858ca038bd4570465d9 GIT binary patch literal 5347 zcmZWtbyO6NvR-oO24RV%BvuJ&=?+<7=`LvyB&A_#M7mSDYw1v6DJkiYl9XjT!%$dLEBTQ8R9|wd3008in6lFF3GV-6mLi?MoP_y~}QUnaDCHI#t z7w^m$@6DI)|C8_jrT?q=f8D?0AM?L)Z}xAo^e^W>t$*Y0KlT5=@bBjT9kxb%-KNdk zeOS1tKO#ChhG7%{ApNBzE2ZVNcxbrin#E1TiAw#BlUhXllzhN$qWez5l;h+t^q#Eav8PhR2|T}y5kkflaK`ba-eoE+Z2q@o6P$)=&` z+(8}+-McnNO>e#$Rr{32ngsZIAX>GH??tqgwUuUz6kjns|LjsB37zUEWd|(&O!)DY zQLrq%Y>)Y8G`yYbYCx&aVHi@-vZ3|ebG!f$sTQqMgi0hWRJ^Wc+Ibv!udh_r%2|U) zPi|E^PK?UE!>_4`f`1k4hqqj_$+d!EB_#IYt;f9)fBOumGNyglU(ofY`yHq4Y?B%- zp&G!MRY<~ajTgIHErMe(Z8JG*;D-PJhd@RX@QatggM7+G(Lz8eZ;73)72Hfx5KDOE zkT(m}i2;@X2AT5fW?qVp?@WgN$aT+f_6eo?IsLh;jscNRp|8H}Z9p_UBO^SJXpZew zEK8fz|0Th%(Wr|KZBGTM4yxkA5CFdAj8=QSrT$fKW#tweUFqr0TZ9D~a5lF{)%-tTGMK^2tz(y2v$i%V8XAxIywrZCp=)83p(zIk6@S5AWl|Oa2hF`~~^W zI;KeOSkw1O#TiQ8;U7OPXjZM|KrnN}9arP)m0v$c|L)lF`j_rpG(zW1Qjv$=^|p*f z>)Na{D&>n`jOWMwB^TM}slgTEcjxTlUby89j1)|6ydRfWERn3|7Zd2&e7?!K&5G$x z`5U3uFtn4~SZq|LjFVrz$3iln-+ucY4q$BC{CSm7Xe5c1J<=%Oagztj{ifpaZk_bQ z9Sb-LaQMKp-qJA*bP6DzgE3`}*i1o3GKmo2pn@dj0;He}F=BgINo};6gQF8!n0ULZ zL>kC0nPSFzlcB7p41doao2F7%6IUTi_+!L`MM4o*#Y#0v~WiO8uSeAUNp=vA2KaR&=jNR2iVwG>7t%sG2x_~yXzY)7K& zk3p+O0AFZ1eu^T3s};B%6TpJ6h-Y%B^*zT&SN7C=N;g|#dGIVMSOru3iv^SvO>h4M=t-N1GSLLDqVTcgurco6)3&XpU!FP6Hlrmj}f$ zp95;b)>M~`kxuZF3r~a!rMf4|&1=uMG$;h^g=Kl;H&Np-(pFT9FF@++MMEx3RBsK?AU0fPk-#mdR)Wdkj)`>ZMl#^<80kM87VvsI3r_c@_vX=fdQ`_9-d(xiI z4K;1y1TiPj_RPh*SpDI7U~^QQ?%0&!$Sh#?x_@;ag)P}ZkAik{_WPB4rHyW#%>|Gs zdbhyt=qQPA7`?h2_8T;-E6HI#im9K>au*(j4;kzwMSLgo6u*}-K`$_Gzgu&XE)udQ zmQ72^eZd|vzI)~!20JV-v-T|<4@7ruqrj|o4=JJPlybwMg;M$Ud7>h6g()CT@wXm` zbq=A(t;RJ^{Xxi*Ff~!|3!-l_PS{AyNAU~t{h;(N(PXMEf^R(B+ZVX3 z8y0;0A8hJYp@g+c*`>eTA|3Tgv9U8#BDTO9@a@gVMDxr(fVaEqL1tl?md{v^j8aUv zm&%PX4^|rX|?E4^CkplWWNv*OKM>DxPa z!RJ)U^0-WJMi)Ksc!^ixOtw^egoAZZ2Cg;X7(5xZG7yL_;UJ#yp*ZD-;I^Z9qkP`} zwCTs0*%rIVF1sgLervtnUo&brwz?6?PXRuOCS*JI-WL6GKy7-~yi0giTEMmDs_-UX zo=+nFrW_EfTg>oY72_4Z0*uG>MnXP=c0VpT&*|rvv1iStW;*^={rP1y?Hv+6R6bxFMkxpWkJ>m7Ba{>zc_q zEefC3jsXdyS5??Mz7IET$Kft|EMNJIv7Ny8ZOcKnzf`K5Cd)&`-fTY#W&jnV0l2vt z?Gqhic}l}mCv1yUEy$%DP}4AN;36$=7aNI^*AzV(eYGeJ(Px-j<^gSDp5dBAv2#?; zcMXv#aj>%;MiG^q^$0MSg-(uTl!xm49dH!{X0){Ew7ThWV~Gtj7h%ZD zVN-R-^7Cf0VH!8O)uUHPL2mO2tmE*cecwQv_5CzWeh)ykX8r5Hi`ehYo)d{Jnh&3p z9ndXT$OW51#H5cFKa76c<%nNkP~FU93b5h-|Cb}ScHs@4Q#|}byWg;KDMJ#|l zE=MKD*F@HDBcX@~QJH%56eh~jfPO-uKm}~t7VkHxHT;)4sd+?Wc4* z>CyR*{w@4(gnYRdFq=^(#-ytb^5ESD?x<0Skhb%Pt?npNW1m+Nv`tr9+qN<3H1f<% zZvNEqyK5FgPsQ`QIu9P0x_}wJR~^CotL|n zk?dn;tLRw9jJTur4uWoX6iMm914f0AJfB@C74a;_qRrAP4E7l890P&{v<}>_&GLrW z)klculcg`?zJO~4;BBAa=POU%aN|pmZJn2{hA!d!*lwO%YSIzv8bTJ}=nhC^n}g(ld^rn#kq9Z3)z`k9lvV>y#!F4e{5c$tnr9M{V)0m(Z< z#88vX6-AW7T2UUwW`g<;8I$Jb!R%z@rCcGT)-2k7&x9kZZT66}Ztid~6t0jKb&9mm zpa}LCb`bz`{MzpZR#E*QuBiZXI#<`5qxx=&LMr-UUf~@dRk}YI2hbMsAMWOmDzYtm zjof16D=mc`^B$+_bCG$$@R0t;e?~UkF?7<(vkb70*EQB1rfUWXh$j)R2)+dNAH5%R zEBs^?N;UMdy}V};59Gu#0$q53$}|+q7CIGg_w_WlvE}AdqoS<7DY1LWS9?TrfmcvT zaypmplwn=P4;a8-%l^e?f`OpGb}%(_mFsL&GywhyN(-VROj`4~V~9bGv%UhcA|YW% zs{;nh@aDX11y^HOFXB$a7#Sr3cEtNd4eLm@Y#fc&j)TGvbbMwze zXtekX_wJqxe4NhuW$r}cNy|L{V=t#$%SuWEW)YZTH|!iT79k#?632OFse{+BT_gau zJwQcbH{b}dzKO?^dV&3nTILYlGw{27UJ72ZN){BILd_HV_s$WfI2DC<9LIHFmtyw? zQ;?MuK7g%Ym+4e^W#5}WDLpko%jPOC=aN)3!=8)s#Rnercak&b3ESRX3z{xfKBF8L z5%CGkFmGO@x?_mPGlpEej!3!AMddChabyf~nJNZxx!D&{@xEb!TDyvqSj%Y5@A{}9 zRzoBn0?x}=krh{ok3Nn%e)#~uh;6jpezhA)ySb^b#E>73e*frBFu6IZ^D7Ii&rsiU z%jzygxT-n*joJpY4o&8UXr2s%j^Q{?e-voloX`4DQyEK+DmrZh8A$)iWL#NO9+Y@!sO2f@rI!@jN@>HOA< z?q2l{^%mY*PNx2FoX+A7X3N}(RV$B`g&N=e0uvAvEN1W^{*W?zT1i#fxuw10%~))J zjx#gxoVlXREWZf4hRkgdHx5V_S*;p-y%JtGgQ4}lnA~MBz-AFdxUxU1RIT$`sal|X zPB6sEVRjGbXIP0U+?rT|y5+ev&OMX*5C$n2SBPZr`jqzrmpVrNciR0e*Wm?fK6DY& zl(XQZ60yWXV-|Ps!A{EF;=_z(YAF=T(-MkJXUoX zI{UMQDAV2}Ya?EisdEW;@pE6dt;j0fg5oT2dxCi{wqWJ<)|SR6fxX~5CzblPGr8cb zUBVJ2CQd~3L?7yfTpLNbt)He1D>*KXI^GK%<`bq^cUq$Q@uJifG>p3LU(!H=C)aEL zenk7pVg}0{dKU}&l)Y2Y2eFMdS(JS0}oZUuVaf2+K*YFNGHB`^YGcIpnBlMhO7d4@vV zv(@N}(k#REdul8~fP+^F@ky*wt@~&|(&&meNO>rKDEnB{ykAZ}k>e@lad7to>Ao$B zz<1(L=#J*u4_LB=8w+*{KFK^u00NAmeNN7pr+Pf+N*Zl^dO{LM-hMHyP6N!~`24jd zXYP|Ze;dRXKdF2iJG$U{k=S86l@pytLx}$JFFs8e)*Vi?aVBtGJ3JZUj!~c{(rw5>vuRF$`^p!P8w1B=O!skwkO5yd4_XuG^QVF z`-r5K7(IPSiKQ2|U9+`@Js!g6sfJwAHVd|s?|mnC*q zp|B|z)(8+mxXyxQ{8Pg3F4|tdpgZZSoU4P&9I8)nHo1@)9_9u&NcT^FI)6|hsAZFk zZ+arl&@*>RXBf-OZxhZerOr&dN5LW9@gV=oGFbK*J+m#R-|e6(Loz(;g@T^*oO)0R zN`N=X46b{7yk5FZGr#5&n1!-@j@g02g|X>MOpF3#IjZ_4wg{dX+G9eqS+Es9@6nC7 zD9$NuVJI}6ZlwtUm5cCAiYv0(Yi{%eH+}t)!E^>^KxB5^L~a`4%1~5q6h>d;paC9c zTj0wTCKrhWf+F#5>EgX`sl%POl?oyCq0(w0xoL?L%)|Q7d|Hl92rUYAU#lc**I&^6p=4lNQPa0 znQ|A~i0ip@`B=FW-Q;zh?-wF;Wl5!+q3GXDu-x&}$gUO)NoO7^$BeEIrd~1Dh{Tr` z8s<(Bn@gZ(mkIGnmYh_ehXnq78QL$pNDi)|QcT*|GtS%nz1uKE+E{7jdEBp%h0}%r zD2|KmYGiPa4;md-t_m5YDz#c*oV_FqXd85d@eub?9N61QuYcb3CnVWpM(D-^|CmkL z(F}L&N7qhL2PCq)fRh}XO@U`Yn<?TNGR4L(mF7#4u29{i~@k;pLsgl({YW5`Mo+p=zZn3L*4{JU;++dG9 X@eDJUQo;Ye2mwlRs?y0|+_a0zY+Zo%Dkae}+MySoIppb75o?vUW_?)>@g{U2`ERQIXV zeY$JrWnMZ$QC<=ii4X|@0H8`si75jB(ElJb00HAB%>SlLR{!zO|C9P3zxw_U8?1d8uRZ=({Ga4shyN}3 zAK}WA(ds|``G4jA)9}Bt2Hy0+f3rV1E6b|@?hpGA=PI&r8)ah|)I2s(P5Ic*Ndhn^ z*T&j@gbCTv7+8rpYbR^Ty}1AY)YH;p!m948r#%7x^Z@_-w{pDl|1S4`EM3n_PaXvK z1JF)E3qy$qTj5Xs{jU9k=y%SQ0>8E$;x?p9ayU0bZZeo{5Z@&FKX>}s!0+^>C^D#z z>xsCPvxD3Z=dP}TTOSJhNTPyVt14VCQ9MQFN`rn!c&_p?&4<5_PGm4a;WS&1(!qKE z_H$;dDdiPQ!F_gsN`2>`X}$I=B;={R8%L~`>RyKcS$72ai$!2>d(YkciA^J0@X%G4 z4cu!%Ps~2JuJ8ex`&;Fa0NQOq_nDZ&X;^A=oc1&f#3P1(!5il>6?uK4QpEG8z0Rhu zvBJ+A9RV?z%v?!$=(vcH?*;vRs*+PPbOQ3cdPr5=tOcLqmfx@#hOqX0iN)wTTO21jH<>jpmwRIAGw7`a|sl?9y9zRBh>(_%| zF?h|P7}~RKj?HR+q|4U`CjRmV-$mLW>MScKnNXiv{vD3&2@*u)-6P@h0A`eeZ7}71 zK(w%@R<4lLt`O7fs1E)$5iGb~fPfJ?WxhY7c3Q>T-w#wT&zW522pH-B%r5v#5y^CF zcC30Se|`D2mY$hAlIULL%-PNXgbbpRHgn<&X3N9W!@BUk@9g*P5mz-YnZBb*-$zMM z7Qq}ic0mR8n{^L|=+diODdV}Q!gwr?y+2m=3HWwMq4z)DqYVg0J~^}-%7rMR@S1;9 z7GFj6K}i32X;3*$SmzB&HW{PJ55kT+EI#SsZf}bD7nW^Haf}_gXciYKX{QBxIPSx2Ma? zHQqgzZq!_{&zg{yxqv3xq8YV+`S}F6A>Gtl39_m;K4dA{pP$BW0oIXJ>jEQ!2V3A2 zdpoTxG&V=(?^q?ZTj2ZUpDUdMb)T?E$}CI>r@}PFPWD9@*%V6;4Ag>D#h>!s)=$0R zRXvdkZ%|c}ubej`jl?cS$onl9Tw52rBKT)kgyw~Xy%z62Lr%V6Y=f?2)J|bZJ5(Wx zmji`O;_B+*X@qe-#~`HFP<{8$w@z4@&`q^Q-Zk8JG3>WalhnW1cvnoVw>*R@c&|o8 zZ%w!{Z+MHeZ*OE4v*otkZqz11*s!#s^Gq>+o`8Z5 z^i-qzJLJh9!W-;SmFkR8HEZJWiXk$40i6)7 zZpr=k2lp}SasbM*Nbn3j$sn0;rUI;%EDbi7T1ZI4qL6PNNM2Y%6{LMIKW+FY_yF3) zSKQ2QSujzNMSL2r&bYs`|i2Dnn z=>}c0>a}>|uT!IiMOA~pVT~R@bGlm}Edf}Kq0?*Af6#mW9f9!}RjW7om0c9Qlp;yK z)=XQs(|6GCadQbWIhYF=rf{Y)sj%^Id-ARO0=O^Ad;Ph+ z0?$eE1xhH?{T$QI>0JP75`r)U_$#%K1^BQ8z#uciKf(C701&RyLQWBUp*Q7eyn76} z6JHpC9}R$J#(R0cDCkXoFSp;j6{x{b&0yE@P7{;pCEpKjS(+1RQy38`=&Yxo%F=3y zCPeefABp34U-s?WmU#JJw23dcC{sPPFc2#J$ZgEN%zod}J~8dLm*fx9f6SpO zn^Ww3bt9-r0XaT2a@Wpw;C23XM}7_14#%QpubrIw5aZtP+CqIFmsG4`Cm6rfxl9n5 z7=r2C-+lM2AB9X0T_`?EW&Byv&K?HS4QLoylJ|OAF z`8atBNTzJ&AQ!>sOo$?^0xj~D(;kS$`9zbEGd>f6r`NC3X`tX)sWgWUUOQ7w=$TO&*j;=u%25ay-%>3@81tGe^_z*C7pb9y*Ed^H3t$BIKH2o+olp#$q;)_ zfpjCb_^VFg5fU~K)nf*d*r@BCC>UZ!0&b?AGk_jTPXaSnCuW110wjHPPe^9R^;jo3 zwvzTl)C`Zl5}O2}3lec=hZ*$JnkW#7enKKc)(pM${_$9Hc=Sr_A9Biwe*Y=T?~1CK z6eZ9uPICjy-sMGbZl$yQmpB&`ouS8v{58__t0$JP%i3R&%QR3ianbZqDs<2#5FdN@n5bCn^ZtH992~5k(eA|8|@G9u`wdn7bnpg|@{m z^d6Y`*$Zf2Xr&|g%sai#5}Syvv(>Jnx&EM7-|Jr7!M~zdAyjt*xl;OLhvW-a%H1m0 z*x5*nb=R5u><7lyVpNAR?q@1U59 zO+)QWwL8t zyip?u_nI+K$uh{y)~}qj?(w0&=SE^8`_WMM zTybjG=999h38Yes7}-4*LJ7H)UE8{mE(6;8voE+TYY%33A>S6`G_95^5QHNTo_;Ao ztIQIZ_}49%{8|=O;isBZ?=7kfdF8_@azfoTd+hEJKWE!)$)N%HIe2cplaK`ry#=pV z0q{9w-`i0h@!R8K3GC{ivt{70IWG`EP|(1g7i_Q<>aEAT{5(yD z=!O?kq61VegV+st@XCw475j6vS)_z@efuqQgHQR1T4;|-#OLZNQJPV4k$AX1Uk8Lm z{N*b*ia=I+MB}kWpupJ~>!C@xEN#Wa7V+7{m4j8c?)ChV=D?o~sjT?0C_AQ7B-vxqX30s0I_`2$in86#`mAsT-w?j{&AL@B3$;P z31G4(lV|b}uSDCIrjk+M1R!X7s4Aabn<)zpgT}#gE|mIvV38^ODy@<&yflpCwS#fRf9ZX3lPV_?8@C5)A;T zqmouFLFk;qIs4rA=hh=GL~sCFsXHsqO6_y~*AFt939UYVBSx1s(=Kb&5;j7cSowdE;7()CC2|-i9Zz+_BIw8#ll~-tyH?F3{%`QCsYa*b#s*9iCc`1P1oC26?`g<9))EJ3%xz+O!B3 zZ7$j~To)C@PquR>a1+Dh>-a%IvH_Y7^ys|4o?E%3`I&ADXfC8++hAdZfzIT#%C+Jz z1lU~K_vAm0m8Qk}K$F>|>RPK%<1SI0(G+8q~H zAsjezyP+u!Se4q3GW)`h`NPSRlMoBjCzNPesWJwVTY!o@G8=(6I%4XHGaSiS3MEBK zhgGFv6Jc>L$4jVE!I?TQuwvz_%CyO!bLh94nqK11C2W$*aa2ueGopG8DnBICVUORP zgytv#)49fVXDaR$SukloYC3u7#5H)}1K21=?DKj^U)8G;MS)&Op)g^zR2($<>C*zW z;X7`hLxiIO#J`ANdyAOJle4V%ppa*(+0i3w;8i*BA_;u8gOO6)MY`ueq7stBMJTB; z-a0R>hT*}>z|Gg}@^zDL1MrH+2hsR8 zHc}*9IvuQC^Ju)^#Y{fOr(96rQNPNhxc;mH@W*m206>Lo<*SaaH?~8zg&f&%YiOEG zGiz?*CP>Bci}!WiS=zj#K5I}>DtpregpP_tfZtPa(N<%vo^#WCQ5BTv0vr%Z{)0q+ z)RbfHktUm|lg&U3YM%lMUM(fu}i#kjX9h>GYctkx9Mt_8{@s%!K_EI zScgwy6%_fR?CGJQtmgNAj^h9B#zmaMDWgH55pGuY1Gv7D z;8Psm(vEPiwn#MgJYu4Ty9D|h!?Rj0ddE|&L3S{IP%H4^N!m`60ZwZw^;eg4sk6K{ ziA^`Sbl_4~f&Oo%n;8Ye(tiAdlZKI!Z=|j$5hS|D$bDJ}p{gh$KN&JZYLUjv4h{NY zBJ>X9z!xfDGY z+oh_Z&_e#Q(-}>ssZfm=j$D&4W4FNy&-kAO1~#3Im;F)Nwe{(*75(p=P^VI?X0GFakfh+X-px4a%Uw@fSbmp9hM1_~R>?Z8+ ziy|e9>8V*`OP}4x5JjdWp}7eX;lVxp5qS}0YZek;SNmm7tEeSF*-dI)6U-A%m6YvCgM(}_=k#a6o^%-K4{`B1+}O4x zztDT%hVb;v#?j`lTvlFQ3aV#zkX=7;YFLS$uIzb0E3lozs5`Xy zi~vF+%{z9uLjKvKPhP%x5f~7-Gj+%5N`%^=yk*Qn{`> z;xj&ROY6g`iy2a@{O)V(jk&8#hHACVDXey5a+KDod_Z&}kHM}xt7}Md@pil{2x7E~ zL$k^d2@Ec2XskjrN+IILw;#7((abu;OJii&v3?60x>d_Ma(onIPtcVnX@ELF0aL?T zSmWiL3(dOFkt!x=1O!_0n(cAzZW+3nHJ{2S>tgSK?~cFha^y(l@-Mr2W$%MN{#af8J;V*>hdq!gx=d0h$T7l}>91Wh07)9CTX zh2_ZdQCyFOQ)l(}gft0UZG`Sh2`x-w`5vC2UD}lZs*5 zG76$akzn}Xi))L3oGJ75#pcN=cX3!=57$Ha=hQ2^lwdyU#a}4JJOz6ddR%zae%#4& za)bFj)z=YQela(F#Y|Q#dp}PJghITwXouVaMq$BM?K%cXn9^Y@g43$=O)F&ZlOUom zJiad#dea;-eywBA@e&D6Pdso1?2^(pXiN91?jvcaUyYoKUmvl5G9e$W!okWe*@a<^ z8cQQ6cNSf+UPDx%?_G4aIiybZHHagF{;IcD(dPO!#=u zWfqLcPc^+7Uu#l(Bpxft{*4lv#*u7X9AOzDO z1D9?^jIo}?%iz(_dwLa{ex#T}76ZfN_Z-hwpus9y+4xaUu9cX}&P{XrZVWE{1^0yw zO;YhLEW!pJcbCt3L8~a7>jsaN{V3>tz6_7`&pi%GxZ=V3?3K^U+*ryLSb)8^IblJ0 zSRLNDvIxt)S}g30?s_3NX>F?NKIGrG_zB9@Z>uSW3k2es_H2kU;Rnn%j5qP)!XHKE zPB2mHP~tLCg4K_vH$xv`HbRsJwbZMUV(t=ez;Ec(vyHH)FbfLg`c61I$W_uBB>i^r z&{_P;369-&>23R%qNIULe=1~T$(DA`ev*EWZ6j(B$(te}x1WvmIll21zvygkS%vwG zzkR6Z#RKA2!z!C%M!O>!=Gr0(J0FP=-MN=5t-Ir)of50y10W}j`GtRCsXBakrKtG& zazmITDJMA0C51&BnLY)SY9r)NVTMs);1<=oosS9g31l{4ztjD3#+2H7u_|66b|_*O z;Qk6nalpqdHOjx|K&vUS_6ITgGll;TdaN*ta=M_YtyC)I9Tmr~VaPrH2qb6sd~=AcIxV+%z{E&0@y=DPArw zdV7z(G1hBx7hd{>(cr43^WF%4Y@PXZ?wPpj{OQ#tvc$pABJbvPGvdR`cAtHn)cSEV zrpu}1tJwQ3y!mSmH*uz*x0o|CS<^w%&KJzsj~DU0cLQUxk5B!hWE>aBkjJle8z~;s z-!A=($+}Jq_BTK5^B!`R>!MulZN)F=iXXeUd0w5lUsE5VP*H*oCy(;?S$p*TVvTxwAeWFB$jHyb0593)$zqalVlDX=GcCN1gU0 zlgU)I$LcXZ8Oyc2TZYTPu@-;7<4YYB-``Qa;IDcvydIA$%kHhJKV^m*-zxcvU4viy&Kr5GVM{IT>WRywKQ9;>SEiQD*NqplK-KK4YR`p0@JW)n_{TU3bt0 zim%;(m1=#v2}zTps=?fU5w^(*y)xT%1vtQH&}50ZF!9YxW=&7*W($2kgKyz1mUgfs zfV<*XVVIFnohW=|j+@Kfo!#liQR^x>2yQdrG;2o8WZR+XzU_nG=Ed2rK?ntA;K5B{ z>M8+*A4!Jm^Bg}aW?R?6;@QG@uQ8&oJ{hFixcfEnJ4QH?A4>P=q29oDGW;L;= z9-a0;g%c`C+Ai!UmK$NC*4#;Jp<1=TioL=t^YM)<<%u#hnnfSS`nq63QKGO1L8RzX z@MFDqs1z ztYmxDl@LU)5acvHk)~Z`RW7=aJ_nGD!mOSYD>5Odjn@TK#LY{jf?+piB5AM-CAoT_ z?S-*q7}wyLJzK>N%eMPuFgN)Q_otKP;aqy=D5f!7<=n(lNkYRXVpkB{TAYLYg{|(jtRqYmg$xH zjmq?B(RE4 zQx^~Pt}gxC2~l=K$$-sYy_r$CO(d=+b3H1MB*y_5g6WLaWTXn+TKQ|hNY^>Mp6k*$ zwkovomhu776vQATqT4blf~g;TY(MWCrf^^yfWJvSAB$p5l;jm@o#=!lqw+Lqfq>X= z$6~kxfm7`3q4zUEB;u4qa#BdJxO!;xGm)wwuisj{0y2x{R(IGMrsIzDY9LW>m!Y`= z04sx3IjnYvL<4JqxQ8f7qYd0s2Ig%`ytYPEMKI)s(LD}D@EY>x`VFtqvnADNBdeao zC96X+MxnwKmjpg{U&gP3HE}1=s!lv&D{6(g_lzyF3A`7Jn*&d_kL<;dAFx!UZ>hB8 z5A*%LsAn;VLp>3${0>M?PSQ)9s3}|h2e?TG4_F{}{Cs>#3Q*t$(CUc}M)I}8cPF6% z=+h(Kh^8)}gj(0}#e7O^FQ6`~fd1#8#!}LMuo3A0bN`o}PYsm!Y}sdOz$+Tegc=qT z8x`PH$7lvnhJp{kHWb22l;@7B7|4yL4UOOVM0MP_>P%S1Lnid)+k9{+3D+JFa#Pyf zhVc#&df87APl4W9X)F3pGS>@etfl=_E5tBcVoOfrD4hmVeTY-cj((pkn%n@EgN{0f zwb_^Rk0I#iZuHK!l*lN`ceJn(sI{$Fq6nN& zE<-=0_2WN}m+*ivmIOxB@#~Q-cZ>l136w{#TIJe478`KE7@=a{>SzPHsKLzYAyBQO zAtuuF$-JSDy_S@6GW0MOE~R)b;+0f%_NMrW(+V#c_d&U8Z9+ec4=HmOHw?gdjF(Lu zzra83M_BoO-1b3;9`%&DHfuUY)6YDV21P$C!Rc?mv&{lx#f8oc6?0?x zK08{WP65?#>(vPfA-c=MCY|%*1_<3D4NX zeVTi-JGl2uP_2@0F{G({pxQOXt_d{g_CV6b?jNpfUG9;8yle-^4KHRvZs-_2siata zt+d_T@U$&t*xaD22(fH(W1r$Mo?3dc%Tncm=C6{V9y{v&VT#^1L04vDrLM9qBoZ4@ z6DBN#m57hX7$C(=#$Y5$bJmwA$T8jKD8+6A!-IJwA{WOfs%s}yxUw^?MRZjF$n_KN z6`_bGXcmE#5e4Ym)aQJ)xg3Pg0@k`iGuHe?f(5LtuzSq=nS^5z>vqU0EuZ&75V%Z{ zYyhRLN^)$c6Ds{f7*FBpE;n5iglx5PkHfWrj3`x^j^t z7ntuV`g!9Xg#^3!x)l*}IW=(Tz3>Y5l4uGaB&lz{GDjm2D5S$CExLT`I1#n^lBH7Y zDgpMag@`iETKAI=p<5E#LTkwzVR@=yY|uBVI1HG|8h+d;G-qfuj}-ZR6fN>EfCCW z9~wRQoAPEa#aO?3h?x{YvV*d+NtPkf&4V0k4|L=uj!U{L+oLa(z#&iuhJr3-PjO3R z5s?=nn_5^*^Rawr>>Nr@K(jwkB#JK-=+HqwfdO<+P5byeim)wvqGlP-P|~Nse8=XF zz`?RYB|D6SwS}C+YQv+;}k6$-%D(@+t14BL@vM z2q%q?f6D-A5s$_WY3{^G0F131bbh|g!}#BKw=HQ7mx;Dzg4Z*bTLQSfo{ed{4}NZW zfrRm^Ca$rlE{Ue~uYv>R9{3smwATcdM_6+yWIO z*ZRH~uXE@#p$XTbCt5j7j2=86e{9>HIB6xDzV+vAo&B?KUiMP|ttOElepnl%|DPqL b{|{}U^kRn2wo}j7|0ATu<;8xA7zX}7|B6mN literal 0 HcmV?d00001 diff --git a/LCU.WebApps.Operations.IncentiveManager/public/manifest.json b/LCU.WebApps.Operations.IncentiveManager/public/manifest.json new file mode 100644 index 0000000..4024efa --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/public/manifest.json @@ -0,0 +1,26 @@ +{ + "short_name": "Incentive Manager", + "name": "Incentive Manager", + "description": "Manage and view Landmark Incentives", + "icons": [ + { + "src": "favicon.ico", + "sizes": "64x64 32x32 24x24 16x16", + "type": "image/x-icon" + }, + { + "src": "logo192.png", + "type": "image/png", + "sizes": "192x192" + }, + { + "src": "logo512.png", + "type": "image/png", + "sizes": "512x512" + } + ], + "start_url": ".", + "display": "standalone", + "theme_color": "#000000", + "background_color": "#ffffff" +} diff --git a/LCU.WebApps.Operations.IncentiveManager/public/robots.txt b/LCU.WebApps.Operations.IncentiveManager/public/robots.txt new file mode 100644 index 0000000..e9e57dc --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/public/robots.txt @@ -0,0 +1,3 @@ +# https://www.robotstxt.org/robotstxt.html +User-agent: * +Disallow: diff --git a/LCU.WebApps.Operations.IncentiveManager/src/App.test.tsx b/LCU.WebApps.Operations.IncentiveManager/src/App.test.tsx new file mode 100644 index 0000000..734daf6 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/App.test.tsx @@ -0,0 +1,7 @@ +import React from "react"; + +describe("Fake Test", () => { + it("Does Not Work", () => { + expect(true).toBe(true); + }); +}); diff --git a/LCU.WebApps.Operations.IncentiveManager/src/App.tsx b/LCU.WebApps.Operations.IncentiveManager/src/App.tsx new file mode 100644 index 0000000..c108b98 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/App.tsx @@ -0,0 +1,293 @@ +import React, { useEffect } from "react"; +import { BrowserRouter, Route, Switch } from "react-router-dom"; +import NotFound404Page from "./components/status-pages/NotFound404Page"; +import TestPage from "./components/status-pages/TestPage"; +import Configuration from "./shared/configuration/Configuration"; +import AppContainer from "./components/layout/AppContainer"; +import ActivityPage from "./containers/activity-page/ActivityPage"; +import ActivityDetailPage from "./containers/activity-detail-page/ActivityDetailPage"; +import DepartmentGoalsPage from "./containers/goals/department/DepartmentGoalsPage"; +import PersonnelGoalsPage from "./containers/goals/personnel/PersonnelGoalsPage"; +import OrganizationView from "./containers/organization-view/OrganizationView"; +import { useDispatch } from "react-redux"; +import RoleManagement from "./containers/role-management/RoleMangement"; +import PayoutProfilePage from "./containers/payout-profile/PayoutProfilePage"; +import CommissionsPage from "./containers/commissions-page/CommissionsPage"; +import ProfileSearchPage from "./containers/profile/ProfileSearchPage"; +import ProfileSelector from "./containers/profile/ProfileSelector"; +import { refreshProfile } from "./actions/ProfileActions"; +import ExportPayoutPage from "./containers/payouts/ExportPayoutPage"; +import DashboardPage from "./containers/dashboard/DashboardPage"; +import ActivityFormPage from "./containers/activity-form-page/ActivityFormPage"; +import { SettingsProvider } from "./shared/providers/SettingsProvider"; +import DepartmentDetailPage from "./containers/department-detail-page/DepartmentDetailPage"; +import PermissionGuard from "./containers/shared/PermissionGuard"; +import EndOfEmploymentPage from "./containers/eoe-list/EndOfEmploymentPage"; +import DeadActivityPage from "./containers/dead-activity-page/DeadActivityPage"; +import RoleMappingPage from "./containers/role-mapping-page/RoleMappingPage"; +import ReportPage from "./containers/report-page/ReportPage"; + +const App: React.FC = () => { + const dispatch = useDispatch(); + useEffect(() => { + dispatch(refreshProfile()); + }, []); + + return ( + //Normally we use Connected Router which requires has. React-Dom router does not require a hash. + + + + + } + /> + } + /> + { + return ( + + + + ); + }} + /> + { + return ( + + + + ); + }} + /> + { + return ( + + + + ); + }} + /> + { + return ( + + + + ); + }} + /> + } + /> + { + return ( + + + + ); + }} + /> + { + return ( + + + + ); + }} + /> + { + return ( + + + + ); + }} + /> + { + return ( + + + + ); + }} + /> + { + return ( + + + + ); + }} + /> + } + /> + } + /> + { + return ( + + + + ); + }} + /> + ( + + )} + /> + ( + + )} + /> + ( + + )} + /> + { + return ( + + + + ); + }} + /> + { + return ( + + + + ); + }} + /> + ( + + )} + /> + + + + + ); +}; + +export default App; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/actions/ActivityActions.ts b/LCU.WebApps.Operations.IncentiveManager/src/actions/ActivityActions.ts new file mode 100644 index 0000000..88c8ff9 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/actions/ActivityActions.ts @@ -0,0 +1,172 @@ +import { ActionCreator, Dispatch } from "redux"; +import { ThunkAction } from "redux-thunk"; +import AsyncState from "../shared/enumerations/AsyncState"; +import IAsyncStateContainer from "../shared/types/IAsyncStateContainer"; +import { + IActivitySearchForm, + IAuditableActivity, + IDepartmentProgress, + IEmployeeProgress +} from "../shared/models/Activity"; +import ActivityService from "./../shared/services/ActivityService"; +import { IAppState } from "../store/Store"; +import IPagedResponse from "./../shared/types/IPagedResponse"; +import { IMonthRange } from "../shared/types/IMonthRange"; + +export enum ActivityActionTypes { + GET_ACTIVITIES = "ACTIVITY_GET_ACTIVITIES", + GET_NEXT_ACTIVITY_PAGE = "GET_NEXT_ACTIVITY_PAGE", + GET_LAST_ACTIVITY_PAGE = "GET_LAST_ACTIVITY_PAGE", + GET_MY_ACTIVITIES = "ACTIVITY_GET_MY_ACTIVITIES", + GET_MY_DEPARTMENT_ACTIVITIES = "ACTIVITY_GET_MY_DEPARTMENT_ACTIVITIES", + SET_ACTIVITY_STATES_FILTER = "SET_ACTIVITY_STATES_FILTER" +} + +export interface IActivityGetAllAction { + type: ActivityActionTypes.GET_ACTIVITIES; + activities: IAsyncStateContainer>; +} +export interface IActivityGetNextPageAction { + type: ActivityActionTypes.GET_NEXT_ACTIVITY_PAGE; + activities: IAsyncStateContainer>; +} + +export interface IActivityGetLastPageAction { + type: ActivityActionTypes.GET_LAST_ACTIVITY_PAGE; + activities: IAsyncStateContainer>; +} + +export interface IActivitySetStateFilter { + type: ActivityActionTypes.SET_ACTIVITY_STATES_FILTER; + allowedStates: string[]; +} + +export interface IActivityGetMyAction { + type: ActivityActionTypes.GET_MY_ACTIVITIES; + progress: IAsyncStateContainer; +} + +export interface IActivityGetMyDepartmentAction { + type: ActivityActionTypes.GET_MY_DEPARTMENT_ACTIVITIES; + departmentProgress: IAsyncStateContainer; +} + +export type ActivityActions = + | IActivityGetAllAction + | IActivityGetMyAction + | IActivityGetMyDepartmentAction + | IActivityGetNextPageAction + | IActivityGetLastPageAction + | IActivitySetStateFilter; + +export const getAllActivities: ActionCreator< + ThunkAction, IAppState, null, IActivityGetAllAction> +> = (page: number, limit: number, filters: IActivitySearchForm) => { + return async (dispatch: Dispatch) => { + dispatch({ + type: ActivityActionTypes.GET_ACTIVITIES, + activities: { state: AsyncState.Loading } + }); + + const newActivities = await ActivityService.getPaginatedActivities( + page, + limit, + filters + ); + + dispatch({ + type: ActivityActionTypes.GET_ACTIVITIES, + activities: newActivities + }); + }; +}; + +export const getNextPageOfActivities: ActionCreator< + ThunkAction, IAppState, null, IActivityGetNextPageAction> +> = () => { + return async (dispatch: Dispatch, getState) => { + const activityPageResponse = getState().activityState + .filteredActivities; + + if (activityPageResponse?.data?.nextPage) { + dispatch({ + type: ActivityActionTypes.GET_NEXT_ACTIVITY_PAGE, + activities: { state: AsyncState.Loading } + }); + + const newActivities = await ActivityService.getPaginatedActivitiesFromUrl( + activityPageResponse.data.nextPage + // you don't need filters... + ); + + dispatch({ + type: ActivityActionTypes.GET_NEXT_ACTIVITY_PAGE, + activities: newActivities + }); + } + }; +}; + +export const getLastPageOfActivities: ActionCreator< + ThunkAction, IAppState, null, IActivityGetLastPageAction> +> = () => { + return async (dispatch: Dispatch, getState) => { + const activityPageResponse = getState().activityState + .filteredActivities; + + if (activityPageResponse?.data?.previousPage) { + dispatch({ + type: ActivityActionTypes.GET_LAST_ACTIVITY_PAGE, + activities: { state: AsyncState.Loading } + }); + + const newActivities = await ActivityService.getPaginatedActivitiesFromUrl( + activityPageResponse.data.previousPage + // you don't need the filters, they're in the page url... + ); + + dispatch({ + type: ActivityActionTypes.GET_LAST_ACTIVITY_PAGE, + activities: newActivities + }); + } + }; +}; + +export const getMyProgress: ActionCreator< + ThunkAction, IAppState, null, IActivityGetMyAction> +> = (monthRange: IMonthRange) => { + return async (dispatch: Dispatch) => { + dispatch({ + type: ActivityActionTypes.GET_MY_ACTIVITIES, + progress: { state: AsyncState.Loading } + }); + + const newActivities = await ActivityService.getMyProgress(monthRange); + + dispatch({ + type: ActivityActionTypes.GET_MY_ACTIVITIES, + progress: newActivities + }); + }; +}; + +export const getMyDepartmentProgress: ActionCreator< + ThunkAction, IAppState, null, IActivityGetMyDepartmentAction> +> = (monthRange: IMonthRange) => { + return async (dispatch: Dispatch) => { + dispatch({ + type: ActivityActionTypes.GET_MY_DEPARTMENT_ACTIVITIES, + departmentProgress: { state: AsyncState.Loading } + }); + + const departmentActivities = await ActivityService.getMyDepartmentProgress( + monthRange + ); + + dispatch({ + type: ActivityActionTypes.GET_MY_DEPARTMENT_ACTIVITIES, + departmentProgress: departmentActivities + }); + }; +}; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/actions/CommissionActions.ts b/LCU.WebApps.Operations.IncentiveManager/src/actions/CommissionActions.ts new file mode 100644 index 0000000..692d6a4 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/actions/CommissionActions.ts @@ -0,0 +1,43 @@ +import { Dispatch } from "react"; +import { ActionCreator } from "redux"; +import { ThunkAction } from "redux-thunk"; +import AsyncState from "../shared/enumerations/AsyncState"; +import { IAuditableCommission } from "../shared/models/Commission"; +import IAsyncStateContainer from "../shared/types/IAsyncStateContainer"; +import { IAppState } from "../store/Store"; +import CommissionService from "../shared/services/CommissionService"; + +export enum CommissionActionTypes { + GET_ALL_COMMISSIONS = "GET_ALL_COMMISSIONS", + SET_COMMISSIONS = "SET_COMMISSIONS" +} + +export interface ICommissionsGetAllAction { + type: CommissionActionTypes.GET_ALL_COMMISSIONS; + commissions: IAsyncStateContainer; +} + +export interface ICommissionsSetAction { + type: CommissionActionTypes.SET_COMMISSIONS; + submitState: IAsyncStateContainer; +} + +export type CommissionActions = ICommissionsGetAllAction; + +export const getAllCommissions: ActionCreator< + ThunkAction, IAppState, null, ICommissionsGetAllAction> +> = () => { + return async (dispatch: Dispatch) => { + dispatch({ + type: CommissionActionTypes.GET_ALL_COMMISSIONS, + commissions: { state: AsyncState.Loading } + }); + + const allCommissions = await CommissionService.getAllCommissions(); + + dispatch({ + type: CommissionActionTypes.GET_ALL_COMMISSIONS, + commissions: allCommissions + }); + }; +}; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/actions/DeadActivityActions.ts b/LCU.WebApps.Operations.IncentiveManager/src/actions/DeadActivityActions.ts new file mode 100644 index 0000000..a71d5ac --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/actions/DeadActivityActions.ts @@ -0,0 +1,125 @@ +import { Dispatch } from "react"; +import { ActionCreator } from "redux"; +import { ThunkAction } from "redux-thunk"; +import AsyncState from "../shared/enumerations/AsyncState"; +import { + IAuditableDeadActivity, + IDeadActivitySearchForm +} from "../shared/models/DeadActivity"; +import DeadActivityService from "../shared/services/DeadActivityService"; +import DeadDeadActivityService from "../shared/services/DeadActivityService"; +import IAsyncStateContainer from "../shared/types/IAsyncStateContainer"; +import IPagedResponse from "../shared/types/IPagedResponse"; +import { IAppState } from "../store/Store"; + +export enum DeadActivityActionTypes { + GET_DEAD_ACTIVITIES = "DEAD_ACTIVITY_GET_DEAD_ACTIVITIES", + GET_NEXT_DEAD_ACTIVITY_PAGE = "GET_NEXT_DEAD_ACTIVITY_PAGE", + GET_LAST_DEAD_ACTIVITY_PAGE = "GET_LAST_DEAD_ACTIVITY_PAGE", + SET_DEAD_ACTIVTY_STATE_FILTER = "SET_DEAD_ACTIVITY_STATE_FILTER" +} + +export interface IDeadActivityGetAllAction { + type: DeadActivityActionTypes.GET_DEAD_ACTIVITIES; + deadActivities: IAsyncStateContainer< + IPagedResponse + >; +} +export interface IDeadActivityGetNextPageAction { + type: DeadActivityActionTypes.GET_NEXT_DEAD_ACTIVITY_PAGE; + deadActivities: IAsyncStateContainer< + IPagedResponse + >; +} + +export interface IDeadActivityGetLastPageAction { + type: DeadActivityActionTypes.GET_LAST_DEAD_ACTIVITY_PAGE; + deadActivities: IAsyncStateContainer< + IPagedResponse + >; +} + +export interface IDeadActivityStateFilter { + type: DeadActivityActionTypes.SET_DEAD_ACTIVTY_STATE_FILTER; + allowedStates: string[]; +} + +export type DeadActivityActions = + | IDeadActivityGetAllAction + | IDeadActivityGetNextPageAction + | IDeadActivityGetLastPageAction + | IDeadActivityStateFilter; + +export const getAllDeadActivities: ActionCreator< + ThunkAction, IAppState, null, IDeadActivityGetAllAction> +> = (page: number, limit: number, filters: IDeadActivitySearchForm) => { + return async (dispatch: Dispatch) => { + dispatch({ + type: DeadActivityActionTypes.GET_DEAD_ACTIVITIES, + deadActivities: { state: AsyncState.Loading } + }); + + const newDeadActivities = await DeadDeadActivityService.getPaginatedDeadActivities( + page, + limit, + filters + ); + + dispatch({ + type: DeadActivityActionTypes.GET_DEAD_ACTIVITIES, + deadActivities: newDeadActivities + }); + }; +}; + +export const getNextPageOfDeadActivities: ActionCreator< + ThunkAction, IAppState, null, IDeadActivityGetNextPageAction> +> = () => { + return async (dispatch: Dispatch, getState) => { + const deadActivityPageResponse = getState().deadActivityState + .filteredDeadActivities; + + if (deadActivityPageResponse?.data?.nextPage) { + dispatch({ + type: DeadActivityActionTypes.GET_NEXT_DEAD_ACTIVITY_PAGE, + deadActivities: { state: AsyncState.Loading } + }); + + const newDeadActivities = await DeadActivityService.getPaginatedDeadActivitiesFromUrl( + deadActivityPageResponse.data.nextPage + // you don't need filters... + ); + + dispatch({ + type: DeadActivityActionTypes.GET_NEXT_DEAD_ACTIVITY_PAGE, + deadActivities: newDeadActivities + }); + } + }; +}; + +export const getLastPageOfDeadActivities: ActionCreator< + ThunkAction, IAppState, null, IDeadActivityGetLastPageAction> +> = () => { + return async (dispatch: Dispatch, getState) => { + const deadActivityPageResponse = getState().deadActivityState + .filteredDeadActivities; + + if (deadActivityPageResponse?.data?.previousPage) { + dispatch({ + type: DeadActivityActionTypes.GET_LAST_DEAD_ACTIVITY_PAGE, + deadActivities: { state: AsyncState.Loading } + }); + + const newDeadActivities = await DeadActivityService.getPaginatedDeadActivitiesFromUrl( + deadActivityPageResponse.data.previousPage + // you don't need the filters, they're in the page url... + ); + + dispatch({ + type: DeadActivityActionTypes.GET_LAST_DEAD_ACTIVITY_PAGE, + deadActivities: newDeadActivities + }); + } + }; +}; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/actions/DepartmentActions.ts b/LCU.WebApps.Operations.IncentiveManager/src/actions/DepartmentActions.ts new file mode 100644 index 0000000..2f00129 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/actions/DepartmentActions.ts @@ -0,0 +1,91 @@ +import IAsyncStateContainer from "../shared/types/IAsyncStateContainer"; +import { IDepartment, IDepartmentDto } from "../shared/models/Department"; +import { ActionCreator, Dispatch } from "redux"; +import { IAppState } from "../store/Store"; +import DepartmentService from "./../shared/services/DepartmentService"; +import AsyncState from "../shared/enumerations/AsyncState"; +import { ThunkAction } from "redux-thunk"; +export enum DepartmentActionTypes { + GET_ALL_DEPARTMENTS = "GET_ALL_DEPARTMENTS", + ADD_NEW_DEPARTMENT = "ADD_NEW_DEPARTNEMTN", + UPDATE_EXISTING_DEPARTNEMTN = "UPDATE_EXISTING_DEPARTMENT" +} + +export interface IDepartmentGetAllAction { + type: DepartmentActionTypes.GET_ALL_DEPARTMENTS; + departments: IAsyncStateContainer; +} + +export interface IDepartmentsAddNew { + type: DepartmentActionTypes.ADD_NEW_DEPARTMENT; + newDepartment: IAsyncStateContainer; +} + +export interface IDepartmentUpdateExisting { + type: DepartmentActionTypes.UPDATE_EXISTING_DEPARTNEMTN; + updatedDepartment: IAsyncStateContainer; +} + +export type DepartmentActions = + | IDepartmentGetAllAction + | IDepartmentsAddNew + | IDepartmentUpdateExisting; + +export const getAllDepartments: ActionCreator< + ThunkAction, IAppState, null, IDepartmentGetAllAction> +> = () => { + return async (dispatch: Dispatch) => { + dispatch({ + type: DepartmentActionTypes.GET_ALL_DEPARTMENTS, + departments: { state: AsyncState.Loading } + }); + + const newDepartments = await DepartmentService.getAllDepartments(); + + dispatch({ + type: DepartmentActionTypes.GET_ALL_DEPARTMENTS, + departments: newDepartments + }); + }; +}; + +export const submitNewDepartment: ActionCreator< + ThunkAction, IAppState, null, IDepartmentsAddNew> +> = (newDepartment: IDepartmentDto) => { + return async (dispatch: Dispatch) => { + dispatch({ + type: DepartmentActionTypes.ADD_NEW_DEPARTMENT, + newDepartment: { state: AsyncState.Loading } + }); + + const submittedDepartment = await DepartmentService.submitNewDepartment( + newDepartment + ); + + dispatch({ + type: DepartmentActionTypes.ADD_NEW_DEPARTMENT, + newDepartment: submittedDepartment + }); + }; +}; + +export const updateExistingDepartment: ActionCreator< + ThunkAction, IAppState, null, IDepartmentGetAllAction> +> = (id: number, newDepartment: IDepartmentDto) => { + return async (dispatch: Dispatch) => { + dispatch({ + type: DepartmentActionTypes.UPDATE_EXISTING_DEPARTNEMTN, + updatedDepartment: { state: AsyncState.Loading } + }); + + const submittedDepartment = await DepartmentService.updateExistingDepartment( + id, + newDepartment + ); + + dispatch({ + type: DepartmentActionTypes.UPDATE_EXISTING_DEPARTNEMTN, + updatedDepartment: submittedDepartment + }); + }; +}; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/actions/EmployeeActions.ts b/LCU.WebApps.Operations.IncentiveManager/src/actions/EmployeeActions.ts new file mode 100644 index 0000000..2bec7e2 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/actions/EmployeeActions.ts @@ -0,0 +1,113 @@ +import IAsyncStateContainer from "../shared/types/IAsyncStateContainer"; +import { IEmployee } from "../shared/models/Employee"; +import { ActionCreator, Dispatch } from "redux"; +import { ThunkAction } from "redux-thunk"; +import { IAppState } from "../store/Store"; +import AsyncState from "../shared/enumerations/AsyncState"; +import EmployeeService from "../shared/services/EmployeeService"; +import { + IDepartmentAssignmentDto, + IDepartmentAssignmentUpdateDto +} from "../shared/models/DepartmentAssignment"; + +export enum EmployeeActionTypes { + SELECT_EMPLOYEE = "SELECT_EMPLOYEE", + SUBMIT_DEPT_ASSIGNMENT = "SUBMIT_DEPARTMENT_ASSIGNMENT", + SUBMIT_END_DEPARTMENT_ASSIGNMENT = "SUBMIT_END_DEPARTMENT_ASSIGNMENT" +} + +export interface IEmployeeSelectAction { + type: EmployeeActionTypes.SELECT_EMPLOYEE; + selectedEmployee: IAsyncStateContainer; +} + +export interface IEmployeeSubmitDeptAssignmentAction { + type: EmployeeActionTypes.SUBMIT_DEPT_ASSIGNMENT; + submitState: IAsyncStateContainer; +} + +export interface IEmployeeSubmitEndDeptAssignmentAction { + type: EmployeeActionTypes.SUBMIT_END_DEPARTMENT_ASSIGNMENT; + submitState: IAsyncStateContainer; +} + +export type EmployeeActions = + | IEmployeeSelectAction + | IEmployeeSubmitDeptAssignmentAction + | IEmployeeSubmitEndDeptAssignmentAction; + +export const trySelectEmployee: ActionCreator< + ThunkAction, IAppState, null, IEmployeeSelectAction> +> = (employeeId: string) => { + return async (dispatch: Dispatch) => { + dispatch({ + type: EmployeeActionTypes.SELECT_EMPLOYEE, + selectedEmployee: { state: AsyncState.Loading } + }); + + const retreivedEmployee = await EmployeeService.getByEmployeeId( + employeeId + ); + + dispatch({ + type: EmployeeActionTypes.SELECT_EMPLOYEE, + selectedEmployee: retreivedEmployee + }); + }; +}; + +export const submitDepartmentAssignment: ActionCreator< + ThunkAction< + Promise, + IAppState, + null, + IEmployeeSubmitDeptAssignmentAction + > +> = ( + employeeId: string, + deptAssignmentData: Partial +) => { + return async (dispatch: Dispatch) => { + dispatch({ + type: EmployeeActionTypes.SUBMIT_DEPT_ASSIGNMENT, + submitState: { state: AsyncState.Loading } + }); + + const postState = await EmployeeService.assignNewDepartment( + deptAssignmentData, + employeeId + ); + + dispatch({ + type: EmployeeActionTypes.SUBMIT_DEPT_ASSIGNMENT, + submitState: postState + }); + }; +}; + +export const submitDepartmentAssignmentUpdate: ActionCreator< + ThunkAction< + Promise, + IAppState, + null, + IEmployeeSubmitEndDeptAssignmentAction + > +> = (assignment: IDepartmentAssignmentUpdateDto) => { + return async ( + dispatch: Dispatch + ) => { + dispatch({ + type: EmployeeActionTypes.SUBMIT_END_DEPARTMENT_ASSIGNMENT, + submitState: { state: AsyncState.Loading } + }); + + const postState = await EmployeeService.updateDepartmentAssignment( + assignment + ); + + dispatch({ + type: EmployeeActionTypes.SUBMIT_END_DEPARTMENT_ASSIGNMENT, + submitState: postState + }); + }; +}; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/actions/GoalActions.ts b/LCU.WebApps.Operations.IncentiveManager/src/actions/GoalActions.ts new file mode 100644 index 0000000..3a16333 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/actions/GoalActions.ts @@ -0,0 +1,153 @@ +import { ActionCreator, Dispatch } from "redux"; +import { ThunkAction } from "redux-thunk"; +import { IMonthRange } from "../shared/types/IMonthRange"; +import { IGoalState } from "../reducers/GoalReducer"; +import AsyncState from "../shared/enumerations/AsyncState"; +import { + IDepartmentGoals, + IDepartmentGoalsForRange, + IDepartmentGoalsInput, + IEmployeeGoals, + IGoal, + IPersonnelGoals, + IPersonnelGoalsInput +} from "../shared/models/Goal"; +import IAsyncStateContainer from "../shared/types/IAsyncStateContainer"; +import GoalService from "./../shared/services/GoalService"; + +export enum GoalActionTypes { + GET_ALL_GOALS = "GET_ALL_GOALS", + GET_DEPT_GOALS = "GET_DEPT_GOALS", + GET_PERS_GOALS = "GET_PERS_GOALS", + GET_MY_GOALS = "GET_MY_GOALS", + GET_MY_DEPTS_GOALS = "GET_MY_DEPTS_GOALS" +} + +export interface IGoalGetAllAction { + type: GoalActionTypes.GET_ALL_GOALS; + retrievedGoals: IAsyncStateContainer; +} + +export interface IGoalGetMyAction { + type: GoalActionTypes.GET_MY_GOALS; + currentUserGoals: IAsyncStateContainer; +} + +export interface IGoalGetMyDeptsAction { + type: GoalActionTypes.GET_MY_DEPTS_GOALS; + currentUsersDepartmentsGoals: IAsyncStateContainer< + IDepartmentGoalsForRange[] + >; +} + +export interface IGoalGetDeptAction { + type: GoalActionTypes.GET_DEPT_GOALS; + retrievedGoals: IAsyncStateContainer; +} + +export interface IGoalGetPersAction { + type: GoalActionTypes.GET_PERS_GOALS; + retrievedGoals: IAsyncStateContainer; +} + +export type GoalActions = + | IGoalGetMyAction + | IGoalGetMyDeptsAction + | IGoalGetAllAction + | IGoalGetDeptAction + | IGoalGetPersAction; + +export const queryGoals: ActionCreator< + ThunkAction, IGoalState, null, IGoalGetAllAction> +> = (range: IMonthRange, includePersonnel: boolean, departmentId?: number) => { + return async (dispatch: Dispatch) => { + dispatch({ + type: GoalActionTypes.GET_ALL_GOALS, + retrievedGoals: { state: AsyncState.Loading } + }); + + const retrievedGoals = await GoalService.getAllGoals( + range, + includePersonnel, + departmentId + ); + + dispatch({ + type: GoalActionTypes.GET_ALL_GOALS, + retrievedGoals: retrievedGoals + }); + }; +}; + +export const getMyGoals: ActionCreator< + ThunkAction, IGoalState, null, IGoalGetMyAction> +> = (monthRange: IMonthRange) => { + return async (dispatch: Dispatch) => { + dispatch({ + type: GoalActionTypes.GET_MY_GOALS, + currentUserGoals: { state: AsyncState.Loading } + }); + + const retrievedGoals = await GoalService.getMyGoals(monthRange); + + dispatch({ + type: GoalActionTypes.GET_MY_GOALS, + currentUserGoals: retrievedGoals + }); + }; +}; + +export const getMyDepartmentsGoals: ActionCreator< + ThunkAction, IGoalState, null, IGoalGetMyDeptsAction> +> = (monthRange: IMonthRange) => { + return async (dispatch: Dispatch) => { + dispatch({ + type: GoalActionTypes.GET_MY_DEPTS_GOALS, + currentUsersDepartmentsGoals: { state: AsyncState.Loading } + }); + + const retrievedGoals = await GoalService.getMyDepartmentsGoals( + monthRange + ); + dispatch({ + type: GoalActionTypes.GET_MY_DEPTS_GOALS, + currentUsersDepartmentsGoals: retrievedGoals + }); + }; +}; + +export const getDepartmentGoals: ActionCreator< + ThunkAction, IGoalState, null, IGoalGetDeptAction> +> = (input: IDepartmentGoalsInput) => { + return async (dispatch: Dispatch) => { + dispatch({ + type: GoalActionTypes.GET_DEPT_GOALS, + retrievedGoals: { state: AsyncState.Loading } + }); + + const retrievedGoals = await GoalService.getDepartmentGoals(input); + + dispatch({ + type: GoalActionTypes.GET_DEPT_GOALS, + retrievedGoals: retrievedGoals + }); + }; +}; + +export const getPersonnelGoals: ActionCreator< + ThunkAction, IGoalState, null, IGoalGetPersAction> +> = (input: IPersonnelGoalsInput) => { + return async (dispatch: Dispatch) => { + dispatch({ + type: GoalActionTypes.GET_PERS_GOALS, + retrievedGoals: { state: AsyncState.Loading } + }); + + const retrievedGoals = await GoalService.getPersonnelGoals(input); + + dispatch({ + type: GoalActionTypes.GET_PERS_GOALS, + retrievedGoals: retrievedGoals + }); + }; +}; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/actions/PayoutActions.ts b/LCU.WebApps.Operations.IncentiveManager/src/actions/PayoutActions.ts new file mode 100644 index 0000000..8f911cb --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/actions/PayoutActions.ts @@ -0,0 +1,65 @@ +import { ActionCreator, Dispatch } from "redux"; +import { ThunkAction } from "redux-thunk"; +import { IGoalState } from "../reducers/GoalReducer"; +import AsyncState from "../shared/enumerations/AsyncState"; +import IAsyncStateContainer from "../shared/types/IAsyncStateContainer"; +import { IPayoutState } from "../reducers/PayoutReducer"; +import PayoutService from "../shared/services/PayoutService"; +import { + IEmployeePayoutsInput, + IPayoutLineItem +} from "../shared/models/Payout"; +import { IMonthRange } from "../shared/types/IMonthRange"; + +export enum PayoutActionTypes { + GET_MY_PAYOUTS = "GET_MY_PAYOUTS", + GET_EMP_PAYOUTS = "GET_EMP_PAYOUTS" +} + +export interface IPayoutsGetMyAction { + type: PayoutActionTypes.GET_MY_PAYOUTS; + currentUserPayouts: IAsyncStateContainer; +} + +export interface IPayoutsGetEmpAction { + type: PayoutActionTypes.GET_EMP_PAYOUTS; + retrievedPayouts: IAsyncStateContainer; +} + +export type PayoutActions = IPayoutsGetMyAction | IPayoutsGetEmpAction; + +export const getMyPayouts: ActionCreator< + ThunkAction, IPayoutState, null, IPayoutsGetMyAction> +> = (month: number, year: number) => { + return async (dispatch: Dispatch) => { + dispatch({ + type: PayoutActionTypes.GET_MY_PAYOUTS, + currentUserPayouts: { state: AsyncState.Loading } + }); + + const retrievedPayouts = await PayoutService.getMyPayouts(month, year); + + dispatch({ + type: PayoutActionTypes.GET_MY_PAYOUTS, + currentUserPayouts: retrievedPayouts + }); + }; +}; + +export const getEmployeePayouts: ActionCreator< + ThunkAction, IGoalState, null, IPayoutsGetEmpAction> +> = (input: IEmployeePayoutsInput) => { + return async (dispatch: Dispatch) => { + dispatch({ + type: PayoutActionTypes.GET_EMP_PAYOUTS, + retrievedPayouts: { state: AsyncState.Loading } + }); + + const retrievedPayouts = await PayoutService.getEmployeePayouts(input); + + dispatch({ + type: PayoutActionTypes.GET_EMP_PAYOUTS, + retrievedPayouts: retrievedPayouts + }); + }; +}; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/actions/ProfileActions.ts b/LCU.WebApps.Operations.IncentiveManager/src/actions/ProfileActions.ts new file mode 100644 index 0000000..b5d7e9d --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/actions/ProfileActions.ts @@ -0,0 +1,63 @@ +import { ActionCreator, Dispatch } from "redux"; +import IAsyncStateContainer from "../shared/types/IAsyncStateContainer"; +import { IAppState } from "./../store/Store"; +import { ThunkAction } from "redux-thunk"; +import AsyncState from "../shared/enumerations/AsyncState"; +import { IEmployee, IEmployeeSummary } from "../shared/models/Employee"; +import ProfileDao from "../shared/dao/ProfileDao"; + +export enum ProfileActionTypes { + REFRESH_PROFILE = "REFRESH_PROFILE", + SEARCH_PROFILE = "SEARCH_PROFILE" +} + +export interface IProfileRefreshAction { + type: ProfileActionTypes.REFRESH_PROFILE; + profile: IAsyncStateContainer; +} + +export interface IProfileSearchAction { + type: ProfileActionTypes.SEARCH_PROFILE; + searchResults: IAsyncStateContainer; +} + +export type ProfileActions = IProfileRefreshAction | IProfileSearchAction; + +export const refreshProfile: ActionCreator< + ThunkAction, IAppState, null, IProfileRefreshAction> +> = (employeeId?: string) => { + return async (dispatch: Dispatch) => { + dispatch({ + type: ProfileActionTypes.REFRESH_PROFILE, + profile: { state: AsyncState.Loading } + }); + + const retrievedProfile = await ProfileDao.getProfile(employeeId); + + dispatch({ + type: ProfileActionTypes.REFRESH_PROFILE, + profile: retrievedProfile + }); + }; +}; + +export const profileSearch: ActionCreator< + ThunkAction, IAppState, null, IProfileSearchAction> +> = (searchQuery: string, role?: string) => { + return async (dispatch: Dispatch) => { + dispatch({ + type: ProfileActionTypes.SEARCH_PROFILE, + searchResults: { state: AsyncState.Loading } + }); + + const retrievedProfile = await ProfileDao.profileSearch( + searchQuery, + role + ); + + dispatch({ + type: ProfileActionTypes.SEARCH_PROFILE, + searchResults: retrievedProfile + }); + }; +}; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/actions/RoleMappingActions.ts b/LCU.WebApps.Operations.IncentiveManager/src/actions/RoleMappingActions.ts new file mode 100644 index 0000000..81929b9 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/actions/RoleMappingActions.ts @@ -0,0 +1,72 @@ +import { Dispatch } from "react"; +import { ActionCreator } from "redux"; +import { ThunkAction } from "redux-thunk"; +import AsyncState from "../shared/enumerations/AsyncState"; +import { + IAuditableRoleMapping, + IRoleMappingChange +} from "../shared/models/RoleMapping"; +import IAsyncStateContainer from "../shared/types/IAsyncStateContainer"; +import { IAppState } from "../store/Store"; +import RoleMappingService from "../shared/services/RoleMappingService"; + +export enum RoleMappingActionTypes { + GET_ALL_ROLE_MAPPINGS = "GET_ALL_ROLE_MAPPINGS", + SET_ROLE_MAPPINGS = "SET_ROLE_MAPPINGS", + SUBMIT_ROLE_MAPPING_ASSIGNMENT_UPDATE = "SUBMIT_ROLE_MAPPING_ASSIGNMENT_UPDATE" +} + +export interface IRoleMappingsGetAllAction { + type: RoleMappingActionTypes.GET_ALL_ROLE_MAPPINGS; + roleMappings: IAsyncStateContainer; +} + +export interface IRoleMappingsSetAction { + type: RoleMappingActionTypes.SET_ROLE_MAPPINGS; + submitState: IAsyncStateContainer; +} + +export interface IRoleMappingSubmitUpdateAction { + type: RoleMappingActionTypes.SUBMIT_ROLE_MAPPING_ASSIGNMENT_UPDATE; + submitState: IAsyncStateContainer; +} + +export type RoleMappingActions = IRoleMappingsGetAllAction; + +export const getAllRoleMappings: ActionCreator< + ThunkAction, IAppState, null, IRoleMappingsGetAllAction> +> = () => { + return async (dispatch: Dispatch) => { + dispatch({ + type: RoleMappingActionTypes.GET_ALL_ROLE_MAPPINGS, + roleMappings: { state: AsyncState.Loading } + }); + + const allRoleMappings = await RoleMappingService.getAllRoleMappings(); + + dispatch({ + type: RoleMappingActionTypes.GET_ALL_ROLE_MAPPINGS, + roleMappings: allRoleMappings + }); + }; +}; + +export const submitRoleMappingUpdate: ActionCreator< + ThunkAction, IAppState, null, IRoleMappingSubmitUpdateAction> +> = (roleMapping: IRoleMappingChange) => { + return async (dispatch: Dispatch) => { + dispatch({ + type: RoleMappingActionTypes.SUBMIT_ROLE_MAPPING_ASSIGNMENT_UPDATE, + submitState: { state: AsyncState.Loading } + }); + + const postState = await RoleMappingService.assignNewRoleMapping( + roleMapping + ); + + dispatch({ + type: RoleMappingActionTypes.SUBMIT_ROLE_MAPPING_ASSIGNMENT_UPDATE, + submitState: postState + }); + }; +}; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/actions/UserAssignmentActions.ts b/LCU.WebApps.Operations.IncentiveManager/src/actions/UserAssignmentActions.ts new file mode 100644 index 0000000..06aaef3 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/actions/UserAssignmentActions.ts @@ -0,0 +1,43 @@ +import { Dispatch } from "react"; +import { ActionCreator } from "redux"; +import { ThunkAction } from "redux-thunk"; +import { IUserAssignmentState } from "../reducers/UserAssignmentReducer"; +import AsyncState from "../shared/enumerations/AsyncState"; +import { IUserAssignment } from "../shared/models/UserAssignment"; +import IAsyncStateContainer from "../shared/types/IAsyncStateContainer"; +import UserAssignmentService from "../shared/services/UserAssignmentService"; + +export enum UserAssignmentActionTypes { + GET_ALL_USER_ASSIGNMENTS = "GET_ALL_USER_ASSIGNMENTS", + SET_REGIONAL_MANAGER = "SET_REGIONAL_MANAGER" +} + +export interface IUserAssignmentGetAllAction { + type: UserAssignmentActionTypes.GET_ALL_USER_ASSIGNMENTS; + userAssignments: IAsyncStateContainer; +} + +export type UserAssignmentActions = IUserAssignmentGetAllAction; + +export const getAllUserAssignments: ActionCreator< + ThunkAction< + Promise, + IUserAssignmentState, + null, + IUserAssignmentGetAllAction + > +> = () => { + return async (dispatch: Dispatch) => { + dispatch({ + type: UserAssignmentActionTypes.GET_ALL_USER_ASSIGNMENTS, + userAssignments: { state: AsyncState.Loading } + }); + + const retrievedAssignments = await UserAssignmentService.getAllUserAssignments(); + + dispatch({ + type: UserAssignmentActionTypes.GET_ALL_USER_ASSIGNMENTS, + userAssignments: retrievedAssignments + }); + }; +}; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/authConfig.ts b/LCU.WebApps.Operations.IncentiveManager/src/authConfig.ts new file mode 100644 index 0000000..9964230 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/authConfig.ts @@ -0,0 +1,23 @@ +import { RedirectRequest } from "@azure/msal-browser"; + +export const loginConfig: RedirectRequest = { + scopes: ["User.Read"] +}; + +export const tokenConfig: RedirectRequest = { + scopes: ["e9cb601e-e98d-4881-90fc-d5758dac5ec8/.default"] +}; + +export const graphConfig = { + graphMeEndpoint: "https://graph.microsoft-ppe.com/v1.0/me" +}; + +export const securityConfig = { + auth: { + clientId: "e9cb601e-e98d-4881-90fc-d5758dac5ec8", + authority: + "https://login.microsoftonline.com/63cd1554-2783-4a86-ae50-b9e6c21c9499", + redirectUri: "/incentive-manager", + postLogoutRedirectUri: "/" + } +}; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/components/activity-detail/ActivityApproval.test.tsx b/LCU.WebApps.Operations.IncentiveManager/src/components/activity-detail/ActivityApproval.test.tsx new file mode 100644 index 0000000..a12be73 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/components/activity-detail/ActivityApproval.test.tsx @@ -0,0 +1,91 @@ +import { shallow } from "enzyme"; +import moment from "moment"; +import React from "react"; +import Product2 from "../../shared/enumerations/Product"; +import { + ActivityState, + ActivityType, + IAuditableActivity, + SystemType +} from "../../shared/models/Activity"; +import { UserType } from "../../shared/models/Auditable"; +import ActivityApproval from "./ActivityApproval"; + +describe("ActivityApproval", () => { + describe("User has permission to approve activity", () => { + // you don't need all of the old tests, because the front end doesn't do this logic anymore. + it("should display buttons when permissions present", () => { + const activity: IAuditableActivity = mockActivity(); + activity.type = ActivityType.Referral; + activity.state = ActivityState.ManualReview; + const mockAuth = { + canApproveTransferTo: true, + canApproveTransferFrom: true, + allowedChanges: [ActivityState.Accepted, ActivityState.Rejected] + }; + const wrapper = shallow( + + ); + + expect( + wrapper.find("[data-test='Accepted-activity-button']").length + ).toBe(1); + expect( + wrapper.find("[data-test='Rejected-activity-button']").length + ).toBe(1); + }); + + it("should not display buttons when not present", () => { + const activity: IAuditableActivity = mockActivity(); + activity.type = ActivityType.Referral; + activity.state = ActivityState.ManualReview; + const mockAuth = { + canApproveTransferTo: true, + canApproveTransferFrom: true, + allowedChanges: [ActivityState.Accepted] + }; + const wrapper = shallow( + + ); + + expect( + wrapper.find("[data-test='Accepted-activity-button']").length + ).toBe(1); + expect( + wrapper.find("[data-test='Rejected-activity-button']").length + ).toBe(0); + }); + }); +}); + +const mockActivity = (): IAuditableActivity => { + return { + id: 1, + product: Product2.Mortgage, + type: ActivityType.Referral, + sourceSystem: SystemType.Dna, + sourceId: "11", + sourceCreated: moment.utc(), + departmentId: "25", + employeeId: "11771", + employeeEmail: "test@test.com", + accepted: moment.utc(), + state: ActivityState.Accepted, + created: moment.utc(), + createdBy: "Created Doe", + createdByType: UserType.Associate, + lastUpdated: moment.utc(), + lastUpdatedBy: "Updated Doe", + lastUpdatedByType: UserType.Associate, + detail: {}, + activityPayouts: [] + }; +}; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/components/activity-detail/ActivityApproval.tsx b/LCU.WebApps.Operations.IncentiveManager/src/components/activity-detail/ActivityApproval.tsx new file mode 100644 index 0000000..4543a7d --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/components/activity-detail/ActivityApproval.tsx @@ -0,0 +1,90 @@ +import React from "react"; +import { Button } from "reactstrap"; +import { + ActivityState, + ActivityStateTitles, + IAuditableActivity, + IAuthorizedActivityCommands, + ManualActivityState +} from "../../shared/models/Activity"; +import moment, { Moment } from "moment"; +import PermissionGuard from "../../containers/shared/PermissionGuard"; +import PermissionCode from "../../shared/enumerations/PermissionCode"; + +interface IActivityApprovalProps { + activity: IAuditableActivity; + onSetActivityState: ( + newState: ActivityState, + newDate: Moment | undefined + ) => void; + authorization: IAuthorizedActivityCommands; +} + +const ActivityApproval: React.FC = ( + props: IActivityApprovalProps +) => { + if ( + props.activity.activityPayouts.length > 0 || + props.authorization.allowedChanges?.length === 0 + ) { + return <>; + } + + const createActionButton = ( + state: ActivityState, + newDate: Moment | undefined + ) => { + return ( + + + + e.preventDefault()} + data-test="accepted-date-input" + min={moment(moment.now()) + .subtract(1, "months") + .startOf("month") + .format("YYYY-MM-DD")} + max={moment(moment.now()) + .add(1, "months") + .format("YYYY-MM-DD")} + onChange={(event) => + (newDate = moment(event.target.value) ?? "") + } + /> + + + + + + + ); + }; + + const manualActions = Object.keys(ManualActivityState); + const availableActions = props.authorization.allowedChanges.map( + (allowed) => { + return ( + manualActions.includes(allowed) && + createActionButton(allowed, undefined) + ); + } + ); + + return <>{availableActions}; +}; + +export default ActivityApproval; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/components/activity-detail/ActivityDetailAlertBar.test.tsx b/LCU.WebApps.Operations.IncentiveManager/src/components/activity-detail/ActivityDetailAlertBar.test.tsx new file mode 100644 index 0000000..108f6fc --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/components/activity-detail/ActivityDetailAlertBar.test.tsx @@ -0,0 +1,162 @@ +import { shallow } from "enzyme"; +import React from "react"; +import AsyncState from "../../shared/enumerations/AsyncState"; +import IAsyncStateContainer from "../../shared/types/IAsyncStateContainer"; +import ActivityDetailAlertBar from "./ActivityDetailAlertBar"; + +describe("Activity Detail Alert Bar", () => { + it("should return activity failure alert if activity state is failure", () => { + const activity: IAsyncStateContainer = { + state: AsyncState.Failure, + data: "Activity Failed message" + }; + + const activityChange: IAsyncStateContainer = { + state: AsyncState.Success, + data: {} + }; + + const activityTransferChange: IAsyncStateContainer = { + state: AsyncState.Success, + data: {} + }; + + const wrapper = shallow( + + ); + + wrapper.debug(); + expect( + wrapper.find("[data-test='activity-failure-alert']").length + ).toBe(1); + expect( + wrapper + .find("[data-test='activity-failure-alert']") + .childAt(0) + .text() + ).toEqual("Activity Failed message"); + }); + + it("should return activity changes failure alert if activity change is failure", () => { + const activity: IAsyncStateContainer = { + state: AsyncState.Success, + data: {} + }; + + const activityChange: IAsyncStateContainer = { + state: AsyncState.Failure, + data: "Activity Change Failed message" + }; + + const activityTransferChange: IAsyncStateContainer = { + state: AsyncState.Success, + data: {} + }; + + const wrapper = shallow( + + ); + + expect(wrapper.find("[data-test='activity-change-alert']").length).toBe( + 1 + ); + expect( + wrapper + .find("[data-test='activity-change-alert']") + .childAt(0) + .text() + ).toEqual("Activity Change Failed message"); + }); + + it("should return loading alert if activity details are loading", () => { + const activity: IAsyncStateContainer = { + state: AsyncState.Loading + }; + + const activityChange: IAsyncStateContainer = { + state: AsyncState.Success, + data: {} + }; + + const activityTransferChange: IAsyncStateContainer = { + state: AsyncState.Success, + data: {} + }; + + const wrapper = shallow( + + ); + + expect( + wrapper.find("[data-test='activity-loading-alert']").length + ).toBe(1); + }); + + it("should return loading alert if activity state change is loading", () => { + const activity: IAsyncStateContainer = { + state: AsyncState.Success, + data: {} + }; + + const activityChange: IAsyncStateContainer = { + state: AsyncState.Loading, + data: {} + }; + + const activityTransferChange: IAsyncStateContainer = { + state: AsyncState.Success, + data: {} + }; + + const wrapper = shallow( + + ); + + expect( + wrapper.find("[data-test='activity-loading-alert']").length + ).toBe(1); + }); + + it("should return an empty fragment if no async states are failure", () => { + const activity: IAsyncStateContainer = { + state: AsyncState.Success, + data: {} + }; + + const activityChange: IAsyncStateContainer = { + state: AsyncState.Success, + data: {} + }; + + const activityTransferChange: IAsyncStateContainer = { + state: AsyncState.Success, + data: {} + }; + + const wrapper = shallow( + + ); + + expect(wrapper.length).toBe(1); + }); +}); diff --git a/LCU.WebApps.Operations.IncentiveManager/src/components/activity-detail/ActivityDetailAlertBar.tsx b/LCU.WebApps.Operations.IncentiveManager/src/components/activity-detail/ActivityDetailAlertBar.tsx new file mode 100644 index 0000000..7a730ff --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/components/activity-detail/ActivityDetailAlertBar.tsx @@ -0,0 +1,62 @@ +import React from "react"; +import { Alert } from "reactstrap"; +import IAsyncStateContainer from "../../shared/types/IAsyncStateContainer"; +import { + IAuditableActivity, + IActivityTransferAction, + IViewableActivityDetail +} from "../../shared/models/Activity"; +import AsyncState from "../../shared/enumerations/AsyncState"; +import Translate from "../../translate/Translate"; + +interface IActivityDetailAlertBarProps { + activity: IAsyncStateContainer; + activityStateChange: IAsyncStateContainer; + activityTransferChange: IAsyncStateContainer; +} + +const alertTranlate = + Translate.Get.components.activityDetails.activityDetailAlertBar; + +const ActivityDetailAlertBar: React.FC = ( + props: IActivityDetailAlertBarProps +) => { + if (props.activityStateChange.state === AsyncState.Failure) { + return ( + + {props.activityStateChange.data} + + ); + } + + if (props.activity.state === AsyncState.Failure) { + return ( + + {props.activity.data} + + ); + } + + if (props.activityTransferChange.state === AsyncState.Failure) { + return ( + + {props.activityTransferChange.data} + + ); + } + + if ( + props.activity.state === AsyncState.Loading || + props.activityStateChange.state === AsyncState.Loading || + props.activityTransferChange.state === AsyncState.Loading + ) { + return ( + + {alertTranlate.loadingMessage} + + ); + } + return <>; +}; + +export default ActivityDetailAlertBar; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/components/activity-detail/ActivityDetailDisplay.test.tsx b/LCU.WebApps.Operations.IncentiveManager/src/components/activity-detail/ActivityDetailDisplay.test.tsx new file mode 100644 index 0000000..3b8bfcd --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/components/activity-detail/ActivityDetailDisplay.test.tsx @@ -0,0 +1,24 @@ +import { shallow } from "enzyme"; +import React from "react"; +import { IActivityDetail } from "../../shared/models/Activity"; +import ActivityDetailDisplay from "./ActivityDetailDisplay"; + +describe("ActivityDetailDisplay", () => { + it("should display activity detail table", () => { + const detail = { + id: 1, + accountNumber: "11", + memberNumber: "12", + memberName: "Member Doe", + accountStatus: "Open", + accountBalance: 21, + accountNewBalance: 11 + } as IActivityDetail; + + const wrapper = shallow(); + + expect( + wrapper.find("[data-test='activity-detail-display']").length + ).toBe(1); + }); +}); diff --git a/LCU.WebApps.Operations.IncentiveManager/src/components/activity-detail/ActivityDetailDisplay.tsx b/LCU.WebApps.Operations.IncentiveManager/src/components/activity-detail/ActivityDetailDisplay.tsx new file mode 100644 index 0000000..b6fd610 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/components/activity-detail/ActivityDetailDisplay.tsx @@ -0,0 +1,145 @@ +import moment from "moment"; +import React from "react"; +import { IActivityDetail } from "../../shared/models/Activity"; +import Translate from "../../translate/Translate"; +import GoalService from "../../shared/services/GoalService"; +import { IDeadActivityDetail } from "../../shared/models/DeadActivity"; + +interface IActivityDetailDisplayProps { + detail: IActivityDetail | IDeadActivityDetail; +} + +const detailTranslate = + Translate.Get.components.activityDetails.activityDetailDisplay; + +const ActivityDetailDisplay: React.FC = ( + props: IActivityDetailDisplayProps +) => { + return ( +
+
{detailTranslate.title}
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
{detailTranslate.headers.id}{props.detail.id}
{detailTranslate.headers.dnaPersonNumber}{props.detail.dnaPersonNbr}
{detailTranslate.headers.accountNumber}{props.detail.accountNumber}
{detailTranslate.headers.subAccountNumber}{props.detail.subAccountNumber}
{detailTranslate.headers.subjectPersonNbr}{props.detail.subjectPersonNbr}
{detailTranslate.headers.subjectOrgNbr}{props.detail.subjectOrgNbr}
{detailTranslate.headers.memberName}{props.detail.memberName}
{detailTranslate.headers.stationName}{props.detail.stationName}
{detailTranslate.headers.branchOrgNbr}{props.detail.branchOrgNbr}
{detailTranslate.headers.branchOrgName}{props.detail.branchOrgName}
{detailTranslate.headers.accountStatus}{props.detail.accountStatus}
{detailTranslate.headers.accountBalance} + {GoalService.formatMonetary( + props.detail.accountBalance + )} +
{detailTranslate.headers.debtProtectionAddDate} + {props.detail.debtProtectionAddDate + ? moment( + props.detail.debtProtectionAddDate + ).format("MM-DD-YYYY") + : ""} +
+ {detailTranslate.headers.debtProtectionRemoveDate} + + {props.detail.debtProtectionRemoveDate + ? moment( + props.detail.debtProtectionRemoveDate + ).format("MM-DD-YYYY") + : ""} +
+ {detailTranslate.headers.isDebtProtectionEligible} + + {props.detail.isDebtProtectionEligible + ? "True" + : "False"} +
{detailTranslate.headers.gapInsuranceAddDate} + {props.detail.gapInsuranceAddDate + ? moment( + props.detail.gapInsuranceAddDate + ).format("MM-DD-YYYY") + : ""} +
+ {detailTranslate.headers.gapInsuranceRemoveDate} + + {props.detail.gapInsuranceRemoveDate + ? moment( + props.detail.gapInsuranceRemoveDate + ).format("MM-DD-YYYY") + : ""} +
+ {detailTranslate.headers.isGapInsuranceEligible} + + {props.detail.isGapInsuranceEligible + ? "True" + : "False"} +
+
+ ); +}; + +export default ActivityDetailDisplay; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/components/activity-detail/ActivityDetailUpdatesTable.test.tsx b/LCU.WebApps.Operations.IncentiveManager/src/components/activity-detail/ActivityDetailUpdatesTable.test.tsx new file mode 100644 index 0000000..4feca4f --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/components/activity-detail/ActivityDetailUpdatesTable.test.tsx @@ -0,0 +1,56 @@ +import { shallow } from "enzyme"; +import moment from "moment"; +import React from "react"; +import { IActivityDetailUpdate } from "../../shared/models/Activity"; +import ActivityDetailUpdatesTable from "./ActivityDetailUpdatesTable"; + +describe("ActivityDetailUpdatesTable", () => { + it("should display the changes in a table", () => { + const changes = [ + { + id: 1, + note: "Account Number change", + created: moment.utc() + }, + { + id: 2, + note: "Account Number change", + created: moment.utc() + } + ] as IActivityDetailUpdate[]; + const wrapper = shallow( + + ); + + expect( + wrapper.find("[data-test='activity-detail-updates-table']").length + ).toBe(1); + // table rows + expect( + wrapper.find("[data-test='activity-detail-updates-table'] tbody tr") + .length + ).toBe(2); + // alert component + expect( + wrapper.find("[data-test='activity-detail-updates-table-alert']") + .length + ).toBe(0); + }); + + it("should display an alert if there are no changes", () => { + const wrapper = shallow( + + ); + + // alert component + expect( + wrapper.find("[data-test='activity-detail-updates-table-alert']") + .length + ).toBe(1); + //table + expect( + wrapper.find("[data-test='activity-detail-updates-table'] table") + .length + ).toBe(0); + }); +}); diff --git a/LCU.WebApps.Operations.IncentiveManager/src/components/activity-detail/ActivityDetailUpdatesTable.tsx b/LCU.WebApps.Operations.IncentiveManager/src/components/activity-detail/ActivityDetailUpdatesTable.tsx new file mode 100644 index 0000000..3796ce6 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/components/activity-detail/ActivityDetailUpdatesTable.tsx @@ -0,0 +1,56 @@ +import moment from "moment"; +import React from "react"; +import { Alert } from "reactstrap"; +import { IActivityDetailUpdate } from "../../shared/models/Activity"; +import Translate from "../../translate/Translate"; + +interface IActivityStateChangesTableProps { + activityDetailUpdates: IActivityDetailUpdate[]; +} + +const tableTranslate = + Translate.Get.components.activityDetails.activityDetailUpdatesTable; + +const ActivityStateChangesTable: React.FC = ( + props: IActivityStateChangesTableProps +) => { + return ( +
+
{tableTranslate.title}
+ {props.activityDetailUpdates.length ? ( + + + + + + + + + + {props.activityDetailUpdates.map((change) => ( + + + + + + ))} + +
{tableTranslate.headers.note}{tableTranslate.headers.created}{tableTranslate.headers.createdBy}
{change.note} + {moment + .utc(change.created) + .local() + .format("lll")} + {change.createdBy}
+ ) : ( + + {tableTranslate.noChangesMessage} + + )} +
+ ); +}; + +export default ActivityStateChangesTable; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/components/activity-detail/ActivityDisplay.test.tsx b/LCU.WebApps.Operations.IncentiveManager/src/components/activity-detail/ActivityDisplay.test.tsx new file mode 100644 index 0000000..45a963d --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/components/activity-detail/ActivityDisplay.test.tsx @@ -0,0 +1,57 @@ +import { shallow } from "enzyme"; +import moment from "moment"; +import React from "react"; +import { + ActivityState, + ActivityType, + IAuditableActivity, + SystemType +} from "../../shared/models/Activity"; +import { UserType } from "../../shared/models/Auditable"; + +import ActivityDisplay from "./ActivityDisplay"; +import { mockProfileState } from "../../shared/testing/mocks/MockProfileState"; +import Product2 from "../../shared/enumerations/Product"; + +describe("ActivityDisplay", () => { + it("should display the activity table", () => { + const activity: IAuditableActivity = { + employeeEmail: "", + employeeId: "", + id: 1, + product: Product2.ATMCard, + type: ActivityType.Referral, + sourceSystem: SystemType.Dna, + sourceId: "11", + sourceCreated: moment.utc(), + departmentId: "25", + accepted: moment.utc(), + state: ActivityState.Accepted, + created: moment.utc(), + createdBy: "Created Doe", + createdByType: UserType.Associate, + lastUpdated: moment.utc(), + lastUpdatedBy: "Updated Doe", + lastUpdatedByType: UserType.Associate, + detail: {}, + activityPayouts: [] + }; + + let callCount = 0; + const onSetState = () => { + callCount++; + }; + const wrapper = shallow( + + ); + + expect(wrapper.find("[data-test='activity-display']").length).toBe(1); + expect(wrapper.find("[data-test='activity-display'] tr").length).toBe( + 12 + ); + }); +}); diff --git a/LCU.WebApps.Operations.IncentiveManager/src/components/activity-detail/ActivityDisplay.tsx b/LCU.WebApps.Operations.IncentiveManager/src/components/activity-detail/ActivityDisplay.tsx new file mode 100644 index 0000000..78b123a --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/components/activity-detail/ActivityDisplay.tsx @@ -0,0 +1,128 @@ +import moment, { Moment } from "moment"; +import React from "react"; +import { + ActivityState, + IAuditableActivity, + IAuthorizedActivityCommands +} from "../../shared/models/Activity"; +import Translate from "../../translate/Translate"; +import ActivityApproval from "./ActivityApproval"; +import { IEmployee } from "../../shared/models/Employee"; +import DepartmentDisplay from "../../containers/shared/DepartmentDisplay"; +import UserDisplay from "../../containers/shared/UserDisplay"; +import DateTimeService from "../../shared/services/DateTimeService"; + +interface IActivityDisplayProps { + activity: IAuditableActivity; + onSetActivityState: ( + newState: ActivityState, + newDate: Moment | undefined + ) => void; + profile: IEmployee; + authorization: IAuthorizedActivityCommands; +} + +const activityTranslate = + Translate.Get.components.activityDetails.activityDisplay; + +const ActivityDisplay: React.FC = ( + props: IActivityDisplayProps +) => { + return ( +
+
{activityTranslate.title}
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {props.activity.activityPayouts.length === 0 && ( + + props.onSetActivityState(newState, newDate) + } + authorization={props.authorization} + /> + )} + + + + + + + + + +
{activityTranslate.headers.id}{props.activity.id}
{activityTranslate.headers.product}{props.activity.product}
{activityTranslate.headers.type}{props.activity.type}
{activityTranslate.headers.sourceSystem}{props.activity.sourceSystem}
{activityTranslate.headers.sourceId}{props.activity.sourceId}
{activityTranslate.headers.sourceCreated} + {DateTimeService.CastServerTimeToClientTimeString( + props.activity.sourceCreated + )} +
{activityTranslate.headers.departmentId} + +
{activityTranslate.headers.employeeId} + +
{activityTranslate.headers.accepted} + {DateTimeService.CastServerTimeToClientTimeString( + props.activity.accepted + )} +
{activityTranslate.headers.state}{props.activity.state}
{activityTranslate.headers.createdBy} + {props.activity.createdBy} on{" "} + {DateTimeService.CastServerTimeToClientTimeString( + props.activity.created + )} +
{activityTranslate.headers.lastUpdatedBy} + {props.activity.lastUpdatedBy} on{" "} + {DateTimeService.CastServerTimeToClientTimeString( + props.activity.lastUpdated + )} +
+
+ ); +}; + +export default ActivityDisplay; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/components/activity-detail/ActivityMetaDataDisplay.tsx b/LCU.WebApps.Operations.IncentiveManager/src/components/activity-detail/ActivityMetaDataDisplay.tsx new file mode 100644 index 0000000..10ddd87 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/components/activity-detail/ActivityMetaDataDisplay.tsx @@ -0,0 +1,45 @@ +import React from "react"; +import Translate from "../../translate/Translate"; +import { IMetaDataLineItem } from "../../shared/models/Activity"; +import { Alert } from "reactstrap"; + +interface IActivityMetaDataDisplayProps { + activityMetaData: IMetaDataLineItem[]; +} + +const metaDataTranslate = + Translate.Get.components.activityDetails.activityMetaDataDisplay; + +const ActivityMetaDataDisplay: React.FC = ( + props: IActivityMetaDataDisplayProps +) => { + return ( +
+
{metaDataTranslate.title}
+ {props.activityMetaData.length ? ( + + + + + + + + + {props.activityMetaData.map((data) => ( + + + + + ))} + +
{metaDataTranslate.headers.metaData}{metaDataTranslate.headers.metaDataType}
{data.metaData}{data.metaDataType}
+ ) : ( + + {metaDataTranslate.noMetaDataMessage} + + )} +
+ ); +}; + +export default ActivityMetaDataDisplay; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/components/activity-detail/ActivityPayoutsDisplay.test.tsx b/LCU.WebApps.Operations.IncentiveManager/src/components/activity-detail/ActivityPayoutsDisplay.test.tsx new file mode 100644 index 0000000..ffb8962 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/components/activity-detail/ActivityPayoutsDisplay.test.tsx @@ -0,0 +1,60 @@ +import { shallow } from "enzyme"; +import moment from "moment"; +import React from "react"; +import { IPayoutLineItem } from "../../shared/models/Payout"; +import ActivityPayoutsDisplay from "./ActivityPayoutsDisplay"; + +describe("ActivityPayoutsDisplay", () => { + it("should display the payouts in a table", () => { + const payouts: IPayoutLineItem[] = [ + { + activityPeriod: moment.utc(), + departmentId: 25, + role: { + description: "Teller", + name: "Teller Doe", + permissions: [], + hasCommissions: true + }, + employeeId: "123", + activityTotal: 100, + payoutTotal: 200 + }, + { + activityPeriod: moment.utc(), + departmentId: 25, + role: { + description: "Branch Manager", + name: "B.M. Doe", + permissions: [], + hasCommissions: true + }, + employeeId: "456", + activityTotal: 100, + payoutTotal: 200 + } + ] as IPayoutLineItem[]; + + const wrapper = shallow(); + expect( + wrapper.find("[data-test='activity-payouts-display']").length + ).toBe(1); + // table rows + expect( + wrapper.find("[data-test='activity-payouts-display'] tbody tr") + .length + ).toBe(2); + // alert component + expect(wrapper.find("[data-test='no-payouts-alert']").length).toBe(0); + }); + + it("should display an alert if there are no payouts", () => { + const wrapper = shallow(); + // alert component + expect(wrapper.find("[data-test='no-payouts-alert']").length).toBe(1); + //table + expect( + wrapper.find("[data-test='activity-payouts-display'] table").length + ).toBe(0); + }); +}); diff --git a/LCU.WebApps.Operations.IncentiveManager/src/components/activity-detail/ActivityPayoutsDisplay.tsx b/LCU.WebApps.Operations.IncentiveManager/src/components/activity-detail/ActivityPayoutsDisplay.tsx new file mode 100644 index 0000000..11f82a8 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/components/activity-detail/ActivityPayoutsDisplay.tsx @@ -0,0 +1,58 @@ +import React from "react"; +import Translate from "../../translate/Translate"; +import { IPayoutLineItem } from "../../shared/models/Payout"; +import moment from "moment"; +import { Alert } from "reactstrap"; + +interface IActivityPayoutsDisplayProps { + payouts: IPayoutLineItem[]; +} + +const payoutsTranslate = + Translate.Get.components.activityDetails.activityPayoutsDisplay; + +const ActivityPayoutsDisplay: React.FC = ( + props: IActivityPayoutsDisplayProps +) => { + return ( +
+
{payoutsTranslate.title}
+ {props.payouts.length ? ( + + + + + + + + + + + + + {props.payouts.map((payout) => ( + + + + + + + + + ))} + +
{payoutsTranslate.headers.period}{payoutsTranslate.headers.departmentId}{payoutsTranslate.headers.role}{payoutsTranslate.headers.employeeId}{payoutsTranslate.headers.activityTotal}{payoutsTranslate.headers.payoutTotal}
+ {moment + .utc(payout.activityPeriod) + .format("ll")} + {payout.departmentId}{payout.role?.description}{payout.employeeId}{payout.activityTotal}{payout.payoutTotal}
+ ) : ( + + {payoutsTranslate.noPayoutsMessage} + + )} +
+ ); +}; + +export default ActivityPayoutsDisplay; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/components/activity-detail/ActivityStateChangesTable.test.tsx b/LCU.WebApps.Operations.IncentiveManager/src/components/activity-detail/ActivityStateChangesTable.test.tsx new file mode 100644 index 0000000..f64dc8b --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/components/activity-detail/ActivityStateChangesTable.test.tsx @@ -0,0 +1,58 @@ +import { shallow } from "enzyme"; +import moment from "moment"; +import React from "react"; +import { IActivityStateChange } from "../../shared/models/Activity"; +import ActivityStateChangesTable from "./ActivityStateChangesTable"; + +describe("ActivityStateChangesTable", () => { + it("should display the changes in a table", () => { + const changes = [ + { + id: 1, + state: "Created", + note: "Auto created", + created: moment.utc() + }, + { + id: 2, + state: "Created", + note: "Auto created", + created: moment.utc() + } + ] as IActivityStateChange[]; + const wrapper = shallow( + + ); + + expect( + wrapper.find("[data-test='activity-state-changes-table']").length + ).toBe(1); + // table rows + expect( + wrapper.find("[data-test='activity-state-changes-table'] tbody tr") + .length + ).toBe(2); + // alert component + expect( + wrapper.find("[data-test='activity-state-changes-table-alert']") + .length + ).toBe(0); + }); + + it("should display an alert if there are no changes", () => { + const wrapper = shallow( + + ); + + // alert component + expect( + wrapper.find("[data-test='activity-state-changes-table-alert']") + .length + ).toBe(1); + //table + expect( + wrapper.find("[data-test='activity-state-changes-table'] table") + .length + ).toBe(0); + }); +}); diff --git a/LCU.WebApps.Operations.IncentiveManager/src/components/activity-detail/ActivityStateChangesTable.tsx b/LCU.WebApps.Operations.IncentiveManager/src/components/activity-detail/ActivityStateChangesTable.tsx new file mode 100644 index 0000000..3bf4ace --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/components/activity-detail/ActivityStateChangesTable.tsx @@ -0,0 +1,83 @@ +import moment from "moment"; +import React from "react"; +import { Alert } from "reactstrap"; +import { IActivityStateChange } from "../../shared/models/Activity"; +import Translate from "../../translate/Translate"; +import Configuration from "../../shared/configuration/Configuration"; + +interface IActivityStateChangesTableProps { + activityStateChanges: IActivityStateChange[]; +} + +const tableTranslate = + Translate.Get.components.activityDetails.activityStateChangesTable; + +const urlPrefix = Configuration.PrefixAppUrlWithBasename( + Configuration.Get.appUrls.activity +); + +const ActivityStateChangesTable: React.FC = ( + props: IActivityStateChangesTableProps +) => { + // If there is a matching activity, make it a link. + const getNote = (change: IActivityStateChange) => { + if (change.activityMatchId) { + return ( + + {change.note} + + ); + } + return change.note; + }; + + return ( +
+
{tableTranslate.title}
+ {props.activityStateChanges.length ? ( + + + + + + + + + + + + {props.activityStateChanges.map((change) => ( + + + + + + + + ))} + +
{tableTranslate.headers.state}{tableTranslate.headers.accepted}{tableTranslate.headers.note}{tableTranslate.headers.created}{tableTranslate.headers.createdBy}
{change.state} + {change.acceptedDate == null + ? "" + : moment(change.acceptedDate).format( + "ll" + )} + {getNote(change)} + {moment + .utc(change.created) + .local() + .format("lll")} + {change.createdBy}
+ ) : ( + + {tableTranslate.noChangesMessage} + + )} +
+ ); +}; + +export default ActivityStateChangesTable; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/components/activity-detail/ActivityTransferApproval.test.tsx b/LCU.WebApps.Operations.IncentiveManager/src/components/activity-detail/ActivityTransferApproval.test.tsx new file mode 100644 index 0000000..f2812f9 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/components/activity-detail/ActivityTransferApproval.test.tsx @@ -0,0 +1,263 @@ +import { shallow } from "enzyme"; +import moment from "moment"; +import React from "react"; +import PermissionCode from "../../shared/enumerations/PermissionCode"; +import Product2 from "../../shared/enumerations/Product"; +import { + ActivityState, + ActivityTransferState, + ActivityType, + IActivityTransferAction, + IAuditableActivity, + SystemType +} from "../../shared/models/Activity"; +import { UserType } from "../../shared/models/Auditable"; +import { IEmployee } from "../../shared/models/Employee"; +import ActivityTransferApproval from "./ActivityTransferApproval"; + +describe("ActivityTransferApproval", () => { + it("should not display if the transfer action is undefined", () => { + const mockAuth = { + canApproveTransferTo: true, + canApproveTransferFrom: true, + allowedChanges: [] + }; + const wrapper = shallow( + + ); + + expect( + wrapper.find("[data-test='activity-approval-component']").length + ).toBe(0); + }); + + it("should display if the transfer action exists", () => { + const userProfile = mockProfile(); + userProfile.departments = []; + const mockAuth = { + canApproveTransferTo: false, + canApproveTransferFrom: false, + allowedChanges: [] + }; + const wrapper = shallow( + + ); + + expect( + wrapper.find("[data-test='activity-approval-component']").length + ).toBe(1); + expect( + wrapper.find("[data-test='approve-transfer-button']").length + ).toBe(0); + expect( + wrapper.find("[data-test='reject-transfer-button']").length + ).toBe(0); + }); + + it("should display approval buttons if profile has a department manager department assignment", () => { + const mockAuth = { + canApproveTransferTo: true, + canApproveTransferFrom: true, + allowedChanges: [] + }; + const wrapper = shallow( + + ); + + expect( + wrapper.find("[data-test='approve-transfer-button']").length + ).toBe(1); + expect( + wrapper.find("[data-test='reject-transfer-button']").length + ).toBe(1); + }); + + it("should display approval buttons if profile has a department manager department assignment", () => { + const mockAuth = { + canApproveTransferTo: true, + canApproveTransferFrom: true, + allowedChanges: [] + }; + const wrapper = shallow( + + ); + + expect( + wrapper.find("[data-test='approve-transfer-button']").length + ).toBe(1); + expect( + wrapper.find("[data-test='reject-transfer-button']").length + ).toBe(1); + }); + + it("should not display approval buttons if profile has been approved for the department", () => { + const mockAuth = { + canApproveTransferTo: false, + canApproveTransferFrom: false, + allowedChanges: [] + }; + const activityFromDept25 = mockActivity(); + // set up transfer approved by the appUser's department + const transferAction = mockActivityTransferAction(); + transferAction.newActivityDepartmentId = 1; + transferAction.requestedDepartmentApprover = "Aaron Manager"; + + const wrapper = shallow( + jest.fn()} + /> + ); + + expect( + wrapper.find("[data-test='approve-transfer-button']").length + ).toBe(0); + expect( + wrapper.find("[data-test='reject-transfer-button']").length + ).toBe(0); + }); + + it("should call the approveTransfer function when buttons are clicked", () => { + const mockAuth = { + canApproveTransferTo: true, + canApproveTransferFrom: true, + allowedChanges: [] + }; + const approveTransferSpy = jest.fn(); + const wrapper = shallow( + + ); + + wrapper.find("[data-test='approve-transfer-button']").simulate("click"); + expect(approveTransferSpy).toHaveBeenCalledWith( + 11, + ActivityTransferState.Accepted + ); + expect(approveTransferSpy).toHaveBeenCalledTimes(1); + wrapper.find("[data-test='reject-transfer-button']").simulate("click"); + expect(approveTransferSpy).toHaveBeenCalledWith( + 11, + ActivityTransferState.Rejected + ); + expect(approveTransferSpy).toHaveBeenCalledTimes(2); + }); +}); + +const mockProfile = (): IEmployee => { + return { + adEmail: "test@test.com", + personId: 11, + employeeId: "11771", + homeDepartment: "HOME", + baseRole: "QA", + fullName: "John Tester", + role: { + name: "Admin", + description: "admin", + permissions: [ + { + code: PermissionCode.ASSIGN_ASSOCIATES_TO_DEPARTMENT, + description: "ASSIGN_ASSOCIATES_TO_DEPARTMENT" + } + ], + hasCommissions: false, + rank: 99 + }, + additionalPermissions: [], + departments: [ + { + employeeId: "", + departmentId: 25, + startDate: moment(), + note: "", + isHome: true, + slidingPayout: 100, + role: { + name: "Manager", + description: "Manager", + hasCommissions: true, + permissions: [], + rank: 99 + }, + created: moment(), + createdBy: "", + createdByType: UserType.Associate, + lastUpdated: moment(), + lastUpdatedBy: "", + lastUpdatedByType: UserType.Associate + } + ], + assignments: [] + }; +}; + +const mockActivity = (): IAuditableActivity => { + return { + id: 1, + product: ("ATMCard" as unknown) as Product2, + type: ActivityType.Referral, + sourceSystem: SystemType.Dna, + sourceId: "11", + sourceCreated: moment.utc(), + departmentId: "25", + employeeId: "2", + employeeEmail: "update@landmarkcu.com", + accepted: moment.utc(), + state: ActivityState.Accepted, + created: moment.utc(), + createdBy: "Created Doe", + createdByType: UserType.Associate, + lastUpdated: moment.utc(), + lastUpdatedBy: "Updated Doe", + lastUpdatedByType: UserType.Associate, + detail: { + id: 1, + accountNumber: "11", + dnaPersonNbr: "12", + memberName: "Member Doe", + accountStatus: "Open", + accountBalance: 21, + accountNewBalance: 11 + }, + metaData: [], + activityPayouts: [] + }; +}; + +const mockActivityTransferAction = (): IActivityTransferAction => { + return { + id: 11, + activityStateBeforeTransfer: ActivityState.Accepted, + newActivityDepartmentId: 25, + newActivityEmployeeId: "C0002", + requestedActivityId: 1, + state: ActivityTransferState.Requested + }; +}; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/components/activity-detail/ActivityTransferApproval.tsx b/LCU.WebApps.Operations.IncentiveManager/src/components/activity-detail/ActivityTransferApproval.tsx new file mode 100644 index 0000000..d4463be --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/components/activity-detail/ActivityTransferApproval.tsx @@ -0,0 +1,124 @@ +import React from "react"; +import { Button } from "reactstrap"; +import DepartmentDisplay from "../../containers/shared/DepartmentDisplay"; +import PermissionGuard from "../../containers/shared/PermissionGuard"; +import PermissionCode from "../../shared/enumerations/PermissionCode"; +import { + ActivityTransferState, + IActivity, + IActivityTransferAction, + IAuthorizedActivityCommands +} from "../../shared/models/Activity"; +import Translate from "../../translate/Translate"; + +interface IActivityTransferApproval { + transferAction?: IActivityTransferAction; + activity: IActivity; + authorization: IAuthorizedActivityCommands; + approveTransfer: ( + requestId: number, + decision: ActivityTransferState + ) => void; +} + +const approvalTranslate = + Translate.Get.components.activityDetails.activityTransferApproval; + +const ActivityTransferApproval: React.FC = ( + props: IActivityTransferApproval +) => { + const canApproveForCurrentDepartment = + props.authorization.canApproveTransferFrom; + const canApproveForNewDepartment = props.authorization.canApproveTransferTo; + + return props.transferAction ? ( +
+
{approvalTranslate.title}
+ + + + + + + + + + + + + + + + + +
{approvalTranslate.headers.department}{approvalTranslate.headers.employeeId} + {approvalTranslate.headers.approvalFrom} + + + {approvalTranslate.headers.approvalFrom} + +
+ + {props.transferAction.newActivityEmployeeId} + {props.transferAction.requestedDepartmentApprover || + approvalTranslate.pending} + + {props.transferAction.newDepartmentApprover || + approvalTranslate.pending} +
+ {(canApproveForCurrentDepartment || canApproveForNewDepartment) && ( + +
+ + +
+
+ )} +
+ ) : ( + <> + ); +}; + +export default ActivityTransferApproval; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/components/activity-detail/ActivityTransferRequest.test.tsx b/LCU.WebApps.Operations.IncentiveManager/src/components/activity-detail/ActivityTransferRequest.test.tsx new file mode 100644 index 0000000..67efd94 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/components/activity-detail/ActivityTransferRequest.test.tsx @@ -0,0 +1,154 @@ +import { shallow } from "enzyme"; +import moment from "moment"; +import React from "react"; +import Product2 from "../../shared/enumerations/Product"; +import { + ActivityState, + ActivityType, + IActivity, + SystemType +} from "../../shared/models/Activity"; +import ActivityTransferRequest from "./ActivityTransferRequest"; + +describe("ActivityTransferRequest", () => { + it("should call props onFormSubmit with selected department and employee", () => { + const mockFormSubmit = jest.fn(); + const mockActivity: IActivity = { + id: 1, + product: Product2.ATMCard, + sourceSystem: SystemType.Dna, + activityPayouts: [], + departmentId: "1", + detail: {}, + employeeEmail: "employee@landmarkcu.com", + employeeId: "C0000", + type: ActivityType.Sale, + state: ActivityState.Accepted + }; + + const wrapper = shallow( + + ); + + expect( + wrapper + .find("[data-test='activity-transfer-request-button']") + .prop("disabled") + ).toBe(true); + + wrapper + .find("UserSearchSelect") + .prop<(field: string, newValue: any) => void>("onUserSelected")( + "25", + "C002" + ); + + expect( + wrapper + .find("[data-test='activity-transfer-request-button']") + .prop("disabled") + ).toBe(true); + + wrapper.find("[data-test='dept-input']").prop("onChange")({ + target: { value: "25" } + }); + expect( + wrapper + .find("[data-test='activity-transfer-request-button']") + .prop("disabled") + ).toBe(false); + + wrapper + .find("[data-test='activity-transfer-request-button']") + .simulate("click"); + + expect(mockFormSubmit).toHaveBeenCalledTimes(1); + expect(mockFormSubmit).toHaveBeenLastCalledWith("25", "C002"); + }); + + it("should not display if the activity has payouts", () => { + const mockFormSubmit = jest.fn(); + const mockActivity: IActivity = { + id: 1, + product: Product2.ATMCard, + sourceSystem: SystemType.Dna, + activityPayouts: [ + { + payoutLineItem: { + id: 1, + activityPeriod: moment.utc(), + departmentId: 25, + role: { + description: "Teller", + name: "Teller Doe", + permissions: [], + hasCommissions: true, + rank: 1 + }, + product: "ATMCard", + goalId: 2, + goalValue: 3, + commissionId: 4, + commissionValue: 5, + employeeId: "123", + activityTotal: 100, + payoutTotal: 200, + slidingPayout: 12, + activityPayouts: undefined, + payoutProfileId: undefined + } + } + ], + departmentId: "1", + detail: {}, + employeeEmail: "employee@landmarkcu.com", + employeeId: "C0000", + type: ActivityType.Sale, + state: ActivityState.Accepted + }; + + const wrapper = shallow( + + ); + + expect(wrapper.find("[data-test='transfer-request-form']").length).toBe( + 0 + ); + }); + + it("should not display if the activity is in PendingTransfer state", () => { + const mockFormSubmit = jest.fn(); + const mockActivity: IActivity = { + id: 1, + product: Product2.ATMCard, + sourceSystem: SystemType.Dna, + activityPayouts: [], + departmentId: "1", + detail: {}, + employeeEmail: "employee@landmarkcu.com", + employeeId: "C0000", + type: ActivityType.Sale, + state: ActivityState.PendingTransfer + }; + + const wrapper = shallow( + + ); + + expect(wrapper.find("[data-test='transfer-request-form']").length).toBe( + 0 + ); + }); +}); diff --git a/LCU.WebApps.Operations.IncentiveManager/src/components/activity-detail/ActivityTransferRequest.tsx b/LCU.WebApps.Operations.IncentiveManager/src/components/activity-detail/ActivityTransferRequest.tsx new file mode 100644 index 0000000..e49e554 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/components/activity-detail/ActivityTransferRequest.tsx @@ -0,0 +1,117 @@ +import React, { useState } from "react"; +import { Button, FormGroup, Input, Label } from "reactstrap"; +import PermissionGuard from "../../containers/shared/PermissionGuard"; +import PermissionCode from "../../shared/enumerations/PermissionCode"; +import { IActivity } from "../../shared/models/Activity"; +import { IDepartment } from "../../shared/models/Department"; +import ActivityService from "../../shared/services/ActivityService"; +import Translate from "../../translate/Translate"; +import UserSearchSelect from "../activity/manual-activity-wizard/UserSearchSelect"; + +interface IActivityTransferRequestForm { + departmentId: string; + employeeId: string; +} + +interface IActivityTransferRequestProps { + departments: IDepartment[]; + activity: IActivity; + onFormSubmit: (departmentId: string, employeeId: string) => void; +} + +const formTranslate = + Translate.Get.components.activityDetails.activityTransferRequest; + +const ActivityTransferRequest: React.FC = ( + props: IActivityTransferRequestProps +) => { + const [formState, setFormState] = useState({ + departmentId: "", + employeeId: "" + }); + + const submitRequest = () => { + props.onFormSubmit(formState.departmentId, formState.employeeId); + }; + const departments = props.departments; + + // BUG? Did they tell you this is wrong and that it should work like approvals? + // OK. Just inject the activity.data.authorized from the parent and check if it can go to "PendingTransfer". + // For now they haven't asked for this, so still use the permission. + return ActivityService.isEligbleForTransfer(props.activity) ? ( + +
+
{formTranslate.title}
+ + + { + setFormState((prevState) => ({ + departmentId: prevState.departmentId, + employeeId: newValue + })); + }} + showValidation={false} + /> + + + + { + const departmentId = event.target.value; + setFormState((prevState) => ({ + departmentId, + employeeId: prevState.employeeId + })); + }} + > + + {departments.map((department) => { + return ( + + ); + })} + + + +
+
+ ) : ( + <> + ); +}; + +export default ActivityTransferRequest; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/components/activity-detail/ActivityTransferToOwnDepartmentRequest.test.tsx b/LCU.WebApps.Operations.IncentiveManager/src/components/activity-detail/ActivityTransferToOwnDepartmentRequest.test.tsx new file mode 100644 index 0000000..193374b --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/components/activity-detail/ActivityTransferToOwnDepartmentRequest.test.tsx @@ -0,0 +1,372 @@ +import { shallow } from "enzyme"; +import moment from "moment"; +import React from "react"; +import Product2 from "../../shared/enumerations/Product"; +import { + ActivityState, + ActivityType, + IActivity, + SystemType +} from "../../shared/models/Activity"; +import ActivityTransferToOwnDepartmentRequest from "./ActivityTransferToOwnDepartmentRequest"; +import { IDepartmentAssignment } from "../../shared/models/DepartmentAssignment"; +import { UserType } from "../../shared/models/Auditable"; + +describe("ActivityTransferToOwnDepartmentRequest", () => { + it("should call props onFormSubmit with selected department and current activity employeeId", () => { + const mockFormSubmit = jest.fn(); + + const mockOwnedDepartments: IDepartmentAssignment[] = [ + { + id: 3, + employeeId: "C0000", + departmentId: 12, + startDate: moment(), + endDate: undefined, + note: "Test", + isHome: true, + slidingPayout: 1, + created: moment(), + createdBy: "George Frick", + createdByType: UserType.Associate, + lastUpdatedBy: "Incentive Manager", + lastUpdated: moment(), + lastUpdatedByType: UserType.System, + role: { + name: "test role", + rank: 1, + description: "test test", + hasCommissions: false, + permissions: [ + { + code: "Secret", + description: "Secret Test Code" + } + ] + } + }, + { + id: 4, + employeeId: "C0000", + departmentId: 14, + startDate: moment(), + endDate: undefined, + note: "Test", + isHome: true, + slidingPayout: 1, + created: moment(), + createdBy: "George Frick", + createdByType: UserType.Associate, + lastUpdatedBy: "Incentive Manager", + lastUpdated: moment(), + lastUpdatedByType: UserType.System, + role: { + name: "test role", + rank: 1, + description: "test test", + hasCommissions: false, + permissions: [ + { + code: "Secret", + description: "Secret Test Code" + } + ] + } + } + ]; + + const mockActivity: IActivity = { + id: 1, + product: Product2.ATMCard, + sourceSystem: SystemType.Dna, + activityPayouts: [], + departmentId: "1", + detail: {}, + employeeEmail: "employee@landmarkcu.com", + employeeId: "C0000", + type: ActivityType.Sale, + state: ActivityState.Accepted + }; + + const wrapper = shallow( + + ); + + expect( + wrapper + .find( + "[data-test='activity-transfer-request-own-department-button']" + ) + .prop("disabled") + ).toBe(true); + + wrapper.find("[data-test='dept-input']").prop("onChange")({ + target: { value: "25" } + }); + + expect( + wrapper + .find( + "[data-test='activity-transfer-request-own-department-button']" + ) + .prop("disabled") + ).toBe(false); + + wrapper + .find( + "[data-test='activity-transfer-request-own-department-button']" + ) + .simulate("click"); + + expect(mockFormSubmit).toHaveBeenCalledTimes(1); + expect(mockFormSubmit).toHaveBeenLastCalledWith("25", "C0000"); + }); + + it("it should not display if the activity is not owned by the current appUser", () => { + const mockFormSubmit = jest.fn(); + + const mockOwnedDepartments: IDepartmentAssignment[] = [ + { + id: 3, + employeeId: "C0000", + departmentId: 12, + startDate: moment(), + endDate: undefined, + note: "Test", + isHome: true, + slidingPayout: 1, + created: moment(), + createdBy: "George Frick", + createdByType: UserType.Associate, + lastUpdatedBy: "Incentive Manager", + lastUpdated: moment(), + lastUpdatedByType: UserType.System, + role: { + name: "test role", + rank: 1, + description: "test test", + hasCommissions: false, + permissions: [ + { + code: "Secret", + description: "Secret Test Code" + } + ] + } + }, + { + id: 4, + employeeId: "C0000", + departmentId: 14, + startDate: moment(), + endDate: undefined, + note: "Test", + isHome: true, + slidingPayout: 1, + created: moment(), + createdBy: "George Frick", + createdByType: UserType.Associate, + lastUpdatedBy: "Incentive Manager", + lastUpdated: moment(), + lastUpdatedByType: UserType.System, + role: { + name: "test role", + rank: 1, + description: "test test", + hasCommissions: false, + permissions: [ + { + code: "Secret", + description: "Secret Test Code" + } + ] + } + } + ]; + + const mockActivity: IActivity = { + id: 1, + product: Product2.ATMCard, + sourceSystem: SystemType.Dna, + activityPayouts: [], + departmentId: "1", + detail: {}, + employeeEmail: "employee@landmarkcu.com", + employeeId: "C0000", + type: ActivityType.Sale, + state: ActivityState.Accepted + }; + + const wrapper = shallow( + + ); + + expect( + wrapper.find("[data-test='transfer-request-own-department-form']") + .length + ).toBe(0); + }); + + it("it should not display if the appUser only has one current department", () => { + const mockFormSubmit = jest.fn(); + + const mockOwnedDepartments: IDepartmentAssignment[] = [ + { + id: 3, + employeeId: "C0000", + departmentId: 12, + startDate: moment(), + endDate: undefined, + note: "Test", + isHome: true, + slidingPayout: 1, + created: moment(), + createdBy: "George Frick", + createdByType: UserType.Associate, + lastUpdatedBy: "Incentive Manager", + lastUpdated: moment(), + lastUpdatedByType: UserType.System, + role: { + name: "test role", + rank: 1, + description: "test test", + hasCommissions: false, + permissions: [ + { + code: "Secret", + description: "Secret Test Code" + } + ] + } + } + ]; + + const mockActivity: IActivity = { + id: 1, + product: Product2.ATMCard, + sourceSystem: SystemType.Dna, + activityPayouts: [], + departmentId: "1", + detail: {}, + employeeEmail: "employee@landmarkcu.com", + employeeId: "C0000", + type: ActivityType.Sale, + state: ActivityState.Accepted + }; + + const wrapper = shallow( + + ); + + expect( + wrapper.find("[data-test='transfer-request-own-department-form']") + .length + ).toBe(0); + }); + + it("should not display if the activity has payouts", () => { + const mockFormSubmit = jest.fn(); + const mockActivity: IActivity = { + id: 1, + product: Product2.ATMCard, + sourceSystem: SystemType.Dna, + activityPayouts: [ + { + payoutLineItem: { + id: 1, + activityPeriod: moment.utc(), + departmentId: 25, + role: { + description: "Teller", + name: "Teller Doe", + permissions: [], + hasCommissions: true, + rank: 1 + }, + product: "ATMCard", + goalId: 2, + goalValue: 3, + commissionId: 4, + commissionValue: 5, + employeeId: "123", + activityTotal: 100, + payoutTotal: 200, + slidingPayout: 12, + activityPayouts: undefined, + payoutProfileId: undefined + } + } + ], + departmentId: "1", + detail: {}, + employeeEmail: "employee@landmarkcu.com", + employeeId: "C0000", + type: ActivityType.Sale, + state: ActivityState.Accepted + }; + + const wrapper = shallow( + + ); + + expect( + wrapper.find("[data-test='transfer-request-own-department-form']") + .length + ).toBe(0); + }); + + it("should not display if the activity is in PendingTransfer state", () => { + const mockFormSubmit = jest.fn(); + const mockActivity: IActivity = { + id: 1, + product: Product2.ATMCard, + sourceSystem: SystemType.Dna, + activityPayouts: [], + departmentId: "1", + detail: {}, + employeeEmail: "employee@landmarkcu.com", + employeeId: "C0000", + type: ActivityType.Sale, + state: ActivityState.PendingTransfer + }; + + const wrapper = shallow( + + ); + + expect( + wrapper.find("[data-test='transfer-request-own-department-form']") + .length + ).toBe(0); + }); +}); diff --git a/LCU.WebApps.Operations.IncentiveManager/src/components/activity-detail/ActivityTransferToOwnDepartmentRequest.tsx b/LCU.WebApps.Operations.IncentiveManager/src/components/activity-detail/ActivityTransferToOwnDepartmentRequest.tsx new file mode 100644 index 0000000..af9a33f --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/components/activity-detail/ActivityTransferToOwnDepartmentRequest.tsx @@ -0,0 +1,114 @@ +import React, { useState } from "react"; +import { Button, FormGroup, Input, Label } from "reactstrap"; +import { IActivity } from "../../shared/models/Activity"; +import { IDepartment } from "../../shared/models/Department"; +import ActivityService from "../../shared/services/ActivityService"; +import Translate from "../../translate/Translate"; +import { IDepartmentAssignment } from "../../shared/models/DepartmentAssignment"; + +interface IActivityTransferRequestForm { + departmentId: string; + employeeId: string; +} + +interface IActivityTransferToOwnDepartmentRequestProps { + ownedDepartments: IDepartmentAssignment[] | undefined; + allDepartments: IDepartment[]; + activity: IActivity; + currentEmployeeId: string; + onFormSubmit: (departmentId: string, employeeId: string) => void; +} + +const formTranslate = + Translate.Get.components.activityDetails + .activityTransferToOwnDepartmentRequest; + +const ActivityTransferToOwnDepartmentRequest: React.FC = ( + props: IActivityTransferToOwnDepartmentRequestProps +) => { + const [formState, setFormState] = useState({ + departmentId: "", + employeeId: props.activity.employeeId + }); + + const submitRequest = () => { + props.onFormSubmit(formState.departmentId, formState.employeeId); + }; + + function isActivityOwnedByUserWithMultipleDepartments(): boolean { + return ( + props.ownedDepartments != null && + props.ownedDepartments.length >= 2 && + props.currentEmployeeId == props.activity.employeeId + ); + } + + // BUG? Did they tell you this is wrong and that it should work like approvals? + // OK. Just inject the activity.data.authorized from the parent and check if it can go to "PendingTransfer". + // For now they haven't asked for this, so still use the permission. + return ActivityService.isEligbleForTransfer(props.activity) && + isActivityOwnedByUserWithMultipleDepartments() ? ( +
+
{formTranslate.title}
+ + + { + const departmentId = event.target.value; + setFormState((prevState) => ({ + departmentId, + employeeId: prevState.employeeId + })); + }} + > + + {props.allDepartments.map((department) => { + if ( + props.ownedDepartments?.some( + (ownedDepartment) => + ownedDepartment.departmentId === + department.id && + department.id.toString() != + props.activity.departmentId + ) + ) + return ( + + ); + })} + + + +
+ ) : ( + <> + ); +}; + +export default ActivityTransferToOwnDepartmentRequest; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/components/activity/activity-audit-log/ActivityAuditLog.test.tsx b/LCU.WebApps.Operations.IncentiveManager/src/components/activity/activity-audit-log/ActivityAuditLog.test.tsx new file mode 100644 index 0000000..4721059 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/components/activity/activity-audit-log/ActivityAuditLog.test.tsx @@ -0,0 +1,140 @@ +import { shallow } from "enzyme"; +import React from "react"; +import ActivityAuditLog from "./ActivityAuditLog"; +import AsyncState from "./../../../shared/enumerations/AsyncState"; +import { IActivitySearchForm } from "../../../shared/models/Activity"; + +describe("An associate opens the Activity Audit Log", () => { + let prevCalled = 0; + let nextCalled = 0; + const getPrev = () => { + prevCalled++; + }; + const getNext = () => { + nextCalled++; + }; + + it("displays loading message when loading", () => { + let testCalls = 0; + let lastFilter: IActivitySearchForm = { + query: "", + dateHigh: null, + dateLow: null, + state: "", + product: "", + employeeId: "", + departmentId: 0 + }; + const testCall = (sort: string) => { + testCalls++; + }; + + let wrapper = shallow( + + ); + expect( + wrapper.find("Alert").find('[color="warning"]').prop("hidden") + ).toBe(false); + expect( + wrapper.find("Alert").find('[color="danger"]').prop("hidden") + ).toBe(true); + }); + + it("displays loading message when error", () => { + let testCalls = 0; + let lastFilter: IActivitySearchForm = { + query: "", + dateHigh: null, + dateLow: null, + state: "", + product: "", + employeeId: "", + departmentId: 0 + }; + const testCall = (sort: string) => { + testCalls++; + }; + let wrapper = shallow( + + ); + expect( + wrapper.find("Alert").find('[color="warning"]').prop("hidden") + ).toBe(true); + expect( + wrapper.find("Alert").find('[color="danger"]').prop("hidden") + ).toBe(false); + }); + + it("displays no message when success", () => { + let testCalls = 0; + let lastFilter: IActivitySearchForm = { + query: "", + dateHigh: null, + dateLow: null, + state: "", + product: "", + employeeId: "", + departmentId: 0 + }; + const testCall = (sort: string) => { + testCalls++; + }; + let wrapper = shallow( + + ); + expect( + wrapper.find("Alert").find('[color="warning"]').prop("hidden") + ).toBe(true); + expect( + wrapper.find("Alert").find('[color="danger"]').prop("hidden") + ).toBe(true); + }); +}); diff --git a/LCU.WebApps.Operations.IncentiveManager/src/components/activity/activity-audit-log/ActivityAuditLog.tsx b/LCU.WebApps.Operations.IncentiveManager/src/components/activity/activity-audit-log/ActivityAuditLog.tsx new file mode 100644 index 0000000..b6b298c --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/components/activity/activity-audit-log/ActivityAuditLog.tsx @@ -0,0 +1,80 @@ +import React from "react"; +import { Alert, Col, Row } from "reactstrap"; +import { + IActivitySearchForm, + IAuditableActivity +} from "../../../shared/models/Activity"; +import Translate from "./../../../translate/Translate"; +import IAsyncStateContainer from "../../../shared/types/IAsyncStateContainer"; +import AsyncState from "../../../shared/enumerations/AsyncState"; +import ActivityTable from "../activity-table/ActivityTable"; +import IPagedResponse from "../../../shared/types/IPagedResponse"; +import PagedContentControls from "../../paged-content-controls/PagedContentControls"; + +export interface IActivityAuditLogProps { + activityContainer: IAsyncStateContainer>; + getNextPage: () => void; + getPrevPage: () => void; + changeSort: (sort: string) => void; + form: IActivitySearchForm; +} + +const ActivityAuditLogTranslate = + Translate.Get.components.activity.activityAuditLog; + +const ActivityAuditLog: React.FC = (props) => { + const changeSort = (headerName: string) => { + if (props.changeSort) { + props.changeSort(headerName); + } + }; + + return ( +
+
+ + +
+ props.getNextPage()} + prevPage={() => props.getPrevPage()} + pagedResponse={props.activityContainer.data} + /> +
+ +
+ props.getNextPage()} + prevPage={() => props.getPrevPage()} + pagedResponse={props.activityContainer.data} + /> +
+
+
+ ); +}; + +export default ActivityAuditLog; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/components/activity/activity-table/ActivitySummaryTable.tsx b/LCU.WebApps.Operations.IncentiveManager/src/components/activity/activity-table/ActivitySummaryTable.tsx new file mode 100644 index 0000000..b011ff8 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/components/activity/activity-table/ActivitySummaryTable.tsx @@ -0,0 +1,71 @@ +import "./ActivityTable.scss"; +import React from "react"; +import { IAuditableActivity } from "./../../../shared/models/Activity"; +import moment from "moment"; +import Translate from "./../../../translate/Translate"; +import { AutoTable } from "../../auto-table/AutoTable"; +import Configuration from "../../../shared/configuration/Configuration"; +import DepartmentDisplay from "../../../containers/shared/DepartmentDisplay"; +import UserDisplay from "../../../containers/shared/UserDisplay"; +import DateTimeService from "../../../shared/services/DateTimeService"; + +interface IActivityTable { + activities?: IAuditableActivity[]; +} + +const ActivityTableTranslate = Translate.Get.components.activity.activityTable; + +const urlPrefix = Configuration.PrefixAppUrlWithBasename( + Configuration.Get.appUrls.activity +); + +const ActivitySummaryTable: React.FC = (props) => { + const sortByUpdatedDate = ( + data: IAuditableActivity[] + ): IAuditableActivity[] => { + return data.sort((a, b) => moment(b.lastUpdated).diff(a.lastUpdated)); + }; + + const headers = [ + ActivityTableTranslate.headers.lastUpdated, + ActivityTableTranslate.headers.id, + ActivityTableTranslate.headers.createdBy, + ActivityTableTranslate.headers.sourceSystem, + ActivityTableTranslate.headers.detailAccountNumber, + ActivityTableTranslate.headers.detailSubjectPersonNbr, + ActivityTableTranslate.headers.detailMemberName + ]; + + const cells = [ + (x: IAuditableActivity) => + DateTimeService.CastServerTimeToClientTimeString(x.lastUpdated), + (x: IAuditableActivity) => ( + + {x.id} + + ), + (x: IAuditableActivity) => x.createdBy, + (x: IAuditableActivity) => x.sourceSystem, + (x: IAuditableActivity) => x.detail?.accountNumber ?? "-", + (x: IAuditableActivity) => x.detail?.subjectPersonNbr ?? "-", + (x: IAuditableActivity) => x.detail?.memberName ?? "-" + ]; + + if (props.activities && props.activities.length > 0) { + return ( + + ); + } + + return ; +}; + +export default ActivitySummaryTable; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/components/activity/activity-table/ActivityTable.scss b/LCU.WebApps.Operations.IncentiveManager/src/components/activity/activity-table/ActivityTable.scss new file mode 100644 index 0000000..ebac25a --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/components/activity/activity-table/ActivityTable.scss @@ -0,0 +1,9 @@ +.scrollable-top-parent { + width: 95vw; + overflow-x: auto; + transform: rotateX(180deg); +} + +.scrollable-top-child { + transform: rotateX(180deg); +} diff --git a/LCU.WebApps.Operations.IncentiveManager/src/components/activity/activity-table/ActivityTable.test.tsx b/LCU.WebApps.Operations.IncentiveManager/src/components/activity/activity-table/ActivityTable.test.tsx new file mode 100644 index 0000000..314265d --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/components/activity/activity-table/ActivityTable.test.tsx @@ -0,0 +1,61 @@ +import { shallow } from "enzyme"; +import React from "react"; +import ActivityTable from "./ActivityTable"; +import { + ActivityState, + ActivityType, + IAuditableActivity, + SystemType +} from "../../../shared/models/Activity"; +import { UserType } from "../../../shared/models/Auditable"; +import Product2 from "../../../shared/enumerations/Product"; +import moment from "moment"; + +describe("An associate views the activity table", () => { + it("Displays a message when there is no data to load", () => { + let wrapper = shallow(); + expect(wrapper.find('[data-test="no-data-message"]').length).toBe(1); + expect(wrapper.find("[data-test='autotable-container']").length).toBe( + 0 + ); + }); + + it("Displays a message when there is no data to load", () => { + let wrapper = shallow(); + expect(wrapper.find('[data-test="no-data-message"]').length).toBe(1); + expect(wrapper.find("[data-test='autotable-container']").length).toBe( + 0 + ); + }); + + it("Does not display message when there is data", () => { + const activities: IAuditableActivity[] = [ + { + id: 100, + lastUpdatedByType: UserType.System, + lastUpdatedBy: "Tester", + lastUpdated: moment().utc(), + createdByType: UserType.System, + createdBy: "Tester1", + created: moment().utc(), + employeeId: "11771", + departmentId: "56", + accepted: moment().utc(), + detail: {}, + product: Product2.Vehicle, + sourceCreated: moment().utc(), + sourceId: "11566", + sourceSystem: SystemType.Dna, + state: ActivityState.Accepted, + type: ActivityType.Sale, + activityPayouts: [], + employeeEmail: "" + } + ]; + let wrapper = shallow(); + expect(wrapper.find("[data-test='no-data-message']").length).toBe(0); + expect(wrapper.find("[data-test='autotable-container']").length).toBe( + 1 + ); + }); +}); diff --git a/LCU.WebApps.Operations.IncentiveManager/src/components/activity/activity-table/ActivityTable.tsx b/LCU.WebApps.Operations.IncentiveManager/src/components/activity/activity-table/ActivityTable.tsx new file mode 100644 index 0000000..310c1bd --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/components/activity/activity-table/ActivityTable.tsx @@ -0,0 +1,164 @@ +import "./ActivityTable.scss"; +import React from "react"; +import { + ActivityState, + ActivityType, + IAuditableActivity +} from "../../../shared/models/Activity"; +import Translate from "./../../../translate/Translate"; +import { + AutoTable, + IAutoTableHeader, + IColumnSort +} from "../../auto-table/AutoTable"; +import Configuration from "../../../shared/configuration/Configuration"; +import DepartmentDisplay from "../../../containers/shared/DepartmentDisplay"; +import UserDisplay from "../../../containers/shared/UserDisplay"; +import DateTimeService from "../../../shared/services/DateTimeService"; + +export interface IActivityTable { + activities?: IAuditableActivity[]; + changeSort?: (sort: string) => void; + sortBy?: string; + sortDescending?: boolean; + activityTypeFilter?: ActivityType; + activityStateFilter?: ActivityState[]; +} + +const ActivityTableTranslate = Translate.Get.components.activity.activityTable; + +const urlPrefix = Configuration.PrefixAppUrlWithBasename( + Configuration.Get.appUrls.activity +); + +const ActivityTable: React.FC = (props) => { + const headers = [ + ActivityTableTranslate.headers.accepted, + ActivityTableTranslate.headers.id, + ActivityTableTranslate.headers.product, + ActivityTableTranslate.headers.type, + ActivityTableTranslate.headers.createdBy, + ActivityTableTranslate.headers.sourceSystem, + ActivityTableTranslate.headers.departmentId, + ActivityTableTranslate.headers.personId, + ActivityTableTranslate.headers.state, + ActivityTableTranslate.headers.detailAccountNumber, + ActivityTableTranslate.headers.detailSubjectPersonNbr, + ActivityTableTranslate.headers.detailSubjectOrgNbr, + ActivityTableTranslate.headers.detailMemberName + ]; + + const fieldNames = [ + "Accepted", + "Id", + "Product", + "Type", + "CreatedBy", + "SourceSystem", + "", + "", + "State", + "", + "", + "" + ]; + + const changeSort = (headerName: string) => { + if (props.changeSort) { + props.changeSort(headerName); + } + }; + + const sortHeaders: IAutoTableHeader[] = headers.map((headerName, idx) => { + return { + display: headerName, + onClick: () => { + if (fieldNames[idx] !== "") { + changeSort(fieldNames[idx]); + } + } + }; + }); + + const cells = [ + (x: IAuditableActivity) => + DateTimeService.CastServerTimeToClientTimeString(x.accepted), + (x: IAuditableActivity) => ( + + {x.id} + + ), + (x: IAuditableActivity) => x.product, + (x: IAuditableActivity) => x.type, + (x: IAuditableActivity) => x.createdBy, + (x: IAuditableActivity) => x.sourceSystem, + (x: IAuditableActivity) => ( + + ), + (x: IAuditableActivity) => , + (x: IAuditableActivity) => x.state, + (x: IAuditableActivity) => x.detail?.accountNumber ?? "-", + (x: IAuditableActivity) => x.detail?.subjectPersonNbr ?? "-", + (x: IAuditableActivity) => x.detail?.subjectOrgNbr ?? "-", + (x: IAuditableActivity) => x.detail?.memberName ?? "-" + ]; + + let activities = props.activities || []; + if (props.activityTypeFilter) { + activities = activities.filter( + (activity) => activity.type === props.activityTypeFilter + ); + } + + if (props.activityStateFilter && props.activityStateFilter.length > 0) { + activities = activities.filter((activity) => + props.activityStateFilter?.includes(activity.state) + ); + } + + const findIndexForColumn = (column?: string) => { + return fieldNames.indexOf(column || "LastUpdated"); + }; + + const x: IColumnSort = { + index: findIndexForColumn(props.sortBy), + isReversed: props.sortDescending ?? false + }; + + if (activities.length > 0) { + return ( +
+ +
+ ); + } + + return ( +
+
+

+ {ActivityTableTranslate.noData} +

+
+ +
+ ); +}; + +export default ActivityTable; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/components/activity/activity-table/ActivityTableWithStateFilter.tsx b/LCU.WebApps.Operations.IncentiveManager/src/components/activity/activity-table/ActivityTableWithStateFilter.tsx new file mode 100644 index 0000000..86dc909 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/components/activity/activity-table/ActivityTableWithStateFilter.tsx @@ -0,0 +1,71 @@ +import React, { useState } from "react"; +import { + ActivityState, + IActivitySearchForm +} from "../../../shared/models/Activity"; +import ActivityTable, { + IActivityTable +} from "../../activity/activity-table/ActivityTable"; +import Translate from "../../../translate/Translate"; +import { FormGroup, Label } from "reactstrap"; +import Select from "react-select"; + +const ActivityTableTranslate = Translate.Get.components.activity.activityTable; + +const ActivityTableWithStateFilter: React.FC = ( + props: IActivityTable +) => { + const [form, setForm] = useState({ + state: ["Accepted"], + dateHigh: null, + dateLow: null, + query: "", + product: "", + type: "", + departmentId: 0, + employeeId: "" + }); + + const activityStateOptions = Object.values(ActivityState).map( + (state) => new Option(state, state) + ); + + const acceptedActivityStateOption = activityStateOptions.find( + (state) => state.value === "Accepted" + ); + + const updateState = (newState: string[]) => { + setForm({ + ...form, + state: newState + }); + }; + + return ( +
+ + + { + onFormDataChange( + "detailAccountNumber", + event.target.value + ); + }} + required={props.requireAccountNumberField} + invalid={ + props.formData.showValidation && + !props.formData.detailAccountNumber + } + type="text" + placeholder={ + activityDetailsFormTranslate.accountNumber + .placeholder + } + /> + + Please enter an account number. + + + + + + + { + onFormDataChange( + "detailSubAccountNumber", + event.target.value + ); + }} + required={false} + invalid={ + props.formData.showValidation && + !props.formData.detailSubAccountNumber + } + type="text" + placeholder={ + activityDetailsFormTranslate + .detailSubAccountNumber.placeholder + } + /> + + Please enter sub account number + + + + + + + { + onFormDataChange( + "detailSubjectPersonNbr", + event.target.value + ); + }} + required={props.formData.detailSubjectOrgNbr === ""} + invalid={ + props.formData.showValidation && + !props.formData.detailSubjectOrgNbr && + !props.formData.detailSubjectPersonNbr + } + type="text" + placeholder={ + activityDetailsFormTranslate.memberNumber + .placeholder + } + /> + + Please enter a member number or organization number. + + + + + + + { + onFormDataChange( + "detailSubjectOrgNbr", + event.target.value + ); + }} + required={ + props.formData.detailSubjectPersonNbr === "" + } + invalid={ + props.formData.showValidation && + !props.formData.detailSubjectOrgNbr && + !props.formData.detailSubjectPersonNbr + } + type="text" + placeholder={ + activityDetailsFormTranslate.orgNumber + .placeholder + } + /> + + Please enter a member number or organization number. + + + + + + + { + onFormDataChange( + "sourceId", + event.target.value + ); + }} + /> + + Please enter the Id from the source system + + + + + + + + + { + onFormDataChange( + "detailMemberName", + event.target.value + ); + }} + required + invalid={ + props.formData.showValidation && + !props.formData.detailMemberName + } + type="text" + placeholder={ + activityDetailsFormTranslate.memberName + .placeholder + } + /> + Please enter member name. + + + + + + { + onFormDataChange( + "detailDebtProtectionAddDate", + moment(event.target.value) + ); + }} + type="datetime-local" + /> + + + + + + { + onFormDataChange( + "detailDebtProtectionRemoveDate", + moment(event.target.value) + ); + }} + type="datetime-local" + /> + + + + + + { + onFormDataChange( + "detailIsDebtProtectionEligible", + event.target.checked + ); + }} + type="checkbox" + /> + + + + + + + + + { + onFormDataChange( + "detailAccountStatus", + event.target.value + ); + }} + required={props.requireAccountStatusField} + invalid={ + props.requireAccountStatusField && + props.formData.showValidation && + !props.formData.detailAccountStatus + } + type="text" + placeholder={ + activityDetailsFormTranslate.accountStatus + .placeholder + } + /> + + Account Status is required for this Product/Activity + Type combination. + + + + + + + { + onFormDataChange( + "detailAccountBalance", + event.target.value + ); + }} + required={props.requireAccountBalanceField} + invalid={ + props.requireAccountBalanceField && + ((props.formData.showValidation && + !props.formData.detailAccountBalance) || + !regex.test( + props.formData.detailAccountBalance + "" + )) + } + placeholder={ + activityDetailsFormTranslate.accountBalance + .placeholder + } + /> + + {regex.test( + props.formData.detailAccountBalance + "" + ) + ? "Account Balance is required for this Product/Activity Type combination." + : "Valid Form: #########.##"} + + + + +
+ ); +}; + +export default ActivityDetailsForm; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/components/activity/manual-activity-wizard/ActivityProductForm.test.tsx b/LCU.WebApps.Operations.IncentiveManager/src/components/activity/manual-activity-wizard/ActivityProductForm.test.tsx new file mode 100644 index 0000000..28232c2 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/components/activity/manual-activity-wizard/ActivityProductForm.test.tsx @@ -0,0 +1,354 @@ +/* eslint-disable @typescript-eslint/quotes */ +import { mount } from "enzyme"; +import React from "react"; +import ActivityProductForm from "./ActivityProductForm"; +import { + ActivityState, + ActivityType, + generateDefaultActivityForm, + IManualActivityFormState, + ManualActivityState, + SystemType +} from "./../../../shared/models/Activity"; +import { IProductDetail, Product2 } from "../../../shared/enumerations/Product"; +import moment from "moment"; +import Translate from "../../../translate/Translate"; +import UserSearchSelect from "./UserSearchSelect"; + +const activityProductFormTranslate = + Translate.Get.components.activity.manualActivityWizard.activityProductForm; + +describe("A appUser inputs data into the Activity Product Form", () => { + it("Defaults Drop Downs When First Accessing the Page", () => { + const formData: IManualActivityFormState = generateDefaultActivityForm(); + + const wrapper = mount( + { + console.log("🎈"); + }} + departments={[]} + products={productDetails()} + /> + ); + + const activityTypeInput = wrapper + .find('[name="ActivityType"]') + .find("Input"); + expect(activityTypeInput.length).toBe(Object.keys(ActivityType).length); + + let productOptions = wrapper + .find('[name="ActivityProduct"]') + .find("option") + .map((x) => x.text()); + expect(productOptions).toEqual([ + "Product Type...", + "New Checking", + "Gap Insurance" + ]); + + const activityProduct = wrapper + .find('[name="ActivityProduct"]') + .find("Input"); + expect(activityProduct.prop("value")).toBe(""); + + const activityState = wrapper + .find('[name="ActivityState"]') + .find("Input"); + expect(activityState.find("option").length).toBe( + Object.keys(ManualActivityState).length + ); + expect(activityState.prop("value")).toBe("Accepted"); + }); + + it("Builds State From Input Changes", () => { + let formData: IManualActivityFormState = { + product: "", + type: ActivityType.Referral, + departmentId: "", + employeeId: undefined, + accepted: undefined, + state: ActivityState.Accepted + }; + + const wrapper = mount( + { + formData = newstate; + }} + departments={[]} + products={productDetails()} + /> + ); + + //Have to manually update the formData using setProps to mock that things are hooked up to wizard + wrapper + .find('[name="ActivityProduct"]') + .find("Input") + .simulate("change", { target: { value: Product2.GAPInsurance } }); + wrapper.setProps({ formData: formData }); + wrapper.update(); + + wrapper + .find('[name="ActivityState"]') + .find("Input") + .simulate("change", { + target: { value: ActivityState.PendingReferral } + }); + wrapper.setProps({ formData: formData }); + wrapper.update(); + + wrapper.find(UserSearchSelect).prop("onUserSelected")( + "employeeId", + "Marty McFly" + ); + wrapper.setProps({ formData: formData }); + wrapper.update(); + + wrapper + .find('[name="CreditToDepartment"]') + .find("Input") + .simulate("change", { target: { value: 119 } }); + wrapper.setProps({ formData: formData }); + wrapper.update(); + + wrapper + .find('[name="AcceptedStateDateTime"]') + .find("Input") + .simulate("change", { target: { value: moment("Oct 21 1995") } }); + wrapper.setProps({ formData: formData }); + wrapper.update(); + + expect(formData).toEqual({ + type: ActivityType.Referral, + product: Product2.GAPInsurance, + departmentId: 119, + employeeId: "Marty McFly", + accepted: moment("Oct 21 1995"), + state: ActivityState.PendingReferral + }); + }); + + describe("Valid product and activity type selection", () => { + it("displays valid products for sale activity type", () => { + let formData: IManualActivityFormState = { + product: "", + type: ActivityType.Sale, + departmentId: "", + employeeId: undefined, + accepted: undefined, + state: ActivityState.Accepted + }; + + const wrapper = mount( + { + formData = newstate; + }} + departments={[]} + products={productDetails()} + /> + ); + + let productOptions = wrapper + .find('[name="ActivityProduct"]') + .find("option") + .map((x) => x.text()); + expect(productOptions).toEqual([ + "Product Type...", + "New Checking", + "Gap Insurance" + ]); + }); + + it("displays valid products for referral activity type", () => { + let formData: IManualActivityFormState = { + product: "", + type: ActivityType.Referral, + departmentId: "", + employeeId: undefined, + accepted: undefined, + state: ActivityState.Accepted + }; + + const wrapper = mount( + { + formData = newstate; + }} + departments={[]} + products={productDetails()} + /> + ); + + let productOptions = wrapper + .find('[name="ActivityProduct"]') + .find("option") + .map((x) => x.text()); + expect(productOptions).toEqual([ + "Product Type...", + "ATM Card", + "CCBT", + "New Checking", + "Credit Card" + ]); + }); + + it("displays valid activity types for selected product", () => { + let formData: IManualActivityFormState = { + product: "", + type: ActivityType.Sale, + departmentId: "", + employeeId: undefined, + accepted: undefined, + state: ActivityState.Accepted, + sourceSystem: SystemType.Manual, + sourceCreated: moment(), + showValidation: false + }; + + const wrapper = mount( + { + formData = newstate; + }} + departments={[]} + products={productDetails()} + /> + ); + + // undefined selected + let activityTypeOptions = wrapper + .find('[name="ActivityType"]') + .find("Input") + .map((x) => x.prop("value")); + expect(activityTypeOptions).toEqual(["Other", "Referral", "Sale"]); + + // sale and referral product + wrapper + .find('[name="ActivityProduct"]') + .find("Input") + .simulate("change", { + target: { value: "NewChecking" } + }); + wrapper.setProps({ formData: formData }); + wrapper.update(); + + activityTypeOptions = wrapper + .find('[name="ActivityType"]') + .find("Input") + .map((x) => x.prop("value")); + expect(activityTypeOptions).toEqual(["Referral", "Sale"]); + + //Referral, other product + wrapper + .find('[name="ActivityProduct"]') + .find("Input") + .simulate("change", { + target: { value: "ATMCard" } + }); + wrapper.setProps({ formData: formData }); + wrapper.update(); + + activityTypeOptions = wrapper + .find('[name="ActivityType"]') + .find("Input") + .map((x) => x.prop("value")); + expect(activityTypeOptions).toEqual(["Other", "Referral"]); + + // sale only product + wrapper + .find('[name="ActivityProduct"]') + .find("Input") + .simulate("change", { + target: { value: "GAPInsurance" } + }); + wrapper.setProps({ formData: formData }); + wrapper.update(); + + activityTypeOptions = wrapper + .find('[name="ActivityType"]') + .find("Input") + .map((x) => x.prop("value")); + expect(activityTypeOptions).toEqual(["Sale"]); + + // referral only product + wrapper + .find('[name="ActivityProduct"]') + .find("Input") + .simulate("change", { + target: { value: "CreditCardBalanceTransfer" } + }); + wrapper.setProps({ formData: formData }); + wrapper.update(); + + activityTypeOptions = wrapper + .find('[name="ActivityType"]') + .find("Input") + .map((x) => x.prop("value")); + expect(activityTypeOptions).toEqual(["Referral"]); + + // referral only product + wrapper + .find('[name="ActivityProduct"]') + .find("Input") + .simulate("change", { + target: { value: "CreditCardBalanceTransfer" } + }); + wrapper.setProps({ formData: formData }); + wrapper.update(); + + activityTypeOptions = wrapper + .find('[name="ActivityType"]') + .find("Input") + .map((x) => x.prop("value")); + expect(activityTypeOptions).toEqual(["Referral"]); + }); + }); +}); + +const productDetails = () => { + return [ + { + product: "ATMCard", + title: "ATM Card", + isSellable: false, + isReferrable: true, + isNonPaying: true + }, + { + product: "CreditCardBalanceTransfer", + title: "CCBT", + isSellable: false, + isReferrable: true, + isNonPaying: false + }, + { + product: "NewChecking", + title: "New Checking", + isSellable: true, + isReferrable: true, + isNonPaying: false + }, + { + product: "GAPInsurance", + title: "Gap Insurance", + isSellable: true, + isReferrable: false, + isNonPaying: false + }, + { + product: "CreditCard", + title: "Credit Card", + isSellable: false, + isReferrable: true, + isNonPaying: false + } + ] as IProductDetail[]; +}; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/components/activity/manual-activity-wizard/ActivityProductForm.tsx b/LCU.WebApps.Operations.IncentiveManager/src/components/activity/manual-activity-wizard/ActivityProductForm.tsx new file mode 100644 index 0000000..9565f0a --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/components/activity/manual-activity-wizard/ActivityProductForm.tsx @@ -0,0 +1,277 @@ +import moment from "moment"; +import React from "react"; +import { Col, FormFeedback, FormGroup, Input, Label, Row } from "reactstrap"; +import { IProductDetail } from "../../../shared/enumerations/Product"; +import { ManualActivityState } from "../../../shared/models/Activity"; +import { IManualActivityFormState } from "../../../shared/models/Activity"; +import { tryUpdateStateWithInput } from "../../../shared/services/InputService"; +import Translate from "./../../../translate/Translate"; +import { IDepartment } from "../../../shared/models/Department"; +import ActivityService from "../../../shared/services/ActivityService"; +import ProductService from "../../../shared/services/ProductService"; +import UserSearchSelect from "./UserSearchSelect"; + +type ActivityProductFormData = Pick< + IManualActivityFormState, + | "type" + | "product" + | "departmentId" + | "employeeId" + | "state" + | "accepted" + | "showValidation" +>; + +export interface IActivityProductFormProps { + /** + * The slice of state ActivityProductform is responsible for, to update the fields update the + * ActivityProductsFromData type in the component see example for shape. + * @example type ActivtyProductFormData = Pick; + */ + formData: ActivityProductFormData; + /**Dispatches the entire slice of state every time it changes. + * Use the spread operator to apply this to the parent state + * @param newData the entire slice of state handled by the form, re-built with latest change + */ + handleChange: (newData: ActivityProductFormData) => void; + departments: IDepartment[]; + products: IProductDetail[]; +} + +const activityProductFormTranslate = + Translate.Get.components.activity.manualActivityWizard.activityProductForm; + +const ActivityProductForm: React.FC = ( + props: IActivityProductFormProps +) => { + const onFormDataChange = (field: string, newValue: any) => { + const updatedFormData = tryUpdateStateWithInput( + props.formData, + field, + newValue + ); + props.handleChange(updatedFormData); + }; + const allowedProducts = ProductService.getValidProductsForActivityType( + props.products, + props.formData.type + ); + const selectedProduct = props.products.find( + (product) => product.product === props.formData.product + ); + const allowedActivityTypes = ActivityService.getValidActivityTypesForProduct( + selectedProduct + ); + + return ( + <> + + {activityProductFormTranslate.formLabel} + + + + + + {allowedActivityTypes.map((activityType) => { + return ( + <> + +
+ + ); + })} +
+ + + + + { + onFormDataChange("product", event.target.value); + }} + > + + {allowedProducts.map((product) => { + return ( + + ); + })} + + Please select a value. + + + + + + { + onFormDataChange( + "departmentId", + event.target.value + ); + }} + > + + {props.departments.map((department) => { + return ( + + ); + })} + + Please select a value. + + +
+ + + + + + + + + + + { + onFormDataChange("state", event.target.value); + }} + > + {Object.values(ManualActivityState).map( + (activityState) => { + return ( + + ); + } + )} + + + + + + + { + onFormDataChange( + "accepted", + event.target.value + ? moment(event.target.value) + : "" + ); + }} + /> + Accepted Date is required. + + + + + + ); +}; + +export default ActivityProductForm; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/components/activity/manual-activity-wizard/ActivitySourceForm.test.tsx b/LCU.WebApps.Operations.IncentiveManager/src/components/activity/manual-activity-wizard/ActivitySourceForm.test.tsx new file mode 100644 index 0000000..c63b80d --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/components/activity/manual-activity-wizard/ActivitySourceForm.test.tsx @@ -0,0 +1,114 @@ +import { mount } from "enzyme"; +import React from "react"; +import ActivitySourceForm from "./ActivitySourceForm"; +import { + ActivityState, + ActivityType, + IManualActivityFormState, + SystemType +} from "./../../../shared/models/Activity"; +import moment from "moment"; +import Translate from "./../../../translate/Translate"; + +describe("A appUser inputing data into the Activity Source Form", () => { + it("Defaults Drop Downs When First Accessing the Page", () => { + const formData: IManualActivityFormState = { + sourceCreated: undefined, + sourceId: undefined, + sourceSystem: undefined + }; + + const wrapper = mount( + { + console.log("🎈"); + }} + /> + ); + + const activitySourceType = wrapper + .find('[name="SourceSystemType"]') + .find("Input"); + expect(activitySourceType.find("option").length).toBe( + Object.keys(SystemType).length + 1 + ); + expect(activitySourceType.prop("value")).toBe( + Translate.Get.components.activity.manualActivityWizard + .activitySourceForm.sourceSystemType.placeholder + ); + }); + + it("Does not allow default after selection made", () => { + const formData: IManualActivityFormState = { + sourceCreated: undefined, + sourceId: undefined, + sourceSystem: SystemType.Dna + }; + + const wrapper = mount( + { + console.log("🎈"); + }} + /> + ); + + const activityTypeInput = wrapper + .find('[name="SourceSystemType"]') + .find("Input"); + + expect( + activityTypeInput + .find("option") + .findWhere((x) => x.prop("hidden") === true).length + ).toBe(1); + }); + + it("Builds State From Input Changes", () => { + let formData: IManualActivityFormState = { + sourceCreated: undefined, + sourceId: undefined, + sourceSystem: undefined + }; + + const wrapper = mount( + { + formData = newData; + }} + /> + ); + + //Have to manually update the formData using setProps to mock that things are hooked up to wizard + + wrapper + .find('[name="SourceSystemType"]') + .find("Input") + .simulate("change", { target: { value: SystemType.Allied } }); + wrapper.setProps({ formData: formData }); + wrapper.update(); + + wrapper + .find('[name="SourceSystemId"]') + .find("Input") + .simulate("change", { target: { value: "101" } }); + wrapper.setProps({ formData: formData }); + wrapper.update(); + + wrapper + .find('[name="SourceCreatedDateTime"]') + .find("Input") + .simulate("change", { target: { value: moment("May 12 2020") } }); + wrapper.setProps({ formData: formData }); + wrapper.update(); + + expect(formData).toEqual({ + sourceSystem: SystemType.Allied, + sourceId: "101", + sourceCreated: moment("May 12 2020") + }); + }); +}); diff --git a/LCU.WebApps.Operations.IncentiveManager/src/components/activity/manual-activity-wizard/ActivitySourceForm.tsx b/LCU.WebApps.Operations.IncentiveManager/src/components/activity/manual-activity-wizard/ActivitySourceForm.tsx new file mode 100644 index 0000000..765b7b9 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/components/activity/manual-activity-wizard/ActivitySourceForm.tsx @@ -0,0 +1,146 @@ +import moment from "moment"; +import React from "react"; +import { Row, Col, Input, Label, FormGroup } from "reactstrap"; +import { + IManualActivityFormState, + SystemType +} from "../../../shared/models/Activity"; +import { tryUpdateStateWithInput } from "../../../shared/services/InputService"; +import Translate from "../../../translate/Translate"; + +type ActivitySourceFormData = Pick< + IManualActivityFormState, + "sourceCreated" | "sourceId" | "sourceSystem" +>; + +export interface IActivitySourceFormProps { + /** + * The slice of state ActivitySourceForm is responsible for, to update the fields update the + * ActivitySourceFormData type in the component see example for shape. + * @example type ActivtyProductFormData = Pick + */ + formData: ActivitySourceFormData; + /**Dispatches the entire slice of state every time it changes. + * Use the spread operator to apply this to the parent state + * @param newData the entire slice of state handled by the form, re-built with latest change + */ + handleChange: (newData: ActivitySourceFormData) => void; +} + +const activitySourceFormTranslate = + Translate.Get.components.activity.manualActivityWizard.activitySourceForm; + +const ActivitySourceForm: React.FC = ( + props: IActivitySourceFormProps +) => { + const onFormDataChange = (field: string, newValue: any) => { + const updatedFormData = tryUpdateStateWithInput( + props.formData, + field, + newValue + ); + props.handleChange(updatedFormData); + }; + + return ( +
+ + {activitySourceFormTranslate.formLabel} + + + + + + { + onFormDataChange( + "sourceSystem", + event.target.value + ); + }} + > + + {Object.values(SystemType).map((systemType) => { + return ( + + ); + })} + + + + + + + { + onFormDataChange( + "sourceId", + event.target.value + ); + }} + /> + + + + + + { + onFormDataChange( + "sourceCreated", + moment(event.target.value) + ); + }} + type="datetime-local" + /> + + + +
+ ); +}; + +export default ActivitySourceForm; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/components/activity/manual-activity-wizard/ActivityWizardAlertBar.test.tsx b/LCU.WebApps.Operations.IncentiveManager/src/components/activity/manual-activity-wizard/ActivityWizardAlertBar.test.tsx new file mode 100644 index 0000000..ef76132 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/components/activity/manual-activity-wizard/ActivityWizardAlertBar.test.tsx @@ -0,0 +1,71 @@ +import React from "react"; +import ActivityWizardAlertBar from "./ActivityWizardAlertBar"; +import { shallow } from "enzyme"; +import AsyncState from "../../../shared/enumerations/AsyncState"; + +describe("After a appUser submits, Activity Wizard Alert Bar Displays the proper message", () => { + let state: AsyncState; + + beforeEach(() => { + state = AsyncState.Inactive; + }); + + it("Displays nothing at start", () => { + let wrapper = shallow(); + expect(wrapper.find('[data-test="Success"]').prop("hidden")).toBe(true); + expect(wrapper.find('[data-test="Failure"]').prop("hidden")).toBe(true); + expect(wrapper.find('[data-test="Loading"]').prop("hidden")).toBe(true); + }); + + it("Displays loading message when submit loading", () => { + state = AsyncState.Loading; + let wrapper = shallow(); + expect(wrapper.find('[data-test="Success"]').prop("hidden")).toBe(true); + expect(wrapper.find('[data-test="Failure"]').prop("hidden")).toBe(true); + expect(wrapper.find('[data-test="Loading"]').prop("hidden")).toBe( + false + ); + }); + + it("Displays success message when submit success", () => { + state = AsyncState.Success; + let wrapper = shallow(); + expect(wrapper.find('[data-test="Success"]').prop("hidden")).toBe( + false + ); + expect(wrapper.find('[data-test="Failure"]').prop("hidden")).toBe(true); + expect(wrapper.find('[data-test="Loading"]').prop("hidden")).toBe(true); + }); + + it("Displays error message when submit Fails", () => { + state = AsyncState.Failure; + let wrapper = shallow(); + expect(wrapper.find('[data-test="Success"]').prop("hidden")).toBe(true); + expect(wrapper.find('[data-test="Failure"]').prop("hidden")).toBe( + false + ); + expect(wrapper.find('[data-test="Loading"]').prop("hidden")).toBe(true); + // expect( + // wrapper + // .find('[data-test="Failure"]') + // .find('[data-test="ErrorMessageFromStatus"]') + // .text() + // ).toContain("oof"); + }); + + it("Displays error message when submit Invalid", () => { + state = AsyncState.Invalid; + let wrapper = shallow(); + expect(wrapper.find('[data-test="Success"]').prop("hidden")).toBe(true); + expect(wrapper.find('[data-test="Failure"]').prop("hidden")).toBe( + false + ); + expect(wrapper.find('[data-test="Loading"]').prop("hidden")).toBe(true); + // expect( + // wrapper + // .find('[data-test="Failure"]') + // .find('[data-test="ErrorMessageFromStatus"]') + // .text() + // ).toContain("oof"); + }); +}); diff --git a/LCU.WebApps.Operations.IncentiveManager/src/components/activity/manual-activity-wizard/ActivityWizardAlertBar.tsx b/LCU.WebApps.Operations.IncentiveManager/src/components/activity/manual-activity-wizard/ActivityWizardAlertBar.tsx new file mode 100644 index 0000000..e0065da --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/components/activity/manual-activity-wizard/ActivityWizardAlertBar.tsx @@ -0,0 +1,66 @@ +import React from "react"; +import { Alert, Spinner } from "reactstrap"; +import Translate from "../../../translate/Translate"; +import AsyncState from "../../../shared/enumerations/AsyncState"; + +export interface IActivityWizardAlertBarProps { + submitStatus: AsyncState; +} + +const ActivityWizardAlertBar: React.FC = ( + props +) => { + return ( +
+ + + +
+ ); +}; + +export default ActivityWizardAlertBar; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/components/activity/manual-activity-wizard/ManualActivityWizard.scss b/LCU.WebApps.Operations.IncentiveManager/src/components/activity/manual-activity-wizard/ManualActivityWizard.scss new file mode 100644 index 0000000..e69de29 diff --git a/LCU.WebApps.Operations.IncentiveManager/src/components/activity/manual-activity-wizard/ManualActivityWizard.tsx b/LCU.WebApps.Operations.IncentiveManager/src/components/activity/manual-activity-wizard/ManualActivityWizard.tsx new file mode 100644 index 0000000..35efbe0 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/components/activity/manual-activity-wizard/ManualActivityWizard.tsx @@ -0,0 +1,122 @@ +import "./ManualActivityWizard.scss"; +import React from "react"; +import ActivityProductForm from "./ActivityProductForm"; +import ActivityDetailsForm from "./ActivityDetailsForm"; +import { IManualActivityFormState } from "./../../../shared/models/Activity"; +import AsyncState from "./../../../shared/enumerations/AsyncState"; +import Translate from "../../../translate/Translate"; +import ActivityWizardAlertBar from "./ActivityWizardAlertBar"; +import { IDepartment } from "../../../shared/models/Department"; +import { IProductDetail } from "../../../shared/enumerations/Product"; +import { Form } from "reactstrap"; +import { tryUpdateStateWithInput } from "../../../shared/services/InputService"; +import ActivityService from "../../../shared/services/ActivityService"; + +export interface IManualActivityWizardProps { + formState: IManualActivityFormState; + submitStatus: AsyncState; + handleChange: (formState: IManualActivityFormState) => void; + submitManualActivity: () => void; + clearForm: () => void; + departments: IDepartment[]; + products: IProductDetail[]; +} + +export const ManualActivityWizard: React.FC = ( + props: IManualActivityWizardProps +) => { + /** + * Apply changes from form state slice to parent state, then update redux store + * @param formData - Updated state slice from form + */ + const handlePartialChangeFromForm = ( + formData: Partial + ): void => { + const newState: IManualActivityFormState = { + ...props.formState, + ...formData + }; + props.handleChange(newState); + }; + + const onFormDataChange = (field: string, newValue: any) => { + const updatedFormData = tryUpdateStateWithInput( + props.formState, + field, + newValue + ); + handlePartialChangeFromForm(updatedFormData); + }; + + return ( +
+
+ { + Translate.Get.components.activity.manualActivityWizard + .wizardTitle + } +
+ +
+ + handlePartialChangeFromForm(newFormData) + } + departments={props.departments} + products={props.products} + /> + + handlePartialChangeFromForm(newFormData) + } + /> +
+ + +
+ +
+ ); +}; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/components/activity/manual-activity-wizard/UserSearchSelect.test.tsx b/LCU.WebApps.Operations.IncentiveManager/src/components/activity/manual-activity-wizard/UserSearchSelect.test.tsx new file mode 100644 index 0000000..2ecf3e8 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/components/activity/manual-activity-wizard/UserSearchSelect.test.tsx @@ -0,0 +1,93 @@ +import { mount } from "enzyme"; +import React from "react"; +import { act } from "react-dom/test-utils"; +import AsyncState from "../../../shared/enumerations/AsyncState"; +import IAsyncStateContainer from "../../../shared/types/IAsyncStateContainer"; +import Translate from "../../../translate/Translate"; +import UserSearchSelect from "./UserSearchSelect"; +import { IEmployeeSummary } from "../../../shared/models/Employee"; +import ProfileDao from "../../../shared/dao/ProfileDao"; + +jest.useFakeTimers("modern"); +const profileSearchMock = jest.spyOn(ProfileDao, "profileSearch"); +const formTranslate = + Translate.Get.components.activity.manualActivityWizard.activityProductForm + .personId; + +describe("A component to search for users", () => { + beforeEach(() => { + profileSearchMock.mockReset(); + }); + + it("should not search if query is not less than > 1", () => { + const mockOnUserSelected = jest.fn(); + + mount( + + ); + + expect(profileSearchMock).not.toHaveBeenCalled(); + }); + + it("should search if query is not less than > 1", async () => { + const mockOnUserSelected = jest.fn(); + profileSearchMock.mockResolvedValue({ + state: AsyncState.Success, + data: [ + { + adEmail: "joe1@landmarkcu.com", + employeeId: "j1", + fullName: "Joe One", + personId: 1 + }, + { + adEmail: "joe2@landmarkcu.com", + employeeId: "j2", + fullName: "Joe Two", + personId: 2 + } + ] + } as IAsyncStateContainer); + const wrapper = mount( + + ); + + const searchInput = () => + wrapper.find("[data-test='appUser-search-input']").find("input"); + + // simulate search to get mock search results + await act(async () => { + searchInput().simulate("change", { target: { value: "joe" } }); + jest.advanceTimersByTime(500); + expect(profileSearchMock).toHaveBeenCalledTimes(1); + expect(profileSearchMock).toHaveBeenCalledWith("joe"); + }); + + wrapper.update(); + expect(searchInput().prop("value")).toEqual("joe"); + + // verify dropdown is populated with search results + const searchSelect = () => + wrapper.find("[data-test='appUser-search-select']").find("select"); + const options = searchSelect() + .children() + .map((option) => option.prop("children")); + expect(options).toEqual([ + formTranslate.select, + "Joe One (j1) joe1@landmarkcu.com", + "Joe Two (j2) joe2@landmarkcu.com" + ]); + + searchSelect().simulate("change", { target: { value: "j1" } }); + expect(mockOnUserSelected).toHaveBeenCalledTimes(1); + expect(mockOnUserSelected).toHaveBeenLastCalledWith("employeeId", "j1"); + }); +}); diff --git a/LCU.WebApps.Operations.IncentiveManager/src/components/activity/manual-activity-wizard/UserSearchSelect.tsx b/LCU.WebApps.Operations.IncentiveManager/src/components/activity/manual-activity-wizard/UserSearchSelect.tsx new file mode 100644 index 0000000..e7bffb0 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/components/activity/manual-activity-wizard/UserSearchSelect.tsx @@ -0,0 +1,104 @@ +import debounce from "debounce"; +import React, { useCallback, useEffect, useState } from "react"; +import { + FormFeedback, + Input, + InputGroup, + InputGroupAddon, + InputGroupText +} from "reactstrap"; +import Translate from "../../../translate/Translate"; +import { IEmployeeSummary } from "../../../shared/models/Employee"; +import ProfileDao from "../../../shared/dao/ProfileDao"; + +export interface IUserSearchSelectProps { + employeeId: string; + showValidation: boolean; + onUserSelected: (field: string, newValue: any) => void; + placeHolder?: string; +} + +const formTranslate = + Translate.Get.components.activity.manualActivityWizard.activityProductForm; + +const UserSearchSelect: React.FC = (props) => { + const [searchQuery, setSearchQuery] = useState(""); + const [suggestions, setSuggestions] = useState([]); + + useEffect(() => { + if (props.employeeId === "") { + setSearchQuery(""); + } + }, [props.employeeId]); + + useEffect(() => { + if (searchQuery && searchQuery.length > 1) { + setSuggestions([]); + search(searchQuery); + } + }, [searchQuery]); + + const search = useCallback( + debounce((query: string) => { + ProfileDao.profileSearch(query).then((result) => { + setSuggestions(() => result.data ?? []); + }); + }, 500), + [] + ); + + const selectUser = (employeeId: string) => { + props.onUserSelected("employeeId", employeeId); + }; + + return ( + <> + + + + + + + { + setSearchQuery(event.target.value); + }} + /> + { + selectUser(event.target.value); + }} + > + + {suggestions.map((suggestion) => ( + + ))} + + Please select an employee. + + + ); +}; + +export default UserSearchSelect; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/components/auto-table/AutoTable.test.tsx b/LCU.WebApps.Operations.IncentiveManager/src/components/auto-table/AutoTable.test.tsx new file mode 100644 index 0000000..6c60849 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/components/auto-table/AutoTable.test.tsx @@ -0,0 +1,93 @@ +import { shallow } from "enzyme"; +import React from "react"; +import { AutoTable } from "./AutoTable"; + +describe("AutoTable Component", () => { + it("Renders headers and rows", () => { + const wrapper = shallow( + + ); + + const headers = wrapper.find("[data-test='auto-table-header']"); + expect(headers.length).toBe(2); + expect(headers.at(0).text()).toEqual("Header #1"); + expect(headers.at(1).text()).toEqual("Header #2"); + + let rows = wrapper.find("[data-test='auto-table-row_1']"); + expect(rows.length).toBe(1); + rows = wrapper.find("[data-test='auto-table-row_2']"); + expect(rows.length).toBe(1); + }); + + it("Calls Header Callbacks", () => { + let wasTriggered = false; + const wrapper = shallow( + (wasTriggered = true) + } + ]} + elements={[{}, {}, {}]} + cells={[]} + /> + ); + + const headers = wrapper.find("[data-test='auto-table-header']"); + expect(headers.at(1).text()).toEqual("Header #2"); + + expect(wasTriggered).toBe(false); + headers.at(1).simulate("click"); + expect(wasTriggered).toBe(true); + }); + + it("Render Ascending Sorted Icon", () => { + const wrapper = shallow( + + ); + + const headers = wrapper.find("[data-test='auto-table-header']"); + expect(headers.length).toBe(2); + expect(headers.at(0).text()).toEqual("Header #1"); + expect(headers.at(1).text()).not.toEqual("Header #2"); + + const sortedAscIcon = wrapper.find("[data-test='asc-sort-icon-1']"); + expect(sortedAscIcon.length).toBe(1); + }); + + it("Render Descending Sorted Icon", () => { + const wrapper = shallow( + + ); + + const headers = wrapper.find("[data-test='auto-table-header']"); + expect(headers.length).toBe(2); + expect(headers.at(0).text()).not.toEqual("Header #1"); + expect(headers.at(1).text()).toEqual("Header #2"); + + const sortedDescIcon = wrapper.find("[data-test='desc-sort-icon-0']"); + expect(sortedDescIcon.length).toBe(1); + }); +}); diff --git a/LCU.WebApps.Operations.IncentiveManager/src/components/auto-table/AutoTable.tsx b/LCU.WebApps.Operations.IncentiveManager/src/components/auto-table/AutoTable.tsx new file mode 100644 index 0000000..2747541 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/components/auto-table/AutoTable.tsx @@ -0,0 +1,161 @@ +import React from "react"; +import { AutoTableRow } from "./auto-table-row/AutoTableRow"; + +export interface IColumnSort { + index: number; + isReversed: boolean; +} + +export interface IAutoTableHeader { + display: string; + onClick?: () => any; +} + +const instanceOfAutoTableHeader = (object: any): object is IAutoTableHeader => { + if (typeof object === "object" && object !== null) { + return "display" in object; + } + return false; +}; + +export interface IAutoTableProps { + /** + * The headers of the table + */ + headers: Array; + + /** + * Array of functions that describe how each cell of a row ought to be displayed. + */ + cells: Array<(element: T) => any>; + + /** + * The array of elements to display (each element being its own row). + */ + elements: T[]; + + /** + * Optional callback method that is triggered when a appUser clicks on a row. + */ + onRowClick?: (elementClicked: T) => any; + + /** + * Optionally show a desired column as being sorted. + * (This DOES NOT sort. Merely displays the header as such.) + */ + sortedColumn?: IColumnSort; + + className?: string; + + /** + * So you want to show something else instead of this row.. what is it and which 0 index row to replace. + */ + overrideRow?: (element: T) => JSX.Element; + overrideRowFn?: (element: T) => boolean; +} + +export class AutoTable extends React.Component> { + getHeaderText(header: IAutoTableHeader | string | number): string | number { + if (instanceOfAutoTableHeader(header)) { + return header.display; + } else { + return header; + } + } + + getHeaderOnClick( + header: IAutoTableHeader | string | number + ): (() => any) | undefined { + if (instanceOfAutoTableHeader(header)) { + return header.onClick; + } + } + + isHeaderClickable(header: IAutoTableHeader | string | number): boolean { + return instanceOfAutoTableHeader(header); + } + + shouldDisplayAscendingSort(index: number): boolean { + return ( + !!this.props.sortedColumn && + this.props.sortedColumn.index === index && + !this.props.sortedColumn.isReversed + ); + } + + shouldDisplayDescendingSort(index: number): boolean { + return ( + !!this.props.sortedColumn && + this.props.sortedColumn.index === index && + this.props.sortedColumn.isReversed + ); + } + + render() { + return ( + + + + {this.props.headers.map((header, index) => ( + + ))} + + + + {this.props.elements.map((el, index) => + this.props.overrideRow !== undefined && + this.props.overrideRowFn !== undefined && + this.props.overrideRowFn(el) ? ( + this.props.overrideRow(el) + ) : ( + + ) + )} + +
+ {this.shouldDisplayAscendingSort(index) ? ( + + ) : null} + {this.shouldDisplayDescendingSort(index) ? ( + + ) : null} + {this.getHeaderText(header)} +
+ ); + } +} diff --git a/LCU.WebApps.Operations.IncentiveManager/src/components/auto-table/auto-table-row/AutoTableRow.css b/LCU.WebApps.Operations.IncentiveManager/src/components/auto-table/auto-table-row/AutoTableRow.css new file mode 100644 index 0000000..ecf95c7 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/components/auto-table/auto-table-row/AutoTableRow.css @@ -0,0 +1,3 @@ +.auto-table-hover { + cursor: pointer; +} diff --git a/LCU.WebApps.Operations.IncentiveManager/src/components/auto-table/auto-table-row/AutoTableRow.test.tsx b/LCU.WebApps.Operations.IncentiveManager/src/components/auto-table/auto-table-row/AutoTableRow.test.tsx new file mode 100644 index 0000000..85f35cb --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/components/auto-table/auto-table-row/AutoTableRow.test.tsx @@ -0,0 +1,43 @@ +import { shallow } from "enzyme"; +import React from "react"; +import { AutoTableRow } from "./AutoTableRow"; + +describe("AutoTableRow Component", () => { + it("Renders Expected Cells", () => { + const wrapper = shallow( + x.val1, (x) => x.val2, (x) => x.val3.child]} + /> + ); + + const cells = wrapper.find("[data-test='auto-table-cell']"); + expect(cells.length).toBe(3); + expect(cells.at(0).text()).toEqual("Test"); + expect(cells.at(1).text()).toEqual("55"); + expect(cells.at(2).text()).toEqual("Child Val"); + }); + + it("OnClick() calls callback", () => { + let wasTriggered = false; + const wrapper = shallow( + (wasTriggered = true)} + /> + ); + + const row = wrapper.find("[data-test='auto-table-row']"); + expect(wasTriggered).toBe(false); + + row.simulate("click"); + expect(wasTriggered).toBe(true); + }); +}); diff --git a/LCU.WebApps.Operations.IncentiveManager/src/components/auto-table/auto-table-row/AutoTableRow.tsx b/LCU.WebApps.Operations.IncentiveManager/src/components/auto-table/auto-table-row/AutoTableRow.tsx new file mode 100644 index 0000000..ab69a5e --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/components/auto-table/auto-table-row/AutoTableRow.tsx @@ -0,0 +1,39 @@ +import React from "react"; +import "./AutoTableRow.css"; + +export interface IAutoTableRowProps { + element: T; + cells: Array<(element: T) => string | number | undefined>; + onRowClick?: (elementClicked: T) => any; +} + +export class AutoTableRow extends React.Component> { + private rowClicked() { + if (this.props.onRowClick) { + this.props.onRowClick(this.props.element); + } + } + + render() { + return ( + this.rowClicked()} + data-test="auto-table-row" + > + {this.props.cells.map((cell, index) => ( + + {cell(this.props.element)} + + ))} + + ); + } +} diff --git a/LCU.WebApps.Operations.IncentiveManager/src/components/commissions/CommissionRatesTable.test.tsx b/LCU.WebApps.Operations.IncentiveManager/src/components/commissions/CommissionRatesTable.test.tsx new file mode 100644 index 0000000..01e830c --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/components/commissions/CommissionRatesTable.test.tsx @@ -0,0 +1,20 @@ +import { shallow } from "enzyme"; +import React from "react"; +import CommissionRatesTable from "./CommissionRatesTable"; + +describe("A table to set commission rates", () => { + it("Displays the table", () => { + const props = { + commissions: [], + roles: [], + products: [], + onRateChange: () => {} + }; + + const wrapper = shallow(); + + const table = wrapper.find("[data-test='commission-rates-table']"); + + expect(table.length).toBe(1); + }); +}); diff --git a/LCU.WebApps.Operations.IncentiveManager/src/components/commissions/CommissionRatesTable.tsx b/LCU.WebApps.Operations.IncentiveManager/src/components/commissions/CommissionRatesTable.tsx new file mode 100644 index 0000000..e82389c --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/components/commissions/CommissionRatesTable.tsx @@ -0,0 +1,57 @@ +import React from "react"; +import { IProductDetail } from "../../shared/enumerations/Product"; +import { ICommissionRateChange } from "../../shared/models/Commission"; +import { IAppRole } from "../../shared/models/Settings"; +import CommissionRatesTableRow from "../../components/commissions/CommissionRatesTableRow"; + +interface ICommissionRatesTableProps { + commissions: ICommissionRateChange[]; + roles: IAppRole[]; + products: IProductDetail[]; + onRateChange: (commission: ICommissionRateChange) => void; +} + +const CommissionRatesTable: React.FC = ( + props: ICommissionRatesTableProps +) => { + const commissionsForProduct = (product: IProductDetail) => + props.commissions.filter( + (commission) => commission.product === product.product + ); + + const changeRate = (commissionChange: ICommissionRateChange) => { + props.onRateChange(commissionChange); + }; + + return ( + + + + + {props.roles.map((role) => ( + + ))} + + + + {props.products.map((product, index) => ( + + ))} + +
+ {role.description} +
+ ); +}; + +export default CommissionRatesTable; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/components/commissions/CommissionRatesTableCell.test.tsx b/LCU.WebApps.Operations.IncentiveManager/src/components/commissions/CommissionRatesTableCell.test.tsx new file mode 100644 index 0000000..b69c25b --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/components/commissions/CommissionRatesTableCell.test.tsx @@ -0,0 +1,22 @@ +import React from "react"; +import { shallow } from "enzyme"; +import CommissionRatesTableCell from "./CommissionRatesTableCell"; + +describe("Cell for setting commission rate", () => { + it("Displays a commssion rates cell", () => { + const props = { + id: 0, + value: 1, + onRateChange: () => jest.fn(), + tabIndex: 1 + }; + + const wrapper = shallow(); + + expect(wrapper.find('[data-test="commission-rate-cell"]').length).toBe( + 1 + ); + expect(wrapper.find('[data-test="valid-feedback"]').length).toBe(1); + expect(wrapper.find('[data-test="invalid-feedback"]').length).toBe(1); + }); +}); diff --git a/LCU.WebApps.Operations.IncentiveManager/src/components/commissions/CommissionRatesTableCell.tsx b/LCU.WebApps.Operations.IncentiveManager/src/components/commissions/CommissionRatesTableCell.tsx new file mode 100644 index 0000000..f6fb654 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/components/commissions/CommissionRatesTableCell.tsx @@ -0,0 +1,51 @@ +import React, { useState } from "react"; + +interface ICommissionRatesTableCellProps { + id: number; + value: number; + onRateChange: (newRate: number, id: number) => void; + tabIndex: number; +} + +const cssValidClass = (originalValue: string, newValue: string) => { + if (originalValue == newValue || (originalValue === "_" && !newValue)) + return "form-control"; + if (!newValue) return "form-control is-invalid"; + return "form-control is-valid"; +}; + +const CommissionRatesTableCell: React.FC = ( + props: ICommissionRatesTableCellProps +) => { + const originalValue = props.value + ""; + const [newValue, setNewValue] = useState(originalValue); + + const changeRate = (value: string) => { + const filtered = value.replace(/\D/g, "").trim(); + props.onRateChange(parseInt(filtered, 10), props.id); + setNewValue(filtered); + }; + + return ( +
+ changeRate(event.target.value)} + tabIndex={props.tabIndex} + /> +
+ Unsaved: {originalValue} {"->"} {newValue} +
+
+ Required +
+
+ ); +}; + +export default CommissionRatesTableCell; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/components/commissions/CommissionRatesTableRow.test.tsx b/LCU.WebApps.Operations.IncentiveManager/src/components/commissions/CommissionRatesTableRow.test.tsx new file mode 100644 index 0000000..bfaa49a --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/components/commissions/CommissionRatesTableRow.test.tsx @@ -0,0 +1,22 @@ +import { shallow } from "enzyme"; +import React from "react"; +import { IProductDetail } from "../../shared/enumerations/Product"; +import CommissionRatesTableRow from "./CommissionRatesTableRow"; + +describe("A row in the commission rates table", () => { + it("Displays a row", () => { + const props = { + product: { title: "ATMCard" } as IProductDetail, + roles: [], + commissions: [], + onRateChange: () => {}, + rowIndex: 1 + }; + + const wrapper = shallow(); + + const tableRow = wrapper.find('[data-test="commission-rate-row"]'); + + expect(tableRow.length).toBe(1); + }); +}); diff --git a/LCU.WebApps.Operations.IncentiveManager/src/components/commissions/CommissionRatesTableRow.tsx b/LCU.WebApps.Operations.IncentiveManager/src/components/commissions/CommissionRatesTableRow.tsx new file mode 100644 index 0000000..33ac720 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/components/commissions/CommissionRatesTableRow.tsx @@ -0,0 +1,54 @@ +import React from "react"; +import { IProductDetail } from "../../shared/enumerations/Product"; +import { ICommissionRateChange } from "../../shared/models/Commission"; +import { IAppRole } from "../../shared/models/Settings"; +import CommissionRatesTableCell from "./CommissionRatesTableCell"; + +interface ICommissionRatesTableRowProps { + product: IProductDetail; + roles: IAppRole[]; + commissions: ICommissionRateChange[]; + onRateChange: (commission: ICommissionRateChange) => void; + rowIndex: number; +} + +const CommissionRatesTableRow: React.FC = ( + props: ICommissionRatesTableRowProps +) => { + const rateChange = (rate: number, role: IAppRole, id: number) => { + const commission: ICommissionRateChange = { + id: id, + product: props.product.product, + ratePerProduct: rate, + role: role + }; + props.onRateChange(commission); + }; + + const existingCommission = (role: IAppRole) => + props.commissions.find( + (com) => + com.product === props.product.product && + com.role.name === role.name + ); + + return ( + + {props.product.title} + {props.roles.map((role, index) => ( + + { + rateChange(newRate, role, id); + }} + tabIndex={props.rowIndex + index + 1} + /> + + ))} + + ); +}; + +export default CommissionRatesTableRow; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/components/commissions/CommissionTable.test.tsx b/LCU.WebApps.Operations.IncentiveManager/src/components/commissions/CommissionTable.test.tsx new file mode 100644 index 0000000..5c1b6bf --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/components/commissions/CommissionTable.test.tsx @@ -0,0 +1,108 @@ +import React from "react"; +import { mount, shallow } from "enzyme"; +import CommissionTable from "./CommissionTable"; +import { IAuditableCommission } from "../../shared/models/Commission"; +import { UserType } from "../../shared/models/Auditable"; +import moment from "moment"; + +describe("A table for displaying commission audit data", () => { + it("Displays table", () => { + const mockCommissions: IAuditableCommission[] = [ + { + id: 1, + product: "ATM Card", + role: { + name: "Teller", + description: "", + permissions: [], + hasCommissions: true, + rank: 99 + }, + ratePerProduct: 10, + created: moment("2021-07-04T10:00:00Z"), + createdBy: "tester", + createdByType: UserType.System, + lastUpdated: moment("2021-07-04T10:00:00Z"), + lastUpdatedBy: "tester", + lastUpdatedByType: UserType.System + } + ]; + + const wrapper = mount( + + ); + + const tableData = wrapper.find("[data-test='auto-table-cell']"); + expect(tableData.length).toBe(8); + }); + + it("Sorts table lastUpdated desc", () => { + const mockCommissions: IAuditableCommission[] = [ + { + id: 1, + product: "ATM Card", + role: { + name: "Teller", + description: "", + permissions: [], + hasCommissions: true, + rank: 99 + }, + ratePerProduct: 10, + created: moment("2021-07-04T10:00:00Z"), + createdBy: "tester", + createdByType: UserType.System, + lastUpdated: moment("2021-07-04T10:00:00Z"), + lastUpdatedBy: "tester", + lastUpdatedByType: UserType.System + }, + { + id: 2, + product: "ATM Card", + role: { + name: "Teller", + description: "", + permissions: [], + hasCommissions: true, + rank: 99 + }, + ratePerProduct: 10, + created: moment("2021-07-04T10:00:00Z"), + createdBy: "tester", + createdByType: UserType.System, + lastUpdated: moment("2021-07-04T10:45:00Z"), + lastUpdatedBy: "tester", + lastUpdatedByType: UserType.System + }, + { + id: 3, + product: "ATM Card", + role: { + name: "Teller", + description: "", + permissions: [], + hasCommissions: true, + rank: 99 + }, + ratePerProduct: 10, + created: moment("2021-07-04T10:00:00Z"), + createdBy: "tester", + createdByType: UserType.System, + lastUpdated: moment("2021-07-04T10:30:00Z"), + lastUpdatedBy: "tester", + lastUpdatedByType: UserType.System + } + ]; + + const wrapper = shallow( + + ); + + const sortedCommissions: IAuditableCommission[] = wrapper + .find("AutoTable") + .prop("elements"); + + const sortedIds = sortedCommissions.map((commission) => commission.id); + expect(sortedIds).toEqual([2, 3, 1]); + }); +}); diff --git a/LCU.WebApps.Operations.IncentiveManager/src/components/commissions/CommissionTable.tsx b/LCU.WebApps.Operations.IncentiveManager/src/components/commissions/CommissionTable.tsx new file mode 100644 index 0000000..2160899 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/components/commissions/CommissionTable.tsx @@ -0,0 +1,60 @@ +import moment from "moment"; +import React from "react"; +import { IAuditableCommission } from "../../shared/models/Commission"; +import Translate from "../../translate/Translate"; +import { AutoTable } from "../auto-table/AutoTable"; + +interface ICommissionTable { + commissions: IAuditableCommission[]; +} + +const CommissionTableTranslate = + Translate.Get.components.commissions.commissionTable; + +const sortByUpdatedDate = ( + data: IAuditableCommission[] +): IAuditableCommission[] => { + return data.sort((a, b) => moment(b.lastUpdated).diff(a.lastUpdated)); +}; + +const CommissionTable: React.FC = ( + props: ICommissionTable +) => { + const headers = [ + CommissionTableTranslate.headers.id, + CommissionTableTranslate.headers.role, + CommissionTableTranslate.headers.ratePerProduct, + CommissionTableTranslate.headers.product, + CommissionTableTranslate.headers.lastUpdated, + CommissionTableTranslate.headers.lastUpdatedBy, + CommissionTableTranslate.headers.created, + CommissionTableTranslate.headers.createdBy + ]; + + const cells = [ + (x: IAuditableCommission) => x.id, + (x: IAuditableCommission) => x.role.name, + (x: IAuditableCommission) => x.ratePerProduct, + (x: IAuditableCommission) => x.product, + (x: IAuditableCommission) => + moment.utc(x.lastUpdated).local().format("lll"), + (x: IAuditableCommission) => x.lastUpdatedBy, + (x: IAuditableCommission) => + moment.utc(x.created).local().format("lll"), + (x: IAuditableCommission) => x.createdBy + ]; + + if (props.commissions && props.commissions.length) { + return ( + + ); + } + + return ; +}; + +export default CommissionTable; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/components/commissions/TabbedCommissionView.test.tsx b/LCU.WebApps.Operations.IncentiveManager/src/components/commissions/TabbedCommissionView.test.tsx new file mode 100644 index 0000000..35a0fa6 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/components/commissions/TabbedCommissionView.test.tsx @@ -0,0 +1,15 @@ +import { shallow } from "enzyme"; +import React from "react"; +import TabbedCommissionView from "./TabbedCommissionView"; + +describe("Basic commission rates editor and table", () => { + it("should render tabs", () => { + const mockCommissions = []; + const wrapper = shallow( + + ); + const tabbedView = wrapper.find("[data-test='tabbed-commission-view']"); + expect(tabbedView.length).toBe(1); + expect(tabbedView.props()["pages"].length).toBe(2); + }); +}); diff --git a/LCU.WebApps.Operations.IncentiveManager/src/components/commissions/TabbedCommissionView.tsx b/LCU.WebApps.Operations.IncentiveManager/src/components/commissions/TabbedCommissionView.tsx new file mode 100644 index 0000000..8edcce4 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/components/commissions/TabbedCommissionView.tsx @@ -0,0 +1,34 @@ +import React from "react"; +import Translate from "./../../translate/Translate"; +import TabView from "../tab-view/TabView"; +import { IAuditableCommission } from "../../shared/models/Commission"; +import CommissionRatesEditor from "../../containers/commissions-page/CommissionRatesEditor"; +import CommissionTable from "./CommissionTable"; + +interface ITabbedCommissionViewProps { + commissions: IAuditableCommission[]; +} + +const viewTranslate = Translate.Get.components.commissions.tabbedComissionView; + +const TabbedCommissionView: React.FC = ( + props: ITabbedCommissionViewProps +) => { + return ( + + }, + { + header: viewTranslate.tableTab, + element: + } + ]} + /> + ); +}; + +export default TabbedCommissionView; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/components/dead-activity/DeadActivityAuditLog.tsx b/LCU.WebApps.Operations.IncentiveManager/src/components/dead-activity/DeadActivityAuditLog.tsx new file mode 100644 index 0000000..881736c --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/components/dead-activity/DeadActivityAuditLog.tsx @@ -0,0 +1,161 @@ +import React, { useState } from "react"; +import { Alert } from "reactstrap"; +import Translate from "../../translate/Translate"; +import IAsyncStateContainer from "../../shared/types/IAsyncStateContainer"; +import AsyncState from "../../shared/enumerations/AsyncState"; +import DeadActivityTable from "./DeadActivityTable"; +import IPagedResponse from "../../shared/types/IPagedResponse"; +import PagedContentControls from "../paged-content-controls/PagedContentControls"; +import { + generateDeadActivityForm, + IAuditableDeadActivity, + IDeadActivitySearchForm +} from "../../shared/models/DeadActivity"; +import { SettingsContext } from "../../shared/providers/SettingsProvider"; +import { IDepartment } from "../../shared/models/Department"; +import { IManualActivityFormState } from "../../shared/models/Activity"; +import { DeadActivityWizard } from "./DeadActivityForm"; +import DeadActivityDisplay from "./DeadActivityDisplay"; +import ActivityService from "../../shared/services/ActivityService"; +import AsyncGuard from "../../containers/shared/AsyncGuard"; + +export interface IDeadActivityAuditLogProps { + deadActivityContainer: IAsyncStateContainer< + IPagedResponse + >; + getNextPage: () => void; + getPrevPage: () => void; + changeSort: (sort: string) => void; + form: IDeadActivitySearchForm; + refresh: () => void; +} + +const DeadActivityAuditLog: React.FC = (props) => { + const settings = React.useContext(SettingsContext); + let departments: IDepartment[] = []; + if (settings?.departments) { + departments = settings.departments.filter((x) => x.state == "Active"); + } + const [ + activityForm, + setActivityForm + ] = useState(); + const [submitState, setSubmitState] = useState( + AsyncState.Inactive + ); + const deadActivities = props.deadActivityContainer.data?.items ?? []; + + const changeSort = (headerName: string) => { + if (props.changeSort) { + props.changeSort(headerName); + } + }; + + const submitActivity = async () => { + if (activityForm && activityForm.deadActivityId) { + setSubmitState(AsyncState.Loading); + let result = await ActivityService.submitManualActivityForm( + activityForm + ); + await ActivityService.submitDeleteDeadActivity( + activityForm.deadActivityId, + result.data + ); + setSubmitState(AsyncState.Success); + setActivityForm(undefined); + props.refresh(); + } + }; + + const deleteActivity = async () => { + if (activityForm && activityForm.deadActivityId) { + setSubmitState(AsyncState.Loading); + await ActivityService.submitDeleteDeadActivity( + activityForm.deadActivityId + ); + setSubmitState(AsyncState.Success); + setActivityForm(undefined); + props.refresh(); + } + }; + + const onFormChange = (newValue: IManualActivityFormState) => { + setActivityForm(newValue); + }; + + const onCancelClick = () => { + setActivityForm(undefined); + }; + + const onRowClick = (deadActivity: IAuditableDeadActivity) => { + setSubmitState(AsyncState.Inactive); + setActivityForm(generateDeadActivityForm(deadActivity)); + }; + + const getActivityById = ( + deadActivityId: number + ): IAuditableDeadActivity | undefined => { + if (deadActivities) { + const index = deadActivities.findIndex( + (da) => da.id === deadActivityId + ); + return deadActivities[index]; + } + }; + + const deadActivityForm = () => { + if (activityForm && activityForm.deadActivityId) { + return ( +
+ + +
+ ); + } + return <>; + }; + + return ( +
+ +
+
+ props.getNextPage()} + prevPage={() => props.getPrevPage()} + pagedResponse={props.deadActivityContainer.data} + /> +
+ +
+ props.getNextPage()} + prevPage={() => props.getPrevPage()} + pagedResponse={props.deadActivityContainer.data} + /> +
+
+
+
+ ); +}; + +export default DeadActivityAuditLog; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/components/dead-activity/DeadActivityDisplay.tsx b/LCU.WebApps.Operations.IncentiveManager/src/components/dead-activity/DeadActivityDisplay.tsx new file mode 100644 index 0000000..0611777 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/components/dead-activity/DeadActivityDisplay.tsx @@ -0,0 +1,173 @@ +import moment from "moment"; +import React from "react"; +import Translate from "../../translate/Translate"; +import { IAuditableDeadActivity } from "../../shared/models/DeadActivity"; +import GoalService from "../../shared/services/GoalService"; +import { Col, Row } from "reactstrap"; + +interface IActivityDisplayProps { + activity: IAuditableDeadActivity; +} + +const activityTranslate = + Translate.Get.components.activityDetails.activityDisplay; +const detailTranslate = + Translate.Get.components.activityDetails.activityDetailDisplay; + +const DeadActivityDisplay: React.FC = ( + props: IActivityDisplayProps +) => { + const details = props.activity.deadDetail || {}; + + return ( + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ {activityTranslate.headers.type} + {props.activity.type}
+ {activityTranslate.headers.sourceSystem} + {props.activity.sourceSystem}
+ {activityTranslate.headers.sourceId} + {props.activity.sourceId}
+ {activityTranslate.headers.sourceCreated} + + {moment + .utc(props.activity.sourceCreated) + .local() + .format("lll")} +
+ {detailTranslate.headers.dnaPersonNumber} + {details.dnaPersonNbr}
+ {detailTranslate.headers.accountNumber} + {details.accountNumber}
+ {detailTranslate.headers.subjectPersonNbr} + {details.subjectPersonNbr}
+ {detailTranslate.headers.subjectOrgNbr} + {details.subjectOrgNbr}
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ {detailTranslate.headers.memberName} + {details.memberName}
+ {detailTranslate.headers.stationName} + {details.stationName}
+ {detailTranslate.headers.branchOrgNbr} + {details.branchOrgNbr}
+ {detailTranslate.headers.branchOrgName} + {details.branchOrgName}
+ {detailTranslate.headers.accountStatus} + {details.accountStatus}
+ {detailTranslate.headers.accountBalance} + + {GoalService.formatMonetary( + details.accountBalance + )} +
+ {detailTranslate.headers.debtProtectionAddDate} + + {details.debtProtectionAddDate + ? moment( + details.debtProtectionAddDate + ).format("MM-DD-YYYY") + : ""} +
+ { + detailTranslate.headers + .debtProtectionRemoveDate + } + + {details.debtProtectionRemoveDate + ? moment( + details.debtProtectionRemoveDate + ).format("MM-DD-YYYY") + : ""} +
+ { + detailTranslate.headers + .isDebtProtectionEligible + } + + {details.isDebtProtectionEligible + ? "True" + : "False"} +
+ +
+ ); +}; + +export default DeadActivityDisplay; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/components/dead-activity/DeadActivityForm.tsx b/LCU.WebApps.Operations.IncentiveManager/src/components/dead-activity/DeadActivityForm.tsx new file mode 100644 index 0000000..4b818c5 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/components/dead-activity/DeadActivityForm.tsx @@ -0,0 +1,247 @@ +import { IDepartment } from "../../shared/models/Department"; +import { + IManualActivityFormState, + ManualActivityState +} from "../../shared/models/Activity"; +import AsyncState from "../../shared/enumerations/AsyncState"; +import { tryUpdateStateWithInput } from "../../shared/services/InputService"; +import Translate from "../../translate/Translate"; +import ActivityWizardAlertBar from "../activity/manual-activity-wizard/ActivityWizardAlertBar"; +import { + Col, + Form, + FormFeedback, + FormGroup, + Input, + Label, + Row +} from "reactstrap"; +import ActivityService from "../../shared/services/ActivityService"; +import UserSearchSelect from "../activity/manual-activity-wizard/UserSearchSelect"; +import moment from "moment"; +import React from "react"; + +export interface IManualActivityWizardProps { + formState: IManualActivityFormState; + submitStatus: AsyncState; + handleChange: (formState: IManualActivityFormState) => void; + submitManualActivity: () => void; + deleteManualActivity: () => void; + clearForm: () => void; + departments: IDepartment[]; +} + +const activityProductFormTranslate = + Translate.Get.components.activity.manualActivityWizard.activityProductForm; + +export const DeadActivityWizard: React.FC = ( + props: IManualActivityWizardProps +) => { + const handlePartialChangeFromForm = ( + formData: Partial + ): void => { + const newState: IManualActivityFormState = { + ...props.formState, + ...formData + }; + props.handleChange(newState); + }; + + const onFormDataChange = (field: string, newValue: any) => { + const updatedFormData = tryUpdateStateWithInput( + props.formState, + field, + newValue + ); + handlePartialChangeFromForm(updatedFormData); + }; + + return ( +
+ +
+ + + + + { + onFormDataChange( + "departmentId", + event.target.value + ); + }} + > + + {props.departments.map((department) => { + return ( + + ); + })} + + Please select a value. + + + + + + + + + + + + { + onFormDataChange( + "state", + event.target.value + ); + }} + > + {Object.values(ManualActivityState).map( + (activityState) => { + return ( + + ); + } + )} + + + + + + + + { + onFormDataChange( + "accepted", + event.target.value + ? moment(event.target.value) + : "" + ); + }} + /> + + Accepted Date is required. + + + + + +
+ + + +
+
+
+ ); +}; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/components/dead-activity/DeadActivityTable.tsx b/LCU.WebApps.Operations.IncentiveManager/src/components/dead-activity/DeadActivityTable.tsx new file mode 100644 index 0000000..df33775 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/components/dead-activity/DeadActivityTable.tsx @@ -0,0 +1,166 @@ +import React from "react"; +import { ActivityType } from "./../../shared/models/Activity"; +import moment from "moment"; +import Translate from "../../translate/Translate"; +import { + AutoTable, + IAutoTableHeader, + IColumnSort +} from "../auto-table/AutoTable"; +import UserDisplay from "../../containers/shared/UserDisplay"; +import { IAuditableDeadActivity } from "../../shared/models/DeadActivity"; +import DepartmentDisplay from "../../containers/shared/DepartmentDisplay"; + +interface IDeadActivityTable { + deadActivities?: IAuditableDeadActivity[]; + changeSort?: (sort: string) => void; + sortBy?: string; + sortDescending?: boolean; + activityTypeFilter?: ActivityType; + deadActivityForm?: JSX.Element; + editDeadActivityId?: number; + onRowClick: (activity: IAuditableDeadActivity) => void; +} + +const _Translate = Translate.Get.components.deadActivity.deadActivityTable; + +const DeadActivityTable: React.FC = (props) => { + const headers = [ + _Translate.headers.accepted, + _Translate.headers.id, + _Translate.headers.product, + _Translate.headers.activityType, + _Translate.headers.source, + _Translate.headers.sourceId, + _Translate.headers.reasonNote, + _Translate.headers.associate, + _Translate.headers.department, + _Translate.headers.accountNbr, + _Translate.headers.memberName, + _Translate.headers.dpEligible + ]; + + const fieldNames = [ + "Accepted", + "Id", + "Product", + "Type", + "SourceSystem", + "SourceId", + "", + "", + "", + "" + ]; + + const changeSort = (headerName: string) => { + if (props.changeSort) { + props.changeSort(headerName); + } + }; + + const sortHeaders: IAutoTableHeader[] = headers.map((headerName, idx) => { + return { + display: headerName, + onClick: () => { + if (fieldNames[idx] !== "") { + changeSort(fieldNames[idx]); + } + } + }; + }); + + const cells = [ + (x: IAuditableDeadActivity) => + moment.utc(x.accepted).local().format("lll"), + (x: IAuditableDeadActivity) => x.id, + (x: IAuditableDeadActivity) => x.product, + (x: IAuditableDeadActivity) => x.type, + (x: IAuditableDeadActivity) => x.sourceSystem, + (x: IAuditableDeadActivity) => x.sourceId, + (x: IAuditableDeadActivity) => x.reason + ": " + x.reasonNote, + (x: IAuditableDeadActivity) => ( + + ), + (x: IAuditableDeadActivity) => ( + + ), + (x: IAuditableDeadActivity) => x.deadDetail?.accountNumber ?? "-", + (x: IAuditableDeadActivity) => x.deadDetail?.memberName ?? "-", + (x: IAuditableDeadActivity) => + (x.deadDetail?.isDebtProtectionEligible ? "Yes - " : "No - ") + + (x.deadDetail?.debtProtectionAddDate + ? moment + .utc(x.deadDetail?.debtProtectionAddDate) + .local() + .format("ll") + : "") + ]; + + let deadActivities = props.deadActivities || []; + if (props.activityTypeFilter) { + deadActivities = deadActivities.filter( + (activity) => activity.type === props.activityTypeFilter + ); + } + + const findIndexForColumn = (column?: string) => { + return fieldNames.indexOf(column || "Accepted"); + }; + + const changeSortedColumn: IColumnSort = { + index: findIndexForColumn(props.sortBy), + isReversed: props.sortDescending ?? false + }; + + if (deadActivities.length > 0) { + return ( +
+ { + return ( + props.deadActivityForm != undefined && + activity.id === props.editDeadActivityId + ); + }} + overrideRow={(activity: IAuditableDeadActivity) => { + return props.deadActivityForm ? ( + + {props.deadActivityForm} + + ) : ( +
+ ); + }} + /> +
+ ); + } + + return ( +
+
+

+ {_Translate.noData} +

+
+ +
+ ); +}; + +export default DeadActivityTable; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/components/department-detail/DepartmentDetailAssignments.test.tsx b/LCU.WebApps.Operations.IncentiveManager/src/components/department-detail/DepartmentDetailAssignments.test.tsx new file mode 100644 index 0000000..a084289 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/components/department-detail/DepartmentDetailAssignments.test.tsx @@ -0,0 +1,77 @@ +import { mount } from "enzyme"; +import React from "react"; +import { IDepartmentAssignmentWithEmployee } from "../../shared/models/DepartmentAssignment"; +import DepartmentDetailAssignments from "./DepartmentDetailAssignments"; +import { mockDeptAssignments } from "../../shared/testing/mocks/MockDepartmentAssignments"; +import PermissionCode from "../../shared/enumerations/PermissionCode"; + +describe("DepartmentDetailAssignments", () => { + it("should display the table if there are assignments", () => { + const assignments: IDepartmentAssignmentWithEmployee[] = [ + { + ...mockDeptAssignments[0], + employee: { + adEmail: "test@test.com", + personId: 11, + employeeId: "11771", + homeDepartment: "HOME", + baseRole: "QA", + fullName: "John Tester", + role: { + name: "Admin", + description: "admin", + permissions: [ + { + code: + PermissionCode.ASSIGN_ASSOCIATES_TO_DEPARTMENT, + description: "ASSIGN_ASSOCIATES_TO_DEPARTMENT" + } + ], + hasCommissions: false + }, + additionalPermissions: [], + departments: [], + assignments: [] + } + } + ]; + const wrapper = mount( + + ); + + expect( + wrapper.find("[data-test='no-department-detail-assignments']") + .length + ).toBe(0); + expect( + wrapper.find("[data-test='department-detail-assignments']").length + ).toBe(1); + }); + + it("should display no assignments if assignments are undefined", () => { + const wrapper = mount(); + + expect( + wrapper.find("[data-test='no-department-detail-assignments']") + .length + ).toBe(1); + expect( + wrapper.find("[data-test='department-detail-assignments']").length + ).toBe(0); + }); + + it("should display no assignments if assignments length is 0", () => { + const assignments: IDepartmentAssignmentWithEmployee[] = []; + const wrapper = mount( + + ); + + expect( + wrapper.find("[data-test='no-department-detail-assignments']") + .length + ).toBe(1); + expect( + wrapper.find("[data-test='department-detail-assignments']").length + ).toBe(0); + }); +}); diff --git a/LCU.WebApps.Operations.IncentiveManager/src/components/department-detail/DepartmentDetailAssignments.tsx b/LCU.WebApps.Operations.IncentiveManager/src/components/department-detail/DepartmentDetailAssignments.tsx new file mode 100644 index 0000000..6f29759 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/components/department-detail/DepartmentDetailAssignments.tsx @@ -0,0 +1,69 @@ +import moment from "moment"; +import React from "react"; +import { IDepartmentAssignmentWithEmployee } from "../../shared/models/DepartmentAssignment"; +import { AutoTable } from "../auto-table/AutoTable"; +import Translate from "../../translate/Translate"; +import UserDisplay, { + UserDisplayTargets +} from "../../containers/shared/UserDisplay"; + +interface IDepartmentDetailAssignments { + assignments?: IDepartmentAssignmentWithEmployee[]; +} + +const assignmentTranslate = + Translate.Get.components.departmentDetail.assignments; + +const DepartmentDetailAssignments: React.FC = ( + props +) => { + const headers = [ + assignmentTranslate.headers.home, + assignmentTranslate.headers.name, + assignmentTranslate.headers.startDate, + assignmentTranslate.headers.endDate, + assignmentTranslate.headers.role, + assignmentTranslate.headers.payout, + assignmentTranslate.headers.note + ]; + + const cells = [ + (x: IDepartmentAssignmentWithEmployee) => + x.isHome ? assignmentTranslate.home : assignmentTranslate.float, + (x: IDepartmentAssignmentWithEmployee) => ( + + ), + (x: IDepartmentAssignmentWithEmployee) => + moment(x.startDate).format("ll"), + (x: IDepartmentAssignmentWithEmployee) => + x.endDate + ? moment(x.endDate).format("ll") + : assignmentTranslate.current, + (x: IDepartmentAssignmentWithEmployee) => + x.role ? x.role.name : assignmentTranslate.missingRole, + (x: IDepartmentAssignmentWithEmployee) => x.slidingPayout + "%", + (x: IDepartmentAssignmentWithEmployee) => x.note + ]; + + if (!props.assignments?.length) { + return ( +
+ {assignmentTranslate.noAssignments} +
+ ); + } + return ( + + ); +}; + +export default DepartmentDetailAssignments; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/components/department-detail/DepartmentDetailDisplay.test.tsx b/LCU.WebApps.Operations.IncentiveManager/src/components/department-detail/DepartmentDetailDisplay.test.tsx new file mode 100644 index 0000000..6789de4 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/components/department-detail/DepartmentDetailDisplay.test.tsx @@ -0,0 +1,123 @@ +import { mount } from "enzyme"; +import React from "react"; +import PermissionCode from "../../shared/enumerations/PermissionCode"; +import { IDepartmentDetails } from "../../shared/models/Department"; +import DepartmentDetailDisplay from "./DepartmentDetailDisplay"; +import { getMockDepartments } from "../../shared/testing/mocks/MockDepartments"; +import { mockDeptAssignments } from "../../shared/testing/mocks/MockDepartmentAssignments"; +import { IDepartmentAssignmentWithEmployee } from "../../shared/models/DepartmentAssignment"; + +describe("DepartmentDetailDisplay", () => { + it("should display department details", () => { + const mockEmployee = { + adEmail: "test@test.com", + personId: 11, + employeeId: "11771", + homeDepartment: "HOME", + baseRole: "QA", + fullName: "John Tester", + role: { + name: "Admin", + description: "admin", + permissions: [ + { + code: PermissionCode.ASSIGN_ASSOCIATES_TO_DEPARTMENT, + description: "ASSIGN_ASSOCIATES_TO_DEPARTMENT" + } + ], + hasCommissions: false + }, + additionalPermissions: [], + departments: [], + assignments: [] + }; + const mockDepartmentAssignments: IDepartmentAssignmentWithEmployee[] = [ + { ...mockDeptAssignments[0], employee: mockEmployee }, + { ...mockDeptAssignments[1], employee: mockEmployee } + ]; + + const mockDepartment = getMockDepartments()[0]; + mockDepartment.regionCode = "NORTH"; + const details: IDepartmentDetails = { + assignments: mockDepartmentAssignments, + regionalManager: mockEmployee + }; + + const wrapper = mount( + + ); + + expect(wrapper.find("[data-test='department-detail-id']").text()).toBe( + "55" + ); + expect( + wrapper.find("[data-test='department-detail-region']").text() + ).toBe("NORTH"); + expect( + wrapper + .find("[data-test='department-detail-regionalManager']") + .text() + ).toBe("John Tester"); + expect( + wrapper.find("[data-test='department-detail-status']").text() + ).toBe("Active"); + expect( + wrapper + .find("[data-test='department-detail-activeAssignments']") + .text() + ).toBe("2"); + }); + + it("should display department details", () => { + const mockEmployee = { + adEmail: "test@test.com", + personId: 11, + employeeId: "11771", + homeDepartment: "HOME", + baseRole: "QA", + fullName: "John Tester", + role: { + name: "Admin", + description: "admin", + permissions: [ + { + code: PermissionCode.ASSIGN_ASSOCIATES_TO_DEPARTMENT, + description: "ASSIGN_ASSOCIATES_TO_DEPARTMENT" + } + ], + hasCommissions: false + }, + additionalPermissions: [], + departments: [], + assignments: [] + }; + const mockDepartmentAssignments: IDepartmentAssignmentWithEmployee[] = [ + { ...mockDeptAssignments[0], employee: mockEmployee }, + { ...mockDeptAssignments[1], employee: mockEmployee } + ]; + + const mockDepartment = getMockDepartments()[0]; + const details: IDepartmentDetails = { + assignments: mockDepartmentAssignments + }; + + const wrapper = mount( + + ); + + expect( + wrapper.find("[data-test='department-detail-region']").text() + ).toBe("No region assigned"); + expect( + wrapper + .find("[data-test='department-detail-regionalManager']") + .text() + ).toBe("No regional manager assigned"); + }); +}); diff --git a/LCU.WebApps.Operations.IncentiveManager/src/components/department-detail/DepartmentDetailDisplay.tsx b/LCU.WebApps.Operations.IncentiveManager/src/components/department-detail/DepartmentDetailDisplay.tsx new file mode 100644 index 0000000..32348b3 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/components/department-detail/DepartmentDetailDisplay.tsx @@ -0,0 +1,71 @@ +import React from "react"; +import { + IDepartment, + IDepartmentDetails +} from "../../shared/models/Department"; +import Translate from "../../translate/Translate"; +import BusinessLineDisplay from "../../containers/shared/BusinessLineDisplay"; + +interface IDepartmentDetailDisplay { + department: IDepartment; + details: IDepartmentDetails; +} + +const detailTranslate = Translate.Get.components.departmentDetail.detailDisplay; + +const DepartmentDetailDisplay: React.FC = (props) => { + return ( + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
{detailTranslate.headers.id} + {props.department.id} +
{detailTranslate.headers.region} + {props.department.regionCode ?? ( + {detailTranslate.noRegion} + )} +
{detailTranslate.headers.regionalManager} + {props.details?.regionalManager?.fullName ?? ( + {detailTranslate.noRegionalManager} + )} +
{detailTranslate.headers.status} + {props.department.state} +
{detailTranslate.headers.businessLine} + +
{detailTranslate.headers.activeAssignments} + {props.details.assignments.length ?? 0} +
+ ); +}; + +export default DepartmentDetailDisplay; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/components/department-progress/DepartmentProgressDisplay.tsx b/LCU.WebApps.Operations.IncentiveManager/src/components/department-progress/DepartmentProgressDisplay.tsx new file mode 100644 index 0000000..ce9ac8c --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/components/department-progress/DepartmentProgressDisplay.tsx @@ -0,0 +1,68 @@ +import React from "react"; +import DepartmentDisplay from "../../containers/shared/DepartmentDisplay"; +import { IDepartmentProgress } from "../../shared/models/Activity"; +import UserDisplay, { + UserDisplayTargets +} from "../../containers/shared/UserDisplay"; +import ProductTableHeader from "../shared/ProductTableHeader"; +import ProgressTableRow from "../shared/ProgressTableRow"; +import ProgressPercentageTableRow from "../shared/ProgressPercentageTableRow"; + +export interface IDepartmentProgressProps { + departmentProgress: IDepartmentProgress; +} + +const DepartmentProgressDisplay: React.FC = ( + props: IDepartmentProgressProps +) => { + const departmentId = props.departmentProgress.departmentId; + const goals = props.departmentProgress.goals; + const progress = props.departmentProgress.progress; + const employeeGoals = props.departmentProgress.employeeGoals; + const employeeActivity = props.departmentProgress.employeeProgress; + const employees = props.departmentProgress.employeeList; + + const employeeRows = employees.map((employeeId) => { + const goal = employeeGoals[employeeId]; + const activity = employeeActivity[employeeId]; + if (!goal || !activity) { + return ""; + } + return ( + + } + progress={activity} + goals={goal} + /> + ); + }); + + return ( + + + + } + progress={progress} + goals={goals} + /> + + {employeeRows} + +
+ ); +}; + +export default DepartmentProgressDisplay; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/components/department-progress/MultiDepartmentProgressDisplay.tsx b/LCU.WebApps.Operations.IncentiveManager/src/components/department-progress/MultiDepartmentProgressDisplay.tsx new file mode 100644 index 0000000..d3777a5 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/components/department-progress/MultiDepartmentProgressDisplay.tsx @@ -0,0 +1,59 @@ +import React from "react"; +import DepartmentDisplay, { + DepartmentDisplayTargets +} from "../../containers/shared/DepartmentDisplay"; +import { IDepartmentProgress } from "../../shared/models/Activity"; +import ProductTableHeader from "../shared/ProductTableHeader"; +import ProgressTableRow from "../shared/ProgressTableRow"; +import ActivityService from "../../shared/services/ActivityService"; +import { IMonthRange } from "../../shared/types/IMonthRange"; + +export interface IDepartmentProgressProps { + showDetails?: boolean; + label: string; + monthRange: IMonthRange; + departments: IDepartmentProgress[]; +} + +const MultiDepartmentProgressDisplay: React.FC = ( + props: IDepartmentProgressProps +) => { + const totals = ActivityService.calculateTotalProgress(props.departments); + + const detailRows = props.showDetails + ? props.departments.map((progress, index) => { + return ( + + } + progress={progress.progress} + goals={progress.goals} + key={index} + /> + ); + }) + : []; + + return ( + + + + + {detailRows} + +
+ ); +}; + +export default MultiDepartmentProgressDisplay; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/components/department-progress/TabbedDepartmentProgressView.test.tsx b/LCU.WebApps.Operations.IncentiveManager/src/components/department-progress/TabbedDepartmentProgressView.test.tsx new file mode 100644 index 0000000..485a219 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/components/department-progress/TabbedDepartmentProgressView.test.tsx @@ -0,0 +1,125 @@ +import React from "react"; +import { shallow } from "enzyme"; +import TabbedDepartmentProgressView from "./TabbedDepartmentProgressView"; +import { IDepartmentGoalsForRange } from "../../shared/models/Goal"; +import { UserType } from "../../shared/models/Auditable"; +import Product2 from "../../shared/enumerations/Product"; +import moment from "moment"; +import { + ActivityType, + IDepartmentProgress, + SystemType +} from "../../shared/models/Activity"; +import * as reactRedux from "react-redux"; +import { IDepartmentState } from "../../reducers/DepartmentReducer"; + +describe("Tabbed department progress view", () => { + const useDispatchMock = jest.spyOn(reactRedux, "useDispatch"); + const useSelectorMock = jest.spyOn(reactRedux, "useSelector"); + beforeEach(() => { + useSelectorMock.mockClear(); + useDispatchMock.mockClear(); + }); + it("should render the tab view", () => { + const state = { + departmentState: { + departments: { + data: [{ id: 1 }] + } + } as IDepartmentState + }; + useDispatchMock.mockReturnValue(jest.fn()); + useSelectorMock.mockImplementation((x) => x(state)); + const goals: IDepartmentGoalsForRange = { + departmentId: 1, + monthRange: { + fromMonth: 1, + fromYear: 2021, + toMonth: 2, + toYear: 2021 + }, + goals: { + productGoals: { + "product one": 10, + "product two": 20 + } + }, + goalAudit: [ + { + id: 1, + departmentId: 1, + employeeId: "C1", + product: Product2.Mortgage, + startDate: moment(), + endDate: moment(), + value: 10, + created: moment(), + createdBy: "manager", + createdByType: UserType.Associate, + lastUpdated: moment(), + lastUpdatedBy: "manager", + lastUpdatedByType: UserType.Associate + }, + { + id: 2, + departmentId: 1, + employeeId: "C1", + product: Product2.Vehicle, + startDate: moment(), + endDate: moment(), + value: 20, + created: moment(), + createdBy: "manager", + createdByType: UserType.Associate, + lastUpdated: moment(), + lastUpdatedBy: "manager", + lastUpdatedByType: UserType.Associate + } + ] + }; + + const progress: IDepartmentProgress = { + departmentId: 1, + monthRange: { + fromMonth: 1, + fromYear: 2021, + toMonth: 2, + toYear: 2021 + }, + progress: { + productActivity: { + "product one": 1 + } + }, + activityAudit: [ + { + id: 88, + product: Product2.Mortgage, + type: ActivityType.Sale, + sourceSystem: SystemType.Dna, + detail: {}, + departmentId: "1", + employeeId: "C1", + created: moment(), + createdBy: "manager", + createdByType: UserType.Associate, + lastUpdated: moment(), + lastUpdatedBy: "manager", + lastUpdatedByType: UserType.Associate, + activityPayouts: [] + } + ] + }; + + const wrapper = shallow( + + ); + + expect( + wrapper.find("[data-test='department-progress-tabbed-view']").length + ).toBe(1); + }); +}); diff --git a/LCU.WebApps.Operations.IncentiveManager/src/components/department-progress/TabbedDepartmentProgressView.tsx b/LCU.WebApps.Operations.IncentiveManager/src/components/department-progress/TabbedDepartmentProgressView.tsx new file mode 100644 index 0000000..e9a8004 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/components/department-progress/TabbedDepartmentProgressView.tsx @@ -0,0 +1,50 @@ +import React from "react"; +import { IDepartmentProgress } from "../../shared/models/Activity"; +import TabView from "../tab-view/TabView"; +import GoalTable from "../goal/GoalTable"; +import Translate from "../../translate/Translate"; +import DepartmentProgressDisplay from "./DepartmentProgressDisplay"; +import ActivityTableWithStateFilter from "../activity/activity-table/ActivityTableWithStateFilter"; + +export interface IDepartmentProgressProps { + departmentProgress: IDepartmentProgress; +} + +const TabViewTranslate = Translate.Get.containers.progressView; + +const TabbedDepartmentProgressView: React.FC = ( + props: IDepartmentProgressProps +) => { + return ( + + ) + }, + { + header: TabViewTranslate.progressTabGroup.goalAudit, + element: ( + + ) + }, + { + header: TabViewTranslate.progressTabGroup.activityAudit, + element: ( + + ) + } + ]} + /> + ); +}; + +export default TabbedDepartmentProgressView; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/components/department-table/AddNewDepartmentRow.test.tsx b/LCU.WebApps.Operations.IncentiveManager/src/components/department-table/AddNewDepartmentRow.test.tsx new file mode 100644 index 0000000..749c5e3 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/components/department-table/AddNewDepartmentRow.test.tsx @@ -0,0 +1,473 @@ +/* eslint-disable @typescript-eslint/quotes */ +import React from "react"; +import { mount } from "enzyme"; +import AddNewDepartmentRow from "./AddNewDepartmentRow"; +import Translate from "../../translate/Translate"; + +let wrapper: any; + +const mockUniqueFunction = jest.fn(); +const mockSubmitFunction = jest.fn(); + +beforeEach(() => { + mockUniqueFunction.mockReturnValue(true); + wrapper = mount( + + + + mockSubmitFunction(department) + } + isDepartmentNameUnique={(deptName) => { + return mockUniqueFunction(); + }} + businessLines={[ + { name: "businessLine1", code: "bl1", orgCode: "ORG" }, + { name: "businessLine2", code: "bl2", orgCode: "ORG" } + ]} + departmentStateSet={{ + Active: "Active", + Inactive: "Inactive" + }} + regions={[ + { + name: "North", + businessLineCode: "BCHOPS", + code: "NORTH" + }, + { + name: "South", + businessLineCode: "BCHOPS", + code: "South" + } + ]} + /> + +
+ ); +}); + +describe("Associate views initial form", () => { + it("Has proper placeholders in initial state", () => { + expect( + wrapper + .find('[data-test="dept-name-input"]') + .hostNodes() + .prop("value") + ).toBe(""); + expect( + wrapper + .find('[data-test="business-line-input"]') + .hostNodes() + .prop("value") + ).toBe( + Translate.Get.components.departmentTable.addNewDepartmentRow + .businessLinePlaceholder + ); + expect( + wrapper.find('[data-test="region-input"]').hostNodes().prop("value") + ).toBe( + Translate.Get.components.departmentTable.addNewDepartmentRow + .regionPlaceholder + ); + expect( + wrapper + .find('[data-test="dept-state-input"]') + .hostNodes() + .prop("value") + ).toBe( + Translate.Get.components.departmentTable.addNewDepartmentRow + .statePlaceholder + ); + }); +}); + +describe("Associate Can Make Changes", () => { + test("Updating Values Updates State", () => { + expect(wrapper.find("td").length).toBe(5); + wrapper + .find('[data-test="dept-name-input"]') + .hostNodes() + .simulate("change", { target: { value: "NewName" } }); + wrapper + .find('[data-test="business-line-input"]') + .hostNodes() + .simulate("change", { target: { value: "bl1" } }); + wrapper + .find('[data-test="region-input"]') + .hostNodes() + .simulate("change", { target: { value: "NORTH" } }); + wrapper + .find('[data-test="dept-state-input"]') + .hostNodes() + .simulate("change", { target: { value: "Inactive" } }); + wrapper.update(); + expect( + wrapper + .find('[data-test="dept-name-input"]') + .hostNodes() + .prop("value") + ).toBe("NewName"); + expect( + wrapper + .find('[data-test="business-line-input"]') + .hostNodes() + .prop("value") + ).toBe("bl1"); + expect( + wrapper.find('[data-test="region-input"]').hostNodes().prop("value") + ).toBe("NORTH"); + expect( + wrapper + .find('[data-test="dept-state-input"]') + .hostNodes() + .prop("value") + ).toBe("Inactive"); + }); + + test("Only - allowed as special charcter", () => { + expect(wrapper.find("td").length).toBe(5); + wrapper + .find('[data-test="dept-name-input"]') + .hostNodes() + .simulate("change", { + target: { value: "New-Name Name.!@#$!#@$" } + }); + wrapper.update(); + expect( + wrapper + .find('[data-test="dept-name-input"]') + .hostNodes() + .prop("value") + ).toBe("New-NameName"); + }); + + test("Clear is hidden until Changes are Made - Name", () => { + expect( + wrapper + .find('[data-test="clear-button"]') + .hostNodes() + .prop("hidden") + ).toBe(true); + wrapper + .find('[data-test="dept-name-input"]') + .hostNodes() + .simulate("change", { target: { value: "NewName" } }); + wrapper.update(); + expect( + wrapper + .find('[data-test="clear-button"]') + .hostNodes() + .prop("hidden") + ).toBe(false); + }); + + test("Clear is hidden until Changes are Made - BusinessLine", () => { + expect( + wrapper + .find('[data-test="clear-button"]') + .hostNodes() + .prop("hidden") + ).toBe(true); + wrapper + .find('[data-test="business-line-input"]') + .hostNodes() + .simulate("change", { target: { value: "bl1" } }); + wrapper.update(); + expect( + wrapper + .find('[data-test="clear-button"]') + .hostNodes() + .prop("hidden") + ).toBe(false); + }); + + test("Clear is hidden until Changes are Made - State", () => { + expect( + wrapper + .find('[data-test="clear-button"]') + .hostNodes() + .prop("hidden") + ).toBe(true); + wrapper + .find('[data-test="dept-state-input"]') + .hostNodes() + .simulate("change", { target: { value: "Inactive" } }); + wrapper.update(); + expect( + wrapper + .find('[data-test="clear-button"]') + .hostNodes() + .prop("hidden") + ).toBe(false); + }); + + test("Clear is hidden until Changes are Made - Region", () => { + expect( + wrapper + .find('[data-test="clear-button"]') + .hostNodes() + .prop("hidden") + ).toBe(true); + wrapper + .find('[data-test="region-input"]') + .hostNodes() + .simulate("change", { target: { value: "NORTH" } }); + wrapper.update(); + expect( + wrapper + .find('[data-test="clear-button"]') + .hostNodes() + .prop("hidden") + ).toBe(false); + }); +}); + +describe("Associate Inputs Are Validated", () => { + test("Name Must Be at least 3 Characters", () => { + expect( + wrapper + .find('[data-test="submit-button"]') + .hostNodes() + .prop("disabled") + ).toBe(true); + wrapper + .find('[data-test="business-line-input"]') + .hostNodes() + .simulate("change", { target: { value: "bl1" } }); + wrapper + .find('[data-test="region-input"]') + .hostNodes() + .simulate("change", { target: { value: "NORTH" } }); + wrapper + .find('[data-test="dept-state-input"]') + .hostNodes() + .simulate("change", { target: { value: "Inactive" } }); + wrapper + .find('[data-test="dept-name-input"]') + .hostNodes() + .simulate("change", { target: { value: "Ne" } }); + wrapper.update(); + ///Input disabled because too short + expect( + wrapper + .find('[data-test="submit-button"]') + .hostNodes() + .prop("disabled") + ).toBe(true); + expect(wrapper.find("FormFeedback").text()).toContain( + Translate.Get.components.departmentTable.addNewDepartmentRow + .validation.departmentNameLength + ); + + //Input Enabled once name is > 2 characters + expect( + wrapper + .find('[data-test="submit-button"]') + .hostNodes() + .prop("disabled") + ).toBe(true); + wrapper + .find('[data-test="dept-name-input"]') + .hostNodes() + .simulate("change", { target: { value: "New" } }); + wrapper.update(); + expect(wrapper.find("FormFeedback").hostNodes().length).toBe(0); + expect( + wrapper + .find('[data-test="submit-button"]') + .hostNodes() + .prop("disabled") + ).toBe(false); + }); + + test("Name Must Be Unique", () => { + mockUniqueFunction.mockReturnValue(false); + wrapper.update(); + wrapper + .find('[data-test="business-line-input"]') + .hostNodes() + .simulate("change", { target: { value: "bl1" } }); + wrapper + .find('[data-test="region-input"]') + .hostNodes() + .simulate("change", { target: { value: "NORTH" } }); + wrapper + .find('[data-test="dept-state-input"]') + .hostNodes() + .simulate("change", { target: { value: "Inactive" } }); + wrapper + .find('[data-test="dept-name-input"]') + .hostNodes() + .simulate("change", { target: { value: "NewNewName" } }); + wrapper.update(); + + ///Input disabled because unique check fails + expect( + wrapper + .find('[data-test="submit-button"]') + .hostNodes() + .prop("disabled") + ).toBe(true); + expect(wrapper.find("FormFeedback").text()).toContain( + Translate.Get.components.departmentTable.addNewDepartmentRow + .validation.departmentNameUnique + ); + + mockUniqueFunction.mockReturnValue(true); + wrapper.update(); + + //Input Enabled once name is unique + wrapper + .find('[data-test="dept-name-input"]') + .hostNodes() + .simulate("change", { target: { value: "NewNewName" } }); + expect( + wrapper + .find('[data-test="submit-button"]') + .hostNodes() + .prop("disabled") + ).toBe(false); + }); +}); + +describe("Associate can Abandon Changes", () => { + test("Cancel Button Clears Inputs and Returns to Display State", () => { + expect(wrapper.find("td").length).toBe(5); + wrapper + .find('[data-test="dept-name-input"]') + .hostNodes() + .simulate("change", { target: { value: "NewName" } }); + wrapper + .find('[data-test="business-line-input"]') + .hostNodes() + .simulate("change", { target: { value: "bl1" } }); + wrapper + .find('[data-test="region-input"]') + .hostNodes() + .simulate("change", { target: { value: "NORTH" } }); + wrapper + .find('[data-test="dept-state-input"]') + .hostNodes() + .simulate("change", { target: { value: "Inactive" } }); + wrapper.update(); + expect( + wrapper + .find('[data-test="dept-name-input"]') + .hostNodes() + .prop("value") + ).toBe("NewName"); + expect( + wrapper.find('[data-test="region-input"]').hostNodes().prop("value") + ).toBe("NORTH"); + expect( + wrapper + .find('[data-test="dept-state-input"]') + .hostNodes() + .prop("value") + ).toBe("Inactive"); + + wrapper + .find('[data-test="clear-button"]') + .hostNodes() + .simulate("click"); + expect(wrapper.find("td").length).toBe(5); + expect( + wrapper + .find('[data-test="dept-name-input"]') + .hostNodes() + .prop("value") + ).toBe(""); + expect( + wrapper + .find('[data-test="business-line-input"]') + .hostNodes() + .prop("value") + ).toBe( + Translate.Get.components.departmentTable.addNewDepartmentRow + .businessLinePlaceholder + ); + expect( + wrapper.find('[data-test="region-input"]').hostNodes().prop("value") + ).toBe( + Translate.Get.components.departmentTable.addNewDepartmentRow + .regionPlaceholder + ); + expect( + wrapper + .find('[data-test="dept-state-input"]') + .hostNodes() + .prop("value") + ).toBe( + Translate.Get.components.departmentTable.addNewDepartmentRow + .statePlaceholder + ); + }); +}); + +describe("Associate can Submit Changes", () => { + test("Submit Button Sends Correct Form Data", () => { + wrapper + .find('[data-test="dept-name-input"]') + .hostNodes() + .simulate("change", { target: { value: "NewName" } }); + wrapper + .find('[data-test="business-line-input"]') + .hostNodes() + .simulate("change", { target: { value: "bl1" } }); + wrapper + .find('[data-test="region-input"]') + .hostNodes() + .simulate("change", { target: { value: "NORTH" } }); + wrapper + .find('[data-test="dept-state-input"]') + .hostNodes() + .simulate("change", { target: { value: "Inactive" } }); + wrapper.update(); + + wrapper + .find('[data-test="submit-button"]') + .hostNodes() + .simulate("click"); + expect(mockSubmitFunction.mock.calls[0][0]).toEqual({ + departmentName: "NewName", + businessLineCode: "bl1", + regionCode: "NORTH", + state: "Inactive" + }); + wrapper.update(); + + //Assert Values have cleared since submit + expect(wrapper.find("td").length).toBe(5); + expect( + wrapper + .find('[data-test="dept-name-input"]') + .hostNodes() + .prop("value") + ).toBe(""); + expect( + wrapper + .find('[data-test="business-line-input"]') + .hostNodes() + .prop("value") + ).toBe( + Translate.Get.components.departmentTable.addNewDepartmentRow + .businessLinePlaceholder + ); + expect( + wrapper.find('[data-test="region-input"]').hostNodes().prop("value") + ).toBe( + Translate.Get.components.departmentTable.addNewDepartmentRow + .regionPlaceholder + ); + expect( + wrapper + .find('[data-test="dept-state-input"]') + .hostNodes() + .prop("value") + ).toBe( + Translate.Get.components.departmentTable.addNewDepartmentRow + .statePlaceholder + ); + }); +}); diff --git a/LCU.WebApps.Operations.IncentiveManager/src/components/department-table/AddNewDepartmentRow.tsx b/LCU.WebApps.Operations.IncentiveManager/src/components/department-table/AddNewDepartmentRow.tsx new file mode 100644 index 0000000..9a87edc --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/components/department-table/AddNewDepartmentRow.tsx @@ -0,0 +1,249 @@ +import React from "react"; +import { useState } from "react"; +import { Button, ButtonGroup, FormFeedback, Input } from "reactstrap"; +import { IDepartmentDto } from "../../shared/models/Department"; +import { IBusinessLine, IRegion } from "../../shared/models/Settings"; +import { tryUpdateStateWithInput } from "../../shared/services/InputService"; +import Translate from "./../../translate/Translate"; + +export interface IAddNewDepartmentRow { + departmentStateSet: { [key: string]: string }; + businessLines: IBusinessLine[]; + regions: IRegion[]; + submitNewDepartment: (newDepartment: IDepartmentDto) => void; + isDepartmentNameUnique: (deptName?: string) => boolean; +} + +const newDepartmentTranslate = + Translate.Get.components.departmentTable.addNewDepartmentRow; + +const AddNewDepartmentRow: React.FC = (props) => { + const emptyFormState = { + departmentName: undefined, + orgCode: undefined, + businessLineCode: undefined, + regionCode: undefined, + state: undefined + }; + + const [departmentFormState, setFormState] = useState< + Partial + >(emptyFormState); + + const clearFormState = () => { + setFormState(emptyFormState); + }; + + const nameLengthIsInvalid = () => { + return ( + (departmentFormState.departmentName ?? "UNDEFINED_IS_VALID") + .length < 3 + ); + }; + + const nameIsNotUnique = () => { + return !props.isDepartmentNameUnique( + departmentFormState.departmentName + ); + }; + + const shouldDisableSubmit = () => { + return ( + isFormMissingValue() || nameIsNotUnique() || nameLengthIsInvalid() + ); + }; + + const isFormMissingValue = () => { + return ( + departmentFormState.businessLineCode == undefined || + departmentFormState.departmentName == undefined || + departmentFormState.state == undefined || + departmentFormState.regionCode === undefined + ); + }; + + const shouldHideClear = () => { + if ( + departmentFormState.businessLineCode == undefined && + departmentFormState.departmentName == undefined && + departmentFormState.state == undefined && + departmentFormState.regionCode === undefined + ) { + return true; + } + return false; + }; + + return ( + + + { + setFormState( + tryUpdateStateWithInput( + departmentFormState, + "departmentName", + event.target.value.replace(/[^a-zA-Z0-9-]/g, "") + ) + ); + }} + invalid={nameLengthIsInvalid() || nameIsNotUnique()} + /> + {nameLengthIsInvalid() ? ( + + {newDepartmentTranslate.validation.departmentNameLength} + + ) : nameIsNotUnique() ? ( + + {newDepartmentTranslate.validation.departmentNameUnique} + + ) : undefined} + + + { + setFormState( + tryUpdateStateWithInput( + departmentFormState, + "businessLineCode", + event.target.value + ) + ); + }} + > + + {props.businessLines.map((bl) => { + return ( + + ); + })} + + + + + setFormState( + tryUpdateStateWithInput( + departmentFormState, + "regionCode", + event.target.value || null + ) + ) + } + > + + + {props.regions.map((region) => ( + + ))} + + + + { + setFormState( + tryUpdateStateWithInput( + departmentFormState, + "state", + event.target.value + ) + ); + }} + > + + {Object.entries(props.departmentStateSet).map( + ([, value]) => { + return ( + + ); + } + )} + + + + + + + + + + ); +}; + +export default AddNewDepartmentRow; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/components/department-table/DepartmentTable.scss b/LCU.WebApps.Operations.IncentiveManager/src/components/department-table/DepartmentTable.scss new file mode 100644 index 0000000..c71ca98 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/components/department-table/DepartmentTable.scss @@ -0,0 +1,3 @@ +Input { + margin-bottom: 0px; +} diff --git a/LCU.WebApps.Operations.IncentiveManager/src/components/department-table/DepartmentTable.test.tsx b/LCU.WebApps.Operations.IncentiveManager/src/components/department-table/DepartmentTable.test.tsx new file mode 100644 index 0000000..23b0097 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/components/department-table/DepartmentTable.test.tsx @@ -0,0 +1,63 @@ +import React from "react"; +import { shallow } from "enzyme"; +import DepartmentTable from "./DepartmentTable"; +import AsyncState from "./../../shared/enumerations/AsyncState"; +import { IDepartment } from "../../shared/models/Department"; + +describe("Associate Views the DepartmentTable", () => { + const wrapper = shallow( + jest.fn()} + updateDepartment={() => jest.fn} + departments={ + [ + { + id: 5, + departmentName: "Test", + businessLineCode: "bl1", + state: "Active" + }, + { + id: 6, + departmentName: "Test2", + businessLineCode: "bl2", + state: "Active" + }, + { + id: 7, + departmentName: "Test3", + businessLineCode: "bl1", + state: "Active" + }, + { + id: 8, + departmentName: "Test4", + businessLineCode: "bl2", + state: "Active" + }, + { + id: 9, + departmentName: "Test5", + businessLineCode: "bl1", + state: "InActive" + } + ] as IDepartment[] + } + submitState={{ state: AsyncState.Success }} + /> + ); + it("Displays all departments", () => { + // Users cannot make departments. + expect(wrapper.find("AddNewDepartmentRow").length).toBe(0); + expect(wrapper.find("DepartmentTableRow").length).toBe(4); + }); +}); diff --git a/LCU.WebApps.Operations.IncentiveManager/src/components/department-table/DepartmentTable.tsx b/LCU.WebApps.Operations.IncentiveManager/src/components/department-table/DepartmentTable.tsx new file mode 100644 index 0000000..e22a665 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/components/department-table/DepartmentTable.tsx @@ -0,0 +1,89 @@ +import React, { useState } from "react"; +import Translate from "../../translate/Translate"; +import { IDepartment, IDepartmentDto } from "../../shared/models/Department"; +import DepartmentTableRow from "./DepartmentTableRow"; +import { IBusinessLine, IRegion } from "../../shared/models/Settings"; +import IAsyncStateContainer from "../../shared/types/IAsyncStateContainer"; +import DepartmentTableAlertBar from "./DepartmentTableAlertBar"; +import { CustomInput, Form } from "reactstrap"; +import DepartmentService from "../../shared/services/DepartmentService"; + +export interface DepartmentTableProps { + departments: IDepartment[]; + organizationSet: { [key: string]: string }; + departmentStateSet: { [key: string]: string }; + businessLines: IBusinessLine[]; + regions: IRegion[]; + submitNewDepartment: (newDepartment: IDepartmentDto) => void; + updateDepartment: (updatedData: IDepartmentDto, id: number) => void; + submitState: IAsyncStateContainer; +} + +const DepartmentTable: React.FC = (props) => { + const [showDetails, setShowDetails] = useState(false); + let departments = props.departments; + if (!showDetails) { + departments = departments.filter( + (department) => department.state === "Active" + ); + } + + const departmentRows = departments.map((department) => { + return ( + { + props.updateDepartment(updatedData, department.id); + }} + isDepartmentNameUnique={(deptName) => + DepartmentService.isDepartmentNameUnique( + departments, + deptName + ) + } + /> + ); + }); + + const departmentHeaders = Translate.Get.components.departmentTable.headers.map( + (header: string) => { + return {header}; + } + ); + + return ( +
+
{Translate.Get.components.departmentTable.title}
+ +
+
+ { + setShowDetails(event.target.checked); + }} + label="Show All Departments" + className="ml-4 mr-1" + /> + +
+ + + + {departmentHeaders} + + + {departmentRows} +
+
+
+ ); +}; + +export default DepartmentTable; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/components/department-table/DepartmentTableAlertBar.test.tsx b/LCU.WebApps.Operations.IncentiveManager/src/components/department-table/DepartmentTableAlertBar.test.tsx new file mode 100644 index 0000000..b970d75 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/components/department-table/DepartmentTableAlertBar.test.tsx @@ -0,0 +1,68 @@ +import { shallow } from "enzyme"; +import React from "react"; +import DepartmentTableAlertBar from "./DepartmentTableAlertBar"; +import AsyncState from "./../../shared/enumerations/AsyncState"; +import Translate from "./../../translate/Translate"; + +describe("DepartmentTableAlertBar", () => { + it("Displays Nothing If Inactive", () => { + const wrapper = shallow( + + ); + expect(wrapper.find("Alert").length).toBe(0); + expect(wrapper.find("div").length).toBe(1); + }); + + it("Displays Loading Message if Loading", () => { + const wrapper = shallow( + + ); + expect(wrapper.find("Alert").length).toBe(1); + expect(wrapper.find("p").text()).toContain( + Translate.Get.components.departmentTable.departmentTableAlertBar + .loading + ); + expect(wrapper.find("Alert").prop("color")).toEqual("warning"); + }); + + it("Displays Success Message if Success", () => { + const wrapper = shallow( + + ); + expect(wrapper.find("Alert").length).toBe(1); + expect(wrapper.find("Alert").prop("color")).toEqual("success"); + expect(wrapper.find("p").text()).toContain( + Translate.Get.components.departmentTable.departmentTableAlertBar + .success + ); + expect(wrapper.find("p").text()).toContain("Test"); + }); + + it("Displays Fail Message if Failure", () => { + const wrapper = shallow( + + ); + expect(wrapper.find("Alert").length).toBe(1); + expect(wrapper.find("Alert").prop("color")).toEqual("danger"); + expect(wrapper.find("p").text()).toContain( + Translate.Get.components.departmentTable.departmentTableAlertBar + .failure + ); + }); +}); diff --git a/LCU.WebApps.Operations.IncentiveManager/src/components/department-table/DepartmentTableAlertBar.tsx b/LCU.WebApps.Operations.IncentiveManager/src/components/department-table/DepartmentTableAlertBar.tsx new file mode 100644 index 0000000..b916630 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/components/department-table/DepartmentTableAlertBar.tsx @@ -0,0 +1,56 @@ +import React from "react"; +import { Alert, Spinner } from "reactstrap"; +import { IDepartment } from "../../shared/models/Department"; +import IAsyncStateContainer from "../../shared/types/IAsyncStateContainer"; +import AsyncState from "./../../shared/enumerations/AsyncState"; +import Translate from "./../../translate/Translate"; + +export interface IDepartmentTableAlertBarProps { + submitState: IAsyncStateContainer; +} + +const alertBarTranslate = + Translate.Get.components.departmentTable.departmentTableAlertBar; + +const DepartmentTableAlertBar: React.FC = ( + props +) => { + if (props.submitState.state == AsyncState.Success) { + return ( + +

+ {alertBarTranslate.success + + props.submitState.data?.departmentName ?? "-"} +

+
+ ); + } + + if (props.submitState.state == AsyncState.Loading) { + return ( + + +

{alertBarTranslate.loading}

+
+ ); + } + + if (props.submitState.state == AsyncState.Failure) { + return ( + + +

{alertBarTranslate.failure}

+
+ ); + } + + return
; +}; + +export default DepartmentTableAlertBar; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/components/department-table/DepartmentTableRow.test.tsx b/LCU.WebApps.Operations.IncentiveManager/src/components/department-table/DepartmentTableRow.test.tsx new file mode 100644 index 0000000..a6e07a7 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/components/department-table/DepartmentTableRow.test.tsx @@ -0,0 +1,586 @@ +/* eslint-disable @typescript-eslint/quotes */ +import React from "react"; +import { mount, shallow } from "enzyme"; +import DepartmentTableRow from "./DepartmentTableRow"; +import Translate from "../../translate/Translate"; +import { IDepartment } from "../../shared/models/Department"; +import ViewDepartmentTableRow from "./ViewDepartmentTableRow"; + +let wrapper: any; + +const mockUniqueFunction = jest.fn(); +const mockSubmitFunction = jest.fn(); + +export interface IFakePermissionProps { + requiredPermissions: string[]; + showWarning?: boolean; + children: JSX.Element; +} + +export const FakePermissionGuard: React.FC = (props) => { + return props.children; +}; + +jest.mock("../../containers/shared/PermissionGuard", () => FakePermissionGuard); + +beforeEach(() => { + mockUniqueFunction.mockReturnValue(true); + wrapper = mount( + + + + mockSubmitFunction(department) + } + isDepartmentNameUnique={(deptName) => { + return mockUniqueFunction(); + }} + businessLines={[ + { name: "businessLine1", code: "bl1", orgCode: "ORG" }, + { name: "businessLine2", code: "bl2", orgCode: "ORG" } + ]} + departmentStateSet={{ + Avctive: "Active", + Inactive: "Inactive" + }} + regions={[ + { + name: "North", + businessLineCode: "BCHOPS", + code: "NORTH" + }, + { + name: "South", + businessLineCode: "BCHOPS", + code: "SOUTH" + } + ]} + /> + +
+ ); +}); + +describe("User Views a DepartmentTableRow", () => { + it("Displays Proper Department Data", () => { + let x = 0; + const temp = () => { + x++; + }; + const tempWrapper = shallow( + + ); + + expect(tempWrapper.find('[data-test="department_tr_4"]').length).toBe( + 1 + ); + expect(tempWrapper.find('[data-test="edit-row"]').length).toBe(0); + expect(tempWrapper.find("td").length).toBe(6); + expect(tempWrapper.find('[data-test="dept-name"]').length).toBe(1); + expect(tempWrapper.find('[data-test="business-line"]').length).toBe(1); + expect(tempWrapper.find('[data-test="dept-state"]').length).toBe(1); + expect( + tempWrapper.find("td").find("Button").children().text() + ).toContain( + Translate.Get.components.departmentTable.departmentTableRow + .editButton + ); + }); +}); + +describe("Associate Can Toggle and Make Edits", () => { + test("Clicking Edit Button Opens Edit", () => { + wrapper.find("Button").simulate("click"); + wrapper.update(); + + expect(wrapper.find('[data-test="display-row"]').length).toBe(0); + expect(wrapper.find('[data-test="edit-row"]').length).toBe(1); + expect(wrapper.find("td").length).toBe(6); + expect( + wrapper.find('[data-test="dept-name-input"]').hostNodes().length + ).toBe(1); + expect( + wrapper.find('[data-test="business-line-input"]').hostNodes().length + ).toBe(1); + expect( + wrapper.find('[data-test="region-input"]').hostNodes().length + ).toBe(1); + expect( + wrapper.find('[data-test="dept-state-input"]').hostNodes().length + ).toBe(1); + expect( + wrapper.find('[data-test="submit-button"]').hostNodes().text() + ).toContain( + Translate.Get.components.departmentTable.departmentTableRow + .saveUpdateButton + ); + expect( + wrapper.find('[data-test="clear-button"]').hostNodes().length + ).toBe(1); + }); + + test("Initial Values are Set Properly", () => { + wrapper.find("Button").simulate("click"); + wrapper.update(); + + expect(wrapper.find('[data-test="display-row"]').length).toBe(0); + expect(wrapper.find('[data-test="edit-row"]').length).toBe(1); + expect(wrapper.find("td").length).toBe(6); + expect( + wrapper + .find('[data-test="dept-name-input"]') + .hostNodes() + .prop("value") + ).toBe("testName"); + expect( + wrapper + .find('[data-test="business-line-input"]') + .hostNodes() + .prop("value") + ).toBe("bl2"); + expect( + wrapper.find('[data-test="region-input"]').hostNodes().prop("value") + ).toBe(""); + expect( + wrapper + .find('[data-test="dept-state-input"]') + .hostNodes() + .prop("value") + ).toBe("Active"); + expect( + wrapper.find('[data-test="submit-button"]').hostNodes().text() + ).toContain( + Translate.Get.components.departmentTable.departmentTableRow + .saveUpdateButton + ); + expect( + wrapper.find('[data-test="clear-button"]').hostNodes().length + ).toBe(1); + }); + + test("Updating Values Updates State", () => { + wrapper.find("Button").simulate("click"); + wrapper.update(); + + expect(wrapper.find('[data-test="display-row"]').length).toBe(0); + expect(wrapper.find('[data-test="edit-row"]').length).toBe(1); + expect(wrapper.find("td").length).toBe(6); + wrapper + .find('[data-test="dept-name-input"]') + .hostNodes() + .simulate("change", { target: { value: "NewName" } }); + wrapper + .find('[data-test="business-line-input"]') + .hostNodes() + .simulate("change", { target: { value: "bl1" } }); + wrapper + .find('[data-test="region-input"]') + .hostNodes() + .simulate("change", { target: { value: "NORTH" } }); + wrapper + .find('[data-test="dept-state-input"]') + .hostNodes() + .simulate("change", { target: { value: "Inactive" } }); + wrapper.update(); + expect( + wrapper + .find('[data-test="dept-name-input"]') + .hostNodes() + .prop("value") + ).toBe("NewName"); + expect( + wrapper + .find('[data-test="business-line-input"]') + .hostNodes() + .prop("value") + ).toBe("bl1"); + expect( + wrapper.find('[data-test="region-input"]').hostNodes().prop("value") + ).toBe("NORTH"); + expect( + wrapper + .find('[data-test="dept-state-input"]') + .hostNodes() + .prop("value") + ).toBe("Inactive"); + }); + + test("Only - allowed as special charcter", () => { + wrapper.find("Button").simulate("click"); + wrapper.update(); + + expect(wrapper.find("td").length).toBe(6); + wrapper + .find('[data-test="dept-name-input"]') + .hostNodes() + .simulate("change", { + target: { value: "New-Name Name.!@#$!#@$" } + }); + wrapper.update(); + expect( + wrapper + .find('[data-test="dept-name-input"]') + .hostNodes() + .prop("value") + ).toBe("New-NameName"); + }); + + test("Submit is Disabled until Changes are Made - Name", () => { + wrapper.find("Button").simulate("click"); + wrapper.update(); + + expect( + wrapper + .find('[data-test="submit-button"]') + .hostNodes() + .prop("disabled") + ).toBe(true); + wrapper + .find('[data-test="dept-name-input"]') + .hostNodes() + .simulate("change", { target: { value: "NewName" } }); + wrapper.update(); + expect( + wrapper + .find('[data-test="submit-button"]') + .hostNodes() + .prop("disabled") + ).toBe(false); + + wrapper + .find('[data-test="dept-name-input"]') + .hostNodes() + .simulate("change", { target: { value: "testName" } }); + wrapper.update(); + expect( + wrapper + .find('[data-test="submit-button"]') + .hostNodes() + .prop("disabled") + ).toBe(true); + }); + + test("Submit is Disabled until Changes are Made - BusinessLine", () => { + wrapper.find("Button").simulate("click"); + wrapper.update(); + + expect( + wrapper + .find('[data-test="submit-button"]') + .hostNodes() + .prop("disabled") + ).toBe(true); + wrapper + .find('[data-test="business-line-input"]') + .hostNodes() + .simulate("change", { target: { value: "bl1" } }); + wrapper.update(); + expect( + wrapper + .find('[data-test="submit-button"]') + .hostNodes() + .prop("disabled") + ).toBe(false); + wrapper + .find('[data-test="business-line-input"]') + .hostNodes() + .simulate("change", { target: { value: "bl2" } }); + wrapper.update(); + expect( + wrapper + .find('[data-test="submit-button"]') + .hostNodes() + .prop("disabled") + ).toBe(true); + }); + + test("Submit is Disabled until Changes are Made - State", () => { + wrapper.find("Button").simulate("click"); + wrapper.update(); + + expect( + wrapper + .find('[data-test="submit-button"]') + .hostNodes() + .prop("disabled") + ).toBe(true); + wrapper + .find('[data-test="dept-state-input"]') + .hostNodes() + .simulate("change", { target: { value: "Inactive" } }); + wrapper.update(); + expect( + wrapper + .find('[data-test="submit-button"]') + .hostNodes() + .prop("disabled") + ).toBe(false); + + expect( + wrapper + .find('[data-test="submit-button"]') + .hostNodes() + .prop("disabled") + ).toBe(false); + wrapper + .find('[data-test="dept-state-input"]') + .hostNodes() + .simulate("change", { target: { value: "Active" } }); + wrapper.update(); + expect( + wrapper + .find('[data-test="submit-button"]') + .hostNodes() + .prop("disabled") + ).toBe(true); + }); +}); + +describe("Associate Inputs Are Validated", () => { + test("Name Must Be at least 3 Characters", () => { + wrapper.find("Button").simulate("click"); + wrapper.update(); + + expect( + wrapper + .find('[data-test="submit-button"]') + .hostNodes() + .prop("disabled") + ).toBe(true); + wrapper + .find('[data-test="dept-name-input"]') + .hostNodes() + .simulate("change", { target: { value: "Ne" } }); + wrapper.update(); + ///Input disabled because too short + expect( + wrapper + .find('[data-test="submit-button"]') + .hostNodes() + .prop("disabled") + ).toBe(true); + expect(wrapper.find("FormFeedback").text()).toContain( + Translate.Get.components.departmentTable.departmentTableRow + .validation.departmentNameLength + ); + + //Input Enabled once name is > 2 characters + expect( + wrapper + .find('[data-test="submit-button"]') + .hostNodes() + .prop("disabled") + ).toBe(true); + wrapper + .find('[data-test="dept-name-input"]') + .hostNodes() + .simulate("change", { target: { value: "New" } }); + wrapper.update(); + expect(wrapper.find("FormFeedback").hostNodes().length).toBe(0); + expect( + wrapper + .find('[data-test="submit-button"]') + .hostNodes() + .prop("disabled") + ).toBe(false); + }); + + test("Name Must Be Unique If Name is Not Original Name", () => { + wrapper.find("Button").simulate("click"); + wrapper.update(); + + mockUniqueFunction.mockReturnValue(false); + wrapper.update(); + + expect( + wrapper + .find('[data-test="submit-button"]') + .hostNodes() + .prop("disabled") + ).toBe(true); + wrapper + .find('[data-test="dept-name-input"]') + .hostNodes() + .simulate("change", { target: { value: "NewNewName" } }); + wrapper.update(); + + ///Input disabled because unique check fails + expect( + wrapper + .find('[data-test="submit-button"]') + .hostNodes() + .prop("disabled") + ).toBe(true); + expect(wrapper.find("FormFeedback").text()).toContain( + Translate.Get.components.departmentTable.departmentTableRow + .validation.departmentNameUnique + ); + + //Input Enabled once name is unique + expect( + wrapper + .find('[data-test="submit-button"]') + .hostNodes() + .prop("disabled") + ).toBe(true); + wrapper + .find('[data-test="dept-name-input"]') + .hostNodes() + .simulate("change", { target: { value: "NewNewName" } }); + }); +}); + +describe("Associate can Abandon Changes", () => { + test("Cancel Button Clears Inputs and Returns to Display State", () => { + wrapper.find("Button").simulate("click"); + wrapper.update(); + + expect(wrapper.find('[data-test="display-row"]').length).toBe(0); + expect(wrapper.find('[data-test="edit-row"]').length).toBe(1); + expect(wrapper.find("td").length).toBe(6); + wrapper + .find('[data-test="dept-name-input"]') + .hostNodes() + .simulate("change", { target: { value: "NewName" } }); + wrapper + .find('[data-test="business-line-input"]') + .hostNodes() + .simulate("change", { target: { value: "bl1" } }); + wrapper + .find('[data-test="region-input"]') + .hostNodes() + .simulate("change", { target: { value: "SOUTH" } }); + wrapper + .find('[data-test="dept-state-input"]') + .hostNodes() + .simulate("change", { target: { value: "Inactive" } }); + wrapper.update(); + + wrapper + .find('[data-test="clear-button"]') + .hostNodes() + .simulate("click"); + wrapper.update(); + + expect(wrapper.find('[data-test="department_tr_4"]').length).toBe(1); + expect(wrapper.find('[data-test="edit-row"]').length).toBe(0); + + wrapper.find("Button").simulate("click"); + wrapper.update(); + + //Assert Values did Not Update After Cancel and are Reset to Prop Values + expect(wrapper.find('[data-test="display-row"]').length).toBe(0); + expect(wrapper.find('[data-test="edit-row"]').length).toBe(1); + expect(wrapper.find("td").length).toBe(6); + expect( + wrapper + .find('[data-test="dept-name-input"]') + .hostNodes() + .prop("value") + ).toBe("testName"); + expect( + wrapper + .find('[data-test="business-line-input"]') + .hostNodes() + .prop("value") + ).toBe("bl2"); + expect( + wrapper.find('[data-test="region-input"]').hostNodes().prop("value") + ).toBe(""); + expect( + wrapper + .find('[data-test="dept-state-input"]') + .hostNodes() + .prop("value") + ).toBe("Active"); + }); +}); + +describe("Associate can Submit Changes", () => { + test("Submit Button Sends Correct Form Data", () => { + wrapper.find("Button").simulate("click"); + wrapper.update(); + + wrapper + .find('[data-test="dept-name-input"]') + .hostNodes() + .simulate("change", { target: { value: "NewName" } }); + wrapper + .find('[data-test="business-line-input"]') + .hostNodes() + .simulate("change", { target: { value: "bl1" } }); + wrapper + .find('[data-test="region-input"]') + .hostNodes() + .simulate("change", { target: { value: "NORTH" } }); + wrapper + .find('[data-test="dept-state-input"]') + .hostNodes() + .simulate("change", { target: { value: "Inactive" } }); + wrapper.update(); + + wrapper + .find('[data-test="submit-button"]') + .hostNodes() + .simulate("click"); + expect(mockSubmitFunction.mock.calls[0][0]).toEqual({ + departmentName: "NewName", + businessLineCode: "bl1", + state: "Inactive", + id: 4, + regionCode: "NORTH" + }); + wrapper.update(); + + expect(wrapper.find('[data-test="department_tr_4"]').length).toBe(1); + expect(wrapper.find('[data-test="edit-row"]').length).toBe(0); + + wrapper.find("Button").simulate("click"); + wrapper.update(); + + //Assert Values have updated since submit + expect(wrapper.find('[data-test="display-row"]').length).toBe(0); + expect(wrapper.find('[data-test="edit-row"]').length).toBe(1); + expect(wrapper.find("td").length).toBe(6); + expect( + wrapper + .find('[data-test="dept-name-input"]') + .hostNodes() + .prop("value") + ).toBe("testName"); + expect( + wrapper + .find('[data-test="business-line-input"]') + .hostNodes() + .prop("value") + ).toBe("bl2"); + expect( + wrapper.find('[data-test="region-input"]').hostNodes().prop("value") + ).toBe(""); + expect( + wrapper + .find('[data-test="dept-state-input"]') + .hostNodes() + .prop("value") + ).toBe("Active"); + }); +}); diff --git a/LCU.WebApps.Operations.IncentiveManager/src/components/department-table/DepartmentTableRow.tsx b/LCU.WebApps.Operations.IncentiveManager/src/components/department-table/DepartmentTableRow.tsx new file mode 100644 index 0000000..ab2572e --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/components/department-table/DepartmentTableRow.tsx @@ -0,0 +1,37 @@ +import React, { useState } from "react"; +import { IDepartment, IDepartmentDto } from "../../shared/models/Department"; +import { IBusinessLine, IRegion } from "../../shared/models/Settings"; +import ViewDepartmentTableRow from "./ViewDepartmentTableRow"; +import EditDepartmentTableRow from "./EditDepartmentTableRow"; + +export interface IDepartmentTableRowProps { + department: IDepartment; + departmentStateSet: { [key: string]: string }; + businessLines: IBusinessLine[]; + regions: IRegion[]; + updateDepartment: (updatedData: IDepartmentDto) => void; + isDepartmentNameUnique: (deptName: string) => boolean; +} + +const DepartmentTableRow: React.FC = (props) => { + const [isEditing, setEditing] = useState(false); + + return isEditing ? ( + setEditing(false)} + /> + ) : ( + setEditing(true)} + /> + ); +}; + +export default DepartmentTableRow; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/components/department-table/EditDepartmentTableRow.tsx b/LCU.WebApps.Operations.IncentiveManager/src/components/department-table/EditDepartmentTableRow.tsx new file mode 100644 index 0000000..cd822c0 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/components/department-table/EditDepartmentTableRow.tsx @@ -0,0 +1,224 @@ +import React, { useState } from "react"; +import { IDepartment, IDepartmentDto } from "../../shared/models/Department"; +import { tryUpdateStateWithInput } from "../../shared/services/InputService"; +import { + Button, + ButtonGroup, + FormFeedback, + Input, + InputGroup +} from "reactstrap"; +import { IBusinessLine, IRegion } from "../../shared/models/Settings"; +import Translate from "../../translate/Translate"; + +export interface IEditDepartmentTableRowProps { + department: IDepartment; + departmentStateSet: { [key: string]: string }; + businessLines: IBusinessLine[]; + regions: IRegion[]; + updateDepartment: (updatedData: IDepartmentDto) => void; + isDepartmentNameUnique: (deptName: string) => boolean; + disableEdit: () => void; +} + +const tableRowTranslate = + Translate.Get.components.departmentTable.departmentTableRow; + +const EditDepartmentTableRow: React.FC = ( + props +) => { + const [departmentFormState, setFormState] = useState(() => { + return props.department; + }); + + const shouldDisableSubmit = () => { + if ( + departmentFormState.businessLineCode == + props.department.businessLineCode && + departmentFormState.departmentName == + props.department.departmentName && + departmentFormState.state == props.department.state && + departmentFormState.regionCode == props.department.regionCode + ) { + return true; + } + return nameLengthIsInvalid() || nameIsNotUnique(); + }; + + const nameLengthIsInvalid = () => { + return ( + (departmentFormState.departmentName ?? "UNDEFINED_IS_VALID") + .length < 3 + ); + }; + + const nameIsNotUnique = () => { + return ( + props.department.departmentName !== + departmentFormState.departmentName && + !props.isDepartmentNameUnique(departmentFormState.departmentName) + ); + }; + + return ( + + + { + setFormState( + tryUpdateStateWithInput( + departmentFormState, + "departmentName", + event.target.value.replace(/[^a-zA-Z0-9-]/g, "") + ) + ); + }} + data-test="dept-name-input" + invalid={nameLengthIsInvalid() || nameIsNotUnique()} + /> + {nameLengthIsInvalid() ? ( + + {tableRowTranslate.validation.departmentNameLength} + + ) : nameIsNotUnique() ? ( + + {tableRowTranslate.validation.departmentNameUnique} + + ) : undefined} + + + { + setFormState( + tryUpdateStateWithInput( + departmentFormState, + "businessLineCode", + event.target.value || null + ) + ); + }} + data-test="business-line-input" + > + + {props.businessLines.map((bl) => { + return ( + + ); + })} + + + + { + setFormState( + tryUpdateStateWithInput( + departmentFormState, + "regionCode", + event.target.value || null + ) + ); + }} + data-test="region-input" + > + + {props.regions.map((region) => ( + + ))} + + + + + { + setFormState( + tryUpdateStateWithInput( + departmentFormState, + "dnaOrgNbr", + event.target.value.replace(/[^0-9]/g, "") + ) + ); + }} + data-test="dept-orgNbr-input" + /> + + + + { + setFormState( + tryUpdateStateWithInput( + departmentFormState, + "state", + event.target.value + ) + ); + }} + data-test="dept-state-input" + > + {Object.entries(props.departmentStateSet).map( + ([, value]) => { + return ( + + ); + } + )} + + + + + + + + + + ); +}; + +export default EditDepartmentTableRow; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/components/department-table/ViewDepartmentTableRow.tsx b/LCU.WebApps.Operations.IncentiveManager/src/components/department-table/ViewDepartmentTableRow.tsx new file mode 100644 index 0000000..c3373ba --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/components/department-table/ViewDepartmentTableRow.tsx @@ -0,0 +1,48 @@ +import React from "react"; +import { IDepartment } from "../../shared/models/Department"; +import { Button } from "reactstrap"; +import Translate from "../../translate/Translate"; +import PermissionGuard from "../../containers/shared/PermissionGuard"; + +export interface IViewDepartmentTableRowProps { + department: IDepartment; + toggleEdit: () => void; +} + +const tableRowTranslate = + Translate.Get.components.departmentTable.departmentTableRow; + +const ViewDepartmentTableRow: React.FC = ( + props +) => { + return ( + + {props.department.departmentName} + + {props.department.businessLineCode} + + + {props.department.regionCode ?? ( + {tableRowTranslate.noneRegion} + )} + + {props.department.dnaOrgNbr} + {props.department.state} + + + + + + + ); +}; + +export default ViewDepartmentTableRow; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/components/employee/EmployeeAlertBar.test.tsx b/LCU.WebApps.Operations.IncentiveManager/src/components/employee/EmployeeAlertBar.test.tsx new file mode 100644 index 0000000..2d9bb1e --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/components/employee/EmployeeAlertBar.test.tsx @@ -0,0 +1,118 @@ +import React from "react"; +import EmployeeAlertBar from "./EmployeeAlertBar"; +import AsyncState from "./../../shared/enumerations/AsyncState"; +import Translate from "./../../translate/Translate"; +import { IEmployee } from "./../../shared/models/Employee"; +import { mount } from "enzyme"; + +describe("EmployeeAlertBar After Employee Navigates to Employee URL", () => { + const data: IEmployee = { + additionalPermissions: [], + assignments: [], + personId: 5, + adEmail: "test@test.com", + employeeId: "123", + baseRole: "Developers", + homeDepartment: "Development", + fullName: "Test McTester", + departments: [], + role: { + name: "role", + description: "role", + permissions: [], + hasCommissions: true + } + }; + + it("Displays a not found message when state is success but data is undefined", () => { + const wrapper = mount( + + ); + + expect(wrapper.find('[data-test="not-found"]').hostNodes().length).toBe( + 1 + ); + expect(wrapper.find('[data-test="error"]').hostNodes().length).toBe(0); + expect(wrapper.find('[data-test="loading"]').hostNodes().length).toBe( + 0 + ); + + expect(wrapper.text()).toContain( + Translate.Get.components.employee.employeeAlertBar.notFound.title + ); + expect(wrapper.text()).toContain( + Translate.Get.components.employee.employeeAlertBar.notFound.message + ); + expect(wrapper.text()).toContain("123"); + }); + + it("Displays a loading message when data is loading", () => { + const wrapper = mount( + + ); + + expect(wrapper.find('[data-test="not-found"]').hostNodes().length).toBe( + 0 + ); + expect(wrapper.find('[data-test="error"]').hostNodes().length).toBe(0); + expect(wrapper.find('[data-test="loading"]').hostNodes().length).toBe( + 1 + ); + + expect(wrapper.find("p").hostNodes().text()).toContain( + Translate.Get.components.employee.employeeAlertBar.loading.title + ); + }); + + it("Displays a error message when request failed", () => { + const wrapper = mount( + + ); + + expect(wrapper.find('[data-test="not-found"]').hostNodes().length).toBe( + 0 + ); + expect(wrapper.find('[data-test="error"]').hostNodes().length).toBe(1); + expect(wrapper.find('[data-test="loading"]').hostNodes().length).toBe( + 0 + ); + + expect(wrapper.text()).toContain( + Translate.Get.components.employee.employeeAlertBar.error.title + ); + expect(wrapper.text()).toContain( + Translate.Get.components.employee.employeeAlertBar.error.message + ); + }); + + it("Displays nothing when successfully finds data", () => { + const wrapper = mount( + + ); + + expect(wrapper.find('[data-test="not-found"]').length).toBe(0); + expect(wrapper.find('[data-test="error"]').length).toBe(0); + expect(wrapper.find('[data-test="loading"]').length).toBe(0); + }); +}); diff --git a/LCU.WebApps.Operations.IncentiveManager/src/components/employee/EmployeeAlertBar.tsx b/LCU.WebApps.Operations.IncentiveManager/src/components/employee/EmployeeAlertBar.tsx new file mode 100644 index 0000000..7b6a7aa --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/components/employee/EmployeeAlertBar.tsx @@ -0,0 +1,91 @@ +import React from "react"; +import IAsyncStateContainer from "./../../shared/types/IAsyncStateContainer"; +import { IEmployee } from "./../../shared/models/Employee"; +import AsyncState from "./../../shared/enumerations/AsyncState"; +import { Alert, Spinner } from "reactstrap"; +import Translate from "./../../translate/Translate"; + +interface IEmployeeAlertBarProps { + employeeContainer: IAsyncStateContainer; + searchEmployeeId: string; +} + +const employeeAlertBarTranslate = + Translate.Get.components.employee.employeeAlertBar; + +const EmployeeAlertBar: React.FC = (props) => { + if ( + props.employeeContainer.state === AsyncState.Success && + !props.employeeContainer.data + ) { + return ( + +
+
+

+

+
+

+ + {employeeAlertBarTranslate.notFound.title} + {props.searchEmployeeId} + +

+

+ {employeeAlertBarTranslate.notFound.message} +

+
+
+
+ ); + } + + if (props.employeeContainer.state === AsyncState.Failure) { + return ( + +
+
+ +
+
+

+ {employeeAlertBarTranslate.error.title} +

+

+ {employeeAlertBarTranslate.error.message} +

+
+
+
+ ); + } + + if (props.employeeContainer.state === AsyncState.Loading) { + return ( + +
+
+ +
+
+

+ {employeeAlertBarTranslate.loading.title} +

+
+
+
+ ); + } + + return
; +}; + +export default EmployeeAlertBar; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/components/employee/EmployeeDepartmentAssignments.test.tsx b/LCU.WebApps.Operations.IncentiveManager/src/components/employee/EmployeeDepartmentAssignments.test.tsx new file mode 100644 index 0000000..1a7218a --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/components/employee/EmployeeDepartmentAssignments.test.tsx @@ -0,0 +1,15 @@ +import React from "react"; +import { shallow, ShallowWrapper } from "enzyme"; +import EmployeeDepartmentAssignments from "./EmployeeDepartmentAssignments"; +import { IDepartmentAssignment } from "../../shared/models/DepartmentAssignment"; + +describe("Display an employees department assignments", () => { + it("Displays an employee with missing role", () => { + let wrapper: ShallowWrapper; + wrapper = shallow( + + ); + + expect(wrapper.html()).toBe("No Assignments"); + }); +}); diff --git a/LCU.WebApps.Operations.IncentiveManager/src/components/employee/EmployeeDepartmentAssignments.tsx b/LCU.WebApps.Operations.IncentiveManager/src/components/employee/EmployeeDepartmentAssignments.tsx new file mode 100644 index 0000000..05e7cd4 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/components/employee/EmployeeDepartmentAssignments.tsx @@ -0,0 +1,73 @@ +import React from "react"; +import DepartmentDisplay from "../../containers/shared/DepartmentDisplay"; +import { IDepartmentAssignment } from "../../shared/models/DepartmentAssignment"; +import moment from "moment"; + +interface IEmployeeDepartmentAssignments { + assignments?: IDepartmentAssignment[]; +} + +const EmployeeDepartmentAssignments: React.FC = ( + props +) => { + if (!props.assignments) { + return No Assignments; + } + + return ( +
+ + + + + + + + + + + + + + {props.assignments.map((assignment) => { + return ( + + + + + + + + + + ); + })} + +
DepartmentHome/FloatStart DateEnd DateRolePayoutNote
+ + {assignment.isHome ? "Home" : "Float"} + {moment(assignment.startDate).format("ll")} + + {assignment.endDate + ? moment(assignment.endDate).format( + "ll" + ) + : "Current"} + + {assignment.role + ? assignment.role.name + : "Missing Role!"} + {assignment.slidingPayout}%{assignment.note}
+
+ ); +}; + +export default EmployeeDepartmentAssignments; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/components/employee/EmployeeRole.tsx b/LCU.WebApps.Operations.IncentiveManager/src/components/employee/EmployeeRole.tsx new file mode 100644 index 0000000..87b27f3 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/components/employee/EmployeeRole.tsx @@ -0,0 +1,20 @@ +import React from "react"; +import { IAppRole } from "../../shared/models/Settings"; + +interface IEmployeeRoleProps { + role?: IAppRole; +} + +const EmployeeRole: React.FC = (props) => { + if (!props.role) { + return No Role; + } + + return ( +
+ {props.role.name} - {props.role.description} +
+ ); +}; + +export default EmployeeRole; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/components/goal/GoalProgressControls.test.tsx b/LCU.WebApps.Operations.IncentiveManager/src/components/goal/GoalProgressControls.test.tsx new file mode 100644 index 0000000..4ab5320 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/components/goal/GoalProgressControls.test.tsx @@ -0,0 +1,48 @@ +import React from "react"; +import { mount } from "enzyme"; +import GoalProgressControls from "./GoalProgressControls"; +import moment from "moment"; + +describe("Component to select a month range", () => { + it("should provide the current month and year for to and from values", () => { + const stubOnChange = jest.fn(); + mount(); + + expect(stubOnChange).toHaveBeenCalledWith({ + fromMonth: moment().month() + 1, + fromYear: moment().year(), + toMonth: moment().month() + 1, + toYear: moment().year() + }); + }); + + it("should provide the month and year for to and from values when input is changed", () => { + const stubOnChange = jest.fn(); + + const wrapper = mount(); + + stubOnChange.mockClear(); + + wrapper + .find("input[name='dashboardStartMonth']") + .simulate("change", { target: { value: "08/01/2021" } }); + + expect(stubOnChange).toHaveBeenCalledWith({ + fromMonth: 8, + fromYear: 2021, + toMonth: moment().month() + 1, + toYear: moment().year() + }); + + wrapper + .find("input[name='dashboardEndMonth']") + .simulate("change", { target: { value: "9/30/2021" } }); + + expect(stubOnChange).toHaveBeenCalledWith({ + fromMonth: 8, + fromYear: 2021, + toMonth: 9, + toYear: 2021 + }); + }); +}); diff --git a/LCU.WebApps.Operations.IncentiveManager/src/components/goal/GoalProgressControls.tsx b/LCU.WebApps.Operations.IncentiveManager/src/components/goal/GoalProgressControls.tsx new file mode 100644 index 0000000..f8c9811 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/components/goal/GoalProgressControls.tsx @@ -0,0 +1,85 @@ +import React, { useEffect, useState } from "react"; +import moment, { Moment } from "moment"; +import { IMonthRange } from "../../shared/types/IMonthRange"; +import { Button, Form, Input } from "reactstrap"; + +export interface IGoalProgressControlsProps { + onChange: (range: IMonthRange) => void; + hideTitle?: boolean; +} + +const GoalProgressControls: React.FC = ( + props: IGoalProgressControlsProps +) => { + const [fromDate, setFromDate] = useState(moment().startOf("month")); + const [toDate, setToDate] = useState(moment().endOf("month")); + + useEffect(() => { + props.onChange({ + fromMonth: fromDate.month() + 1, + fromYear: fromDate.year(), + toMonth: toDate.month() + 1, + toYear: toDate.year() + }); + }, [fromDate, toDate]); + + const previousMonth = () => { + const newFromMonth = moment(fromDate.subtract(1, "months")); + setFromDate(newFromMonth); + const newToMonth = moment(toDate.subtract(1, "months")); + setToDate(newToMonth); + }; + + const nextMonth = () => { + const newFromMonth = moment(fromDate.add(1, "months")); + setFromDate(newFromMonth); + const newToMonth = moment(toDate.add(1, "months")); + setToDate(newToMonth); + }; + + return ( +
+
+ + { + setFromDate( + moment(event.target.value).startOf("month") + ); + }} + /> + { + setToDate(moment(event.target.value).endOf("month")); + }} + /> + +
+
+ ); +}; + +export default GoalProgressControls; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/components/goal/GoalTable.scss b/LCU.WebApps.Operations.IncentiveManager/src/components/goal/GoalTable.scss new file mode 100644 index 0000000..ebac25a --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/components/goal/GoalTable.scss @@ -0,0 +1,9 @@ +.scrollable-top-parent { + width: 95vw; + overflow-x: auto; + transform: rotateX(180deg); +} + +.scrollable-top-child { + transform: rotateX(180deg); +} diff --git a/LCU.WebApps.Operations.IncentiveManager/src/components/goal/GoalTable.test.tsx b/LCU.WebApps.Operations.IncentiveManager/src/components/goal/GoalTable.test.tsx new file mode 100644 index 0000000..ccaeea9 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/components/goal/GoalTable.test.tsx @@ -0,0 +1,50 @@ +import { shallow } from "enzyme"; +import React from "react"; +import moment from "moment"; +import { IGoal } from "../../shared/models/Goal"; +import GoalTable from "./GoalTable"; +import Product2 from "../../shared/enumerations/Product"; +import { UserType } from "../../shared/models/Auditable"; + +describe("An associate views the goal audit table", () => { + it("Displays a message when there is no data to load", () => { + let wrapper = shallow(); + expect(wrapper.find('[data-test="no-data-message"]').length).toBe(1); + expect(wrapper.find("[data-test='autotable-container']").length).toBe( + 0 + ); + }); + + it("Displays a message when there is no data to load", () => { + let wrapper = shallow(); + expect(wrapper.find('[data-test="no-data-message"]').length).toBe(1); + expect(wrapper.find("[data-test='autotable-container']").length).toBe( + 0 + ); + }); + + it("Does not display message when there is data", () => { + const goals: IGoal[] = [ + { + id: 100, + lastUpdatedByType: UserType.System, + lastUpdatedBy: "Tester", + lastUpdated: moment().utc(), + createdByType: UserType.System, + createdBy: "Tester1", + created: moment().utc(), + employeeId: "11771", + departmentId: 56, + product: Product2.Vehicle, + startDate: moment().utc(), + endDate: moment().utc(), + value: 1 + } + ]; + let wrapper = shallow(); + expect(wrapper.find("[data-test='no-data-message']").length).toBe(0); + expect(wrapper.find("[data-test='autotable-container']").length).toBe( + 1 + ); + }); +}); diff --git a/LCU.WebApps.Operations.IncentiveManager/src/components/goal/GoalTable.tsx b/LCU.WebApps.Operations.IncentiveManager/src/components/goal/GoalTable.tsx new file mode 100644 index 0000000..1b52c81 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/components/goal/GoalTable.tsx @@ -0,0 +1,82 @@ +import "./GoalTable.scss"; +import React from "react"; +import moment from "moment"; +import { IGoal } from "../../shared/models/Goal"; +import Translate from "../../translate/Translate"; +import { AutoTable } from "../auto-table/AutoTable"; +import DepartmentDisplay from "../../containers/shared/DepartmentDisplay"; +import UserDisplay from "../../containers/shared/UserDisplay"; +import GoalService from "../../shared/services/GoalService"; + +interface IActivityTable { + goals?: IGoal[]; +} + +const GoalTableTranslate = Translate.Get.components.goal.goalTable; + +const GoalTable: React.FC = (props) => { + const sortByUpdatedDate = (data: IGoal[]): IGoal[] => { + return data.sort((a, b) => moment(b.lastUpdated).diff(a.lastUpdated)); + }; + + const headers = [ + GoalTableTranslate.headers.id, + GoalTableTranslate.headers.product, + GoalTableTranslate.headers.departmentId, + "Start", + GoalTableTranslate.headers.value, + GoalTableTranslate.headers.personId, + GoalTableTranslate.headers.lastUpdated, + GoalTableTranslate.headers.lastUpdatedBy + ]; + + const cells = [ + (x: IGoal) => x.id, + (x: IGoal) => x.product, + (x: IGoal) => , + (x: IGoal) => moment(x.startDate).format("ll"), + (x: IGoal) => GoalService.formatMonthlyGoal(x.value, x.product), + (x: IGoal) => + x.employeeId ? ( + + ) : ( + + ), + (x: IGoal) => moment.utc(x.lastUpdated).local().format("lll"), + (x: IGoal) => x.lastUpdatedBy + ]; + + if (props.goals && props.goals.length > 0) { + return ( +
+ +
+ ); + } + + return ( +
+
+

+ {GoalTableTranslate.noData} +

+
+ +
+ ); +}; + +export default GoalTable; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/components/goal/GoalsDisplay.test.tsx b/LCU.WebApps.Operations.IncentiveManager/src/components/goal/GoalsDisplay.test.tsx new file mode 100644 index 0000000..4c07d59 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/components/goal/GoalsDisplay.test.tsx @@ -0,0 +1,77 @@ +import { shallow } from "enzyme"; +import React from "react"; +import moment from "moment"; +import { IEmployeeGoals, IGoal } from "../../shared/models/Goal"; +import Product2 from "../../shared/enumerations/Product"; +import { UserType } from "../../shared/models/Auditable"; +import GoalsDisplay from "./GoalsDisplay"; +import { IEmployeeProgress } from "../../shared/models/Activity"; +import { IEmployee } from "../../shared/models/Employee"; + +describe("Display an employee goal and progress", () => { + const mockUser: IEmployee = { + role: { + name: "baseRole", + permissions: [], + description: "fake role", + hasCommissions: true + }, + departments: [ + { + departmentId: 55, + employeeId: "11771", + isHome: true, + note: "fake home assignment", + startDate: moment(), + endDate: moment(), + created: moment(), + createdBy: "nobody", + createdByType: UserType.System, + lastUpdated: moment(), + id: 5, + lastUpdatedBy: "nobody", + lastUpdatedByType: UserType.System, + role: { + name: "role", + description: "role", + permissions: [], + hasCommissions: true + }, + slidingPayout: 100 + } + ], + baseRole: "none", + homeDepartment: "none", + personId: 555, + adEmail: "test@test.com", + employeeId: "11771", + fullName: "Test McTester", + additionalPermissions: [], + assignments: [] + }; + + it("Displays goal and progress table", () => { + const userGoals: IEmployeeGoals = { + month: 6, + year: 2021, + user: mockUser, + goals: {}, + departments: [], + goalAudit: [] + }; + const userProgress: IEmployeeProgress = { + user: mockUser, + month: 6, + year: 2021, + activityAudit: [], + departments: [], + progress: {} + }; + let wrapper = shallow( + + ); + expect( + wrapper.find("[data-test='goal-progress-table-view']").length + ).toBe(1); + }); +}); diff --git a/LCU.WebApps.Operations.IncentiveManager/src/components/goal/GoalsDisplay.tsx b/LCU.WebApps.Operations.IncentiveManager/src/components/goal/GoalsDisplay.tsx new file mode 100644 index 0000000..cd7821f --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/components/goal/GoalsDisplay.tsx @@ -0,0 +1,43 @@ +import React from "react"; +import DepartmentDisplay from "../../containers/shared/DepartmentDisplay"; +import { IEmployeeProgress } from "../../shared/models/Activity"; +import ProductTableHeader from "../shared/ProductTableHeader"; +import ProgressTableRow from "../shared/ProgressTableRow"; + +export interface IGoalsDisplayProps { + userProgress: IEmployeeProgress; +} + +const GoalsDisplay: React.FC = ( + props: IGoalsDisplayProps +) => { + const goals = props.userProgress.goals; + const departments = props.userProgress.departments; + const progress = props.userProgress.progress; + + const rows = departments.map((department) => { + const goal = goals[department.departmentId]; + const activity = progress[department.departmentId]; + return ( + + } + progress={activity} + goals={goal} + /> + ); + }); + + return ( + + + {rows} +
+ ); +}; + +export default GoalsDisplay; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/components/goal/MonthRangeControl.test.tsx b/LCU.WebApps.Operations.IncentiveManager/src/components/goal/MonthRangeControl.test.tsx new file mode 100644 index 0000000..85adc20 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/components/goal/MonthRangeControl.test.tsx @@ -0,0 +1,121 @@ +import React from "react"; +import { mount } from "enzyme"; +import MonthRangeControl from "./MonthRangeControl"; + +describe("Component to select a month range with no state", () => { + // If you are reading this test, this is what is breaking all the useEffects. + // When you have children with useEffect and useState AND a Parent, you'll double render! 2^useEffect in the tree. + // Compare this to GoalProgressControls... + it("should render", () => { + const stubOnChange = jest.fn(); + const range = { + fromMonth: 7, + fromYear: 2021, + toMonth: 7, + toYear: 2021 + }; + mount(); + expect(stubOnChange).not.toHaveBeenCalledWith(range); + }); + + it("should provide the from month when input is changed", () => { + const stubOnChange = jest.fn(); + const range = { + fromMonth: 7, + fromYear: 2021, + toMonth: 7, + toYear: 2021 + }; + const wrapper = mount( + + ); + + stubOnChange.mockClear(); + + wrapper + .find("#fromMonth") + .simulate("change", { target: { value: "8" } }); + + expect(stubOnChange).toHaveBeenCalledWith({ + fromMonth: 8, + fromYear: 2021, + toMonth: 7, + toYear: 2021 + }); + }); + it("should provide the to month when input is changed", () => { + const stubOnChange = jest.fn(); + const range = { + fromMonth: 7, + fromYear: 2021, + toMonth: 7, + toYear: 2021 + }; + const wrapper = mount( + + ); + + stubOnChange.mockClear(); + + wrapper.find("#toMonth").simulate("change", { target: { value: "9" } }); + + expect(stubOnChange).toHaveBeenCalledWith({ + fromMonth: 7, + fromYear: 2021, + toMonth: 9, + toYear: 2021 + }); + }); + + it("should provide the from year when input is changed", () => { + const stubOnChange = jest.fn(); + const range = { + fromMonth: 7, + fromYear: 2021, + toMonth: 7, + toYear: 2021 + }; + const wrapper = mount( + + ); + + stubOnChange.mockClear(); + + wrapper + .find("#fromYear") + .simulate("change", { target: { value: "2022" } }); + + expect(stubOnChange).toHaveBeenCalledWith({ + fromMonth: 7, + fromYear: 2022, + toMonth: 7, + toYear: 2021 + }); + }); + + it("should provide the to year when input is changed", () => { + const stubOnChange = jest.fn(); + const range = { + fromMonth: 7, + fromYear: 2021, + toMonth: 7, + toYear: 2021 + }; + const wrapper = mount( + + ); + + stubOnChange.mockClear(); + + wrapper.find("#toYear").simulate("change", { + target: { value: "2023" } + }); + + expect(stubOnChange).toHaveBeenCalledWith({ + fromMonth: 7, + fromYear: 2021, + toMonth: 7, + toYear: 2023 + }); + }); +}); diff --git a/LCU.WebApps.Operations.IncentiveManager/src/components/goal/MonthRangeControl.tsx b/LCU.WebApps.Operations.IncentiveManager/src/components/goal/MonthRangeControl.tsx new file mode 100644 index 0000000..67b170a --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/components/goal/MonthRangeControl.tsx @@ -0,0 +1,102 @@ +import React from "react"; +import moment from "moment"; +import GoalService from "../../shared/services/GoalService"; +import { IMonthRange } from "../../shared/types/IMonthRange"; + +export interface IGoalProgressControlsProps { + onChange: (range: IMonthRange) => void; + monthRange: IMonthRange; +} + +const MonthRangeControl: React.FC = ({ + monthRange, + onChange +}) => { + const setMonthRange = (monthRange: IMonthRange) => { + onChange(monthRange); + }; + + const years = [moment().year() - 2, moment().year() - 1, moment().year()]; + + return ( + +
+ + + to + + +
+
+ ); +}; + +export default MonthRangeControl; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/components/goal/TabbedProgressView.test.tsx b/LCU.WebApps.Operations.IncentiveManager/src/components/goal/TabbedProgressView.test.tsx new file mode 100644 index 0000000..057dee0 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/components/goal/TabbedProgressView.test.tsx @@ -0,0 +1,80 @@ +import { shallow } from "enzyme"; +import React from "react"; +import moment from "moment"; +import { IEmployeeGoals } from "../../shared/models/Goal"; +import { UserType } from "../../shared/models/Auditable"; +import TabbedProgressView from "./TabbedProgressView"; +import { IEmployeeProgress } from "../../shared/models/Activity"; +import { IEmployee } from "../../shared/models/Employee"; + +describe("Basic tabbed goals and activity progress", () => { + const mockUser: IEmployee = { + role: { + name: "baseRole", + permissions: [], + description: "fake role", + hasCommissions: true + }, + departments: [ + { + departmentId: 55, + employeeId: "11771", + isHome: true, + note: "fake home assignment", + startDate: moment(), + endDate: moment(), + created: moment(), + createdBy: "nobody", + createdByType: UserType.System, + lastUpdated: moment(), + id: 5, + lastUpdatedBy: "nobody", + lastUpdatedByType: UserType.System, + role: { + name: "role", + description: "role", + permissions: [], + hasCommissions: true + }, + slidingPayout: 100 + } + ], + baseRole: "none", + homeDepartment: "none", + personId: 555, + adEmail: "test@test.com", + employeeId: "11771", + fullName: "Test McTester", + assignments: [], + additionalPermissions: [] + }; + + it("Displays the table", () => { + const userGoals: IEmployeeGoals = { + month: 6, + year: 2021, + user: mockUser, + goals: {}, + departments: [], + goalAudit: [] + }; + const userProgress: IEmployeeProgress = { + user: mockUser, + month: 6, + year: 2021, + activityAudit: [], + departments: [], + progress: {} + }; + + const wrapper = shallow( + + ); + expect(wrapper.find("[data-test='progress-tabbed-view']").length).toBe( + 1 + ); + }); +}); diff --git a/LCU.WebApps.Operations.IncentiveManager/src/components/goal/TabbedProgressView.tsx b/LCU.WebApps.Operations.IncentiveManager/src/components/goal/TabbedProgressView.tsx new file mode 100644 index 0000000..78912c1 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/components/goal/TabbedProgressView.tsx @@ -0,0 +1,53 @@ +import React from "react"; +import Translate from "./../../translate/Translate"; +import { ActivityType, IEmployeeProgress } from "../../shared/models/Activity"; +import TabView from "../tab-view/TabView"; +import GoalsDisplay from "./GoalsDisplay"; +import GoalTable from "./GoalTable"; +import ActivityTableWithStateFilter from "../activity/activity-table/ActivityTableWithStateFilter"; + +export interface IGoalsDisplayProps { + userProgress: IEmployeeProgress; +} + +const TabViewTranslate = Translate.Get.containers.progressView; + +const TabbedProgressView: React.FC = ( + props: IGoalsDisplayProps +) => { + return ( + + }, + { + header: TabViewTranslate.progressTabGroup.goalAudit, + element: + }, + { + header: TabViewTranslate.progressTabGroup.activitySales, + element: ( + + ) + }, + { + header: TabViewTranslate.progressTabGroup.activityReferrals, + element: ( + + ) + } + ]} + /> + ); +}; + +export default TabbedProgressView; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/components/goal/dispersal/DisersalFromAlertBar.test.tsx b/LCU.WebApps.Operations.IncentiveManager/src/components/goal/dispersal/DisersalFromAlertBar.test.tsx new file mode 100644 index 0000000..5e8e4e7 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/components/goal/dispersal/DisersalFromAlertBar.test.tsx @@ -0,0 +1,65 @@ +import { mount, shallow, ShallowWrapper } from "enzyme"; +import React from "react"; +import AsyncState from "../../../shared/enumerations/AsyncState"; +import Translate from "../../../translate/Translate"; +import DispersalFormAlertBar from "./DispersalFormAlertBar"; + +let wrapper: any; + +const DFATranslate = + Translate.Get.components.goal.goalDispersal.dispersalFormAlertBar; + +describe("Alert bar displays expected message", () => { + it("Displays loading messsage when loading", () => { + wrapper = mount( + + ); + expect( + wrapper.find(`[data-test="loading"]`).hostNodes().text() + ).toContain(DFATranslate.loading); + }); + + it("Displays loading messsage when error", () => { + wrapper = mount( + + ); + expect( + wrapper.find(`[data-test="error"]`).hostNodes().text() + ).toContain(DFATranslate.failure); + }); + + it("Displays success messsage when success", () => { + wrapper = mount( + + ); + expect( + wrapper.find(`[data-test="success"]`).hostNodes().text() + ).toContain(DFATranslate.success.alerts); + expect( + wrapper.find(`[data-test="success"]`).hostNodes().text() + ).toContain(DFATranslate.success.message); + }); + + it("Displays sucess messsage when success and no data", () => { + wrapper = mount( + + ); + expect( + wrapper.find(`[data-test="success"]`).hostNodes().text() + ).not.toContain(DFATranslate.success.alerts); + expect( + wrapper.find(`[data-test="success"]`).hostNodes().text() + ).toContain(DFATranslate.success.message); + }); +}); diff --git a/LCU.WebApps.Operations.IncentiveManager/src/components/goal/dispersal/DispersalForm.test.tsx b/LCU.WebApps.Operations.IncentiveManager/src/components/goal/dispersal/DispersalForm.test.tsx new file mode 100644 index 0000000..6bfcf93 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/components/goal/dispersal/DispersalForm.test.tsx @@ -0,0 +1,150 @@ +import React from "react"; +import { shallow, ShallowWrapper } from "enzyme"; +import DispersalForm from "./DispersalForm"; +import AsyncState from "./../../../shared/enumerations/AsyncState"; +import Translate from "../../../translate/Translate"; +import { AllowedGoalDispersalStrategy } from "../../../shared/models/GoalDispersal"; + +let wrapper: ShallowWrapper; + +beforeEach(() => { + wrapper = shallow( + + ); +}); + +const DFTranslate = Translate.Get.components.goal.goalDispersal.dispersalForm; + +describe("User Opens Dispersal Form", () => { + it("Displays placeholder data for drop down and description", () => { + expect( + wrapper.find('[data-test="select-strategy"]').prop("value") + ).toBe(""); + expect( + wrapper.find('[data-test="strategy-description"]').text() + ).toContain(DFTranslate.description.placeholder); + }); + it("disables submit", () => { + expect(wrapper.find("Button").prop("disabled")).toBe(true); + }); +}); +describe("User Closes Dispersal Form", () => { + it("displays the proper description based on selection", () => { + expect( + wrapper.find('[data-test="select-strategy"]').prop("value") + ).toBe(""); + wrapper.find('[data-test="select-strategy"]').invoke("onChange")({ + target: { + value: AllowedGoalDispersalStrategy.ExtrapolateFromExistingGoals.toString() + } + }); + expect( + wrapper.find('[data-test="select-strategy"]').prop("value") + ).toBe( + AllowedGoalDispersalStrategy.ExtrapolateFromExistingGoals.toString() + ); + expect(wrapper.find('[data-test="strategy-description"]').text()).toBe( + DFTranslate.description.extrapolateFromExisting + ); + }); + it("disables submit until an option is selected", () => { + expect( + wrapper.find('[data-test="select-strategy"]').prop("value") + ).toBe(""); + wrapper.find('[data-test="select-strategy"]').invoke("onChange")({ + target: { + value: AllowedGoalDispersalStrategy.ExtrapolateFromExistingGoals.toString() + } + }); + expect(wrapper.find('[data-test="strategy-description"]').text()).toBe( + DFTranslate.description.extrapolateFromExisting + ); + expect(wrapper.find("Button").prop("disabled")).toBe(false); + }); + it("does not allow submit when loading", () => { + wrapper.setProps({ submitState: AsyncState.Loading }); + wrapper.update(); + expect( + wrapper.find('[data-test="select-strategy"]').prop("value") + ).toBe(""); + wrapper.find('[data-test="select-strategy"]').invoke("onChange")({ + target: { + value: AllowedGoalDispersalStrategy.ExtrapolateFromExistingGoals.toString() + } + }); + expect(wrapper.find('[data-test="strategy-description"]').text()).toBe( + DFTranslate.description.extrapolateFromExisting + ); + expect(wrapper.find("Button").prop("disabled")).toBe(true); + }); +}); + +describe("User Opens Dispersal From and Selects CustomWeightedDispersal", () => { + it("Displays the correct descripton for CustomWeightedDispersal", () => { + expect( + wrapper.find('[data-test="select-strategy"]').prop("value") + ).toBe(""); + wrapper.find('[data-test="select-strategy"]').invoke("onChange")({ + target: { + value: AllowedGoalDispersalStrategy.CustomWeightedDispersal.toString() + } + }); + expect(wrapper.find('[data-test="strategy-description"]').text()).toBe( + DFTranslate.description.customWeighted + ); + }); + + it("Hides Default Submit Button and Warning", () => { + expect(wrapper.find("Button").prop("hidden")).toBe(false); + expect( + wrapper.find('[data-test="select-strategy"]').prop("value") + ).toBe(""); + wrapper.find('[data-test="select-strategy"]').invoke("onChange")({ + target: { + value: AllowedGoalDispersalStrategy.CustomWeightedDispersal.toString() + } + }); + expect(wrapper.find('[data-test="strategy-description"]').text()).toBe( + DFTranslate.description.customWeighted + ); + expect(wrapper.find("Button").prop("hidden")).toBe(true); + }); + + it("Shows the CustomWeightedDispersalForm When Strategy is Selected", () => { + expect( + wrapper + .find('[data-test="custom-weighted-dispersal-dropdown"]') + .prop("isOpen") + ).toBe(false); + expect( + wrapper.find('[data-test="select-strategy"]').prop("value") + ).toBe(""); + wrapper.find('[data-test="select-strategy"]').invoke("onChange")({ + target: { + value: AllowedGoalDispersalStrategy.ExtrapolateFromExistingGoals.toString() + } + }); + expect( + wrapper + .find('[data-test="custom-weighted-dispersal-dropdown"]') + .prop("isOpen") + ).toBe(false); + wrapper.find('[data-test="select-strategy"]').invoke("onChange")({ + target: { + value: AllowedGoalDispersalStrategy.CustomWeightedDispersal.toString() + } + }); + expect(wrapper.find('[data-test="strategy-description"]').text()).toBe( + DFTranslate.description.customWeighted + ); + expect( + wrapper + .find('[data-test="custom-weighted-dispersal-dropdown"]') + .prop("isOpen") + ).toBe(true); + }); +}); diff --git a/LCU.WebApps.Operations.IncentiveManager/src/components/goal/dispersal/DispersalForm.tsx b/LCU.WebApps.Operations.IncentiveManager/src/components/goal/dispersal/DispersalForm.tsx new file mode 100644 index 0000000..19b93f1 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/components/goal/dispersal/DispersalForm.tsx @@ -0,0 +1,164 @@ +import React from "react"; +import { + Button, + Collapse, + Dropdown, + FormGroup, + Input, + Label +} from "reactstrap"; +import { useState } from "react"; +import { useEffect } from "react"; +import { AllowedGoalDispersalStrategy } from "../../../shared/models/GoalDispersal"; +import AsyncState from "../../../shared/enumerations/AsyncState"; +import Translate from "./../../../translate/Translate"; +import CustomWeightedDispersalForm from "./custom-weighted-dispersal/CustomWeightedDispersalForm"; +import { IProductDetail } from "../../../shared/enumerations/Product"; +import { SettingsContext } from "../../../shared/providers/SettingsProvider"; +import { ICustomWeightedDispersalFormData } from "./../../../shared/models/GoalDispersal"; + +export interface IDispersalForm { + isOpen?: boolean; + confirmStatelessDispersal: ( + selectedStrategy: AllowedGoalDispersalStrategy + ) => void; + confirmWeightedDispersal: ( + formData: ICustomWeightedDispersalFormData + ) => void; + submitState: AsyncState; +} + +const DFTranslate = Translate.Get.components.goal.goalDispersal.dispersalForm; + +const DispersalForm: React.FC = (props) => { + const [selected, selectOption] = useState(""); + const settings = React.useContext(SettingsContext); + + useEffect(() => { + selectOption(""); + }, [props.isOpen]); + + useEffect(() => { + if (props.submitState === AsyncState.Success) { + selectOption(""); + } + }, [props.submitState]); + + const getDescription = () => { + switch (selected) { + case AllowedGoalDispersalStrategy.ExtrapolateFromExistingGoals.toString(): + return DFTranslate.description.extrapolateFromExisting; + case AllowedGoalDispersalStrategy.CustomWeightedDispersal.toString(): + return DFTranslate.description.customWeighted; + default: + return DFTranslate.description.placeholder; + } + }; + + const doesStrategyHaveForm = () => { + if (selected === AllowedGoalDispersalStrategy.CustomWeightedDispersal) { + return true; + } else return false; + }; + + const getStrategyFromSelected = (): AllowedGoalDispersalStrategy => { + const key = selected as keyof typeof AllowedGoalDispersalStrategy; + return AllowedGoalDispersalStrategy[key]; + }; + + return ( + +
+
+ + + { + selectOption(event.target.value.toString()); + }} + > + + {Object.values(AllowedGoalDispersalStrategy) + .filter((key) => isNaN(Number(key))) + .map((value) => { + return ( + + ); + })} + + + + +

+ {getDescription()} +

+
+ + + + + {/* Are there additonally required fields? Display them here. Hide this submit button and provide your own in your details component*/} +
+ +
+
+ { + props.confirmWeightedDispersal(formData); + }} + products={settings?.productDetails ?? []} + roles={settings?.appRoles ?? []} + /> +
+
+
+ ); +}; + +export default DispersalForm; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/components/goal/dispersal/DispersalFormAlertBar.tsx b/LCU.WebApps.Operations.IncentiveManager/src/components/goal/dispersal/DispersalFormAlertBar.tsx new file mode 100644 index 0000000..03701f4 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/components/goal/dispersal/DispersalFormAlertBar.tsx @@ -0,0 +1,102 @@ +import React from "react"; +import { Alert, Spinner } from "reactstrap"; +import AsyncState from "../../../shared/enumerations/AsyncState"; +import IAsyncStateContainer from "../../../shared/types/IAsyncStateContainer"; +import { IGoalDispersalReceipt } from "../../../shared/models/GoalDispersal"; +import DispersalReceiptErrorDisplay from "./DispersalReceiptErrorDisplay"; +import Translate from "./../../../translate/Translate"; + +export interface IDispersalFormAlertBarProps { + goalDispersalReceiptContainer: IAsyncStateContainer; +} + +const DFATranslate = + Translate.Get.components.goal.goalDispersal.dispersalFormAlertBar; + +const DispersalFormAlertBar: React.FC = ( + props +) => { + if (props.goalDispersalReceiptContainer.state === AsyncState.Loading) { + return ( + +
+
+ +
+
+

{DFATranslate.loading}

+
+
+
+ ); + } else if ( + props.goalDispersalReceiptContainer.state === AsyncState.Failure + ) { + return ( + +
+
+ +
+
+

{DFATranslate.failure}

+
+
+
+ ); + } else if ( + props.goalDispersalReceiptContainer.state == AsyncState.Success && + props.goalDispersalReceiptContainer.data + ) { + return ( + +
+
+ +
+
+

{DFATranslate.success.message}

+
+ {DFATranslate.success.alerts} + +
+
+
+ ); + } else if ( + props.goalDispersalReceiptContainer.state == AsyncState.Success + ) { + return ( + +
+
+ +
+
+

{DFATranslate.success.message}

+
+
+
+ ); + } + + return
; +}; + +export default DispersalFormAlertBar; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/components/goal/dispersal/DispersalReceiptErrorDisplay.test.tsx b/LCU.WebApps.Operations.IncentiveManager/src/components/goal/dispersal/DispersalReceiptErrorDisplay.test.tsx new file mode 100644 index 0000000..08c147e --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/components/goal/dispersal/DispersalReceiptErrorDisplay.test.tsx @@ -0,0 +1,122 @@ +import React from "react"; +import { shallow, ShallowWrapper } from "enzyme"; +import DispersalReceiptErrorDisplay from "./DispersalReceiptErrorDisplay"; +import Translate from "../../../translate/Translate"; + +const DEDTranslate = + Translate.Get.components.goal.goalDispersal.dispersalReceiptErrorDisplay; + +let wrapper: ShallowWrapper; +beforeEach(() => { + wrapper = shallow( + + ); +}); + +describe("DispersalFormReceiptErrorDisplay has to display every error type", () => { + it("Properly displays new appUser error", () => { + expect( + wrapper + .find('[data-test="error-row"]') + .findWhere((x) => x.key() == "emp2") + .text() + ).toContain(DEDTranslate.userRequiresManualEntry); + expect( + wrapper + .find('[data-test="error-row"]') + .findWhere((x) => x.key() == "emp2") + .text() + ).toContain("Batman"); + }); + it("Properly displays permission error", () => { + expect( + wrapper + .find('[data-test="error-row"]') + .findWhere((x) => x.key() == "emp1") + .text() + ).toContain(DEDTranslate.userWithoutPermission); + expect( + wrapper + .find('[data-test="error-row"]') + .findWhere((x) => x.key() == "emp1") + .text() + ).toContain("John Wick"); + }); + it("Properly displays appUser no longer ind epartment error", () => { + expect( + wrapper + .find('[data-test="error-row"]') + .findWhere((x) => x.key() == "emp3") + .text() + ).toContain(DEDTranslate.userNoLongerInDepartment); + expect( + wrapper + .find('[data-test="error-row"]') + .findWhere((x) => x.key() == "emp3") + .text() + ).toContain("Superman"); + }); + it("Properly displays 'other' error", () => { + expect( + wrapper + .find('[data-test="error-row"]') + .findWhere((x) => x.key() == "emp4") + .text() + ).toContain(DEDTranslate.default); + expect( + wrapper + .find('[data-test="error-row"]') + .findWhere((x) => x.key() == "emp4") + .text() + ).toContain("Megaman"); + }); + it("Falls back to employeeID when no name is available", () => { + expect( + wrapper + .find('[data-test="error-row"]') + .findWhere((x) => x.key() == "emp5") + .text() + ).toContain(DEDTranslate.userWithoutPermission); + expect( + wrapper + .find('[data-test="error-row"]') + .findWhere((x) => x.key() == "emp5") + .text() + ).toContain("emp5"); + }); +}); diff --git a/LCU.WebApps.Operations.IncentiveManager/src/components/goal/dispersal/DispersalReceiptErrorDisplay.tsx b/LCU.WebApps.Operations.IncentiveManager/src/components/goal/dispersal/DispersalReceiptErrorDisplay.tsx new file mode 100644 index 0000000..7d5a6da --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/components/goal/dispersal/DispersalReceiptErrorDisplay.tsx @@ -0,0 +1,42 @@ +import React from "react"; +import { IGoalDispersalReceipt } from "../../../shared/models/GoalDispersal"; +import Translate from "./../../../translate/Translate"; + +export interface IDispersalReceiptErrorDisplayProps { + dispersalReceipt: IGoalDispersalReceipt; +} + +const DEDTranslate = + Translate.Get.components.goal.goalDispersal.dispersalReceiptErrorDisplay; + +const GetMesageFromErrorType = (errorType: string) => { + switch (errorType) { + case "DispersedToUserWithoutPermission": + return DEDTranslate.userWithoutPermission; + case "NewUserRequiresManualEntry": + return DEDTranslate.userRequiresManualEntry; + case "UserNoLongerInDepartment": + return DEDTranslate.userNoLongerInDepartment; + default: + return DEDTranslate.default; + } +}; + +const DispersalReceiptErrorDisplay: React.FC = ( + props +) => { + return ( +
+ {props.dispersalReceipt.goalDispersalErrors.map((error) => { + return ( +

+ {error.employeeName ?? error.employeeId} {"->"}{" "} + {GetMesageFromErrorType(error.errorType)} +

+ ); + })} +
+ ); +}; + +export default DispersalReceiptErrorDisplay; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/components/goal/dispersal/custom-weighted-dispersal/CustomWeightedDispersalForm.test.tsx b/LCU.WebApps.Operations.IncentiveManager/src/components/goal/dispersal/custom-weighted-dispersal/CustomWeightedDispersalForm.test.tsx new file mode 100644 index 0000000..145e1cc --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/components/goal/dispersal/custom-weighted-dispersal/CustomWeightedDispersalForm.test.tsx @@ -0,0 +1,307 @@ +import React from "react"; +import { ShallowWrapper, shallow } from "enzyme"; +import { IProductDetail } from "../../../../shared/enumerations/Product"; +import { AllowedGoalDispersalStrategy } from "../../../../shared/models/GoalDispersal"; +import { IAppRole } from "../../../../shared/models/Settings"; +import CustomWeightedDispersalForm from "./CustomWeightedDispersalForm"; + +let wrapper: ShallowWrapper; + +beforeEach(() => { + wrapper = shallow( + + ); +}); +describe("Submit Button is Disabled until percentage add up to 100.", () => { + beforeEach(() => { + wrapper.find('[name="DispersalProduct"]').invoke("onChange")({ + target: { value: "ATMCard" } + }); + wrapper + .find('[data-test="dispersal-weight-input"]') + .invoke("handleChange")([ + { + roleName: "Admin", + value: 50 + } + ]); + }); + it("Allows Submit when all inputs are populated", () => { + expect( + wrapper.find('[name="SubmitDispersalButton"]').prop("disabled") + ).toBe(true); + }); + it("Disables submit if any given percentage is above 100%", () => { + wrapper + .find('[data-test="dispersal-weight-input"]') + .invoke("handleChange")([ + { + roleName: "LIC", + value: 100 + } + ]); + expect( + wrapper.find('[name="SubmitDispersalButton"]').prop("disabled") + ).toBe(false); + }); + it("Disables submit if any given percentage is above 100%", () => { + wrapper + .find('[data-test="dispersal-weight-input"]') + .invoke("handleChange")([ + { + roleName: "LIC", + value: 101 + } + ]); + expect( + wrapper.find('[name="SubmitDispersalButton"]').prop("disabled") + ).toBe(true); + }); +}); + +describe("Submit Button is Disabled until FormState is valid", () => { + beforeEach(() => { + wrapper.find('[name="DispersalProduct"]').invoke("onChange")({ + target: { value: "ATMCard" } + }); + wrapper + .find('[data-test="dispersal-weight-input"]') + .invoke("handleChange")([ + { + roleName: "Admin", + value: 100 + } + ]); + }); + it("Allows Submit when all inputs are populated", () => { + expect( + wrapper.find('[name="SubmitDispersalButton"]').prop("disabled") + ).toBe(false); + }); + it("Disables submit when product is not selected", () => { + wrapper.find('[name="DispersalProduct"]').invoke("onChange")({ + target: { value: "" } + }); + expect( + wrapper.find('[name="SubmitDispersalButton"]').prop("disabled") + ).toBe(true); + }); + it("Disables submit when there are placeholder Roles in form state", () => { + wrapper + .find('[data-test="dispersal-weight-input"]') + .invoke("handleChange")([ + { + roleName: "PLACE-HOLDER-ROW", + value: 50 + } + ]); + expect( + wrapper.find('[name="SubmitDispersalButton"]').prop("disabled") + ).toBe(true); + }); + + it("Disables submit if any given percentage is below 0%", () => { + wrapper + .find('[data-test="dispersal-weight-input"]') + .invoke("handleChange")([ + { + roleName: "LIC", + value: -1 + } + ]); + expect( + wrapper.find('[name="SubmitDispersalButton"]').prop("disabled") + ).toBe(true); + }); +}); + +describe("A User Opens the Form", () => { + it("Starts with an expected placeholder role value", () => { + wrapper.find('[name="DispersalProduct"]').invoke("onChange")({ + target: { value: "ATMCard" } + }); + expect( + wrapper + .find('[data-test="dispersal-weight-input"]') + .prop("weightsByRole") + ).toEqual([{ roleName: "PLACE-HOLDER-ROW", value: 0 }]); + }); +}); + +describe("A User adds a new role", () => { + beforeEach(() => { + wrapper.find('[name="DispersalProduct"]').invoke("onChange")({ + target: { value: "ATMCard" } + }); + }); + + it("Starts with add role disabled", () => { + expect(wrapper.find('[name="AddNewRoleButton"]').prop("disabled")).toBe( + true + ); + expect( + wrapper + .find('[data-test="dispersal-weight-input"]') + .prop("weightsByRole") + ).toEqual([{ roleName: "PLACE-HOLDER-ROW", value: 0 }]); + }); + + it("Enables the add button when a role is picked", () => { + wrapper + .find('[data-test="dispersal-weight-input"]') + .invoke("handleChange")([ + { + roleName: "Admin", + value: 50 + } + ]); + expect(wrapper.find('[name="AddNewRoleButton"]').prop("disabled")).toBe( + false + ); + }); + + it("Creates new role with a placeholder value when clicked", () => { + wrapper + .find('[data-test="dispersal-weight-input"]') + .invoke("handleChange")([ + { + roleName: "Admin", + value: 50 + } + ]); + expect( + wrapper + .find('[data-test="dispersal-weight-input"]') + .prop("weightsByRole") + ).toEqual([{ roleName: "Admin", value: 50 }]); + wrapper.find('[name="AddNewRoleButton"]').invoke("onClick")(undefined); + expect( + wrapper + .find('[data-test="dispersal-weight-input"]') + .prop("weightsByRole") + ).toEqual([ + { roleName: "Admin", value: 50 }, + { roleName: "PLACE-HOLDER-ROW", value: 0 } + ]); + }); + + it("Does not allow a new role to be added until all roles populated", () => { + wrapper + .find('[data-test="dispersal-weight-input"]') + .invoke("handleChange")([ + { + roleName: "Admin", + value: 50 + } + ]); + wrapper.find('[name="AddNewRoleButton"]').invoke("onClick")(undefined); + expect( + wrapper + .find('[data-test="dispersal-weight-input"]') + .prop("weightsByRole") + ).toEqual([ + { roleName: "Admin", value: 50 }, + { roleName: "PLACE-HOLDER-ROW", value: 0 } + ]); + expect(wrapper.find('[name="AddNewRoleButton"]').prop("disabled")).toBe( + true + ); + }); +}); + +const roles: IAppRole[] = [ + { + name: "Admin", + description: "Admin", + permissions: [], + hasCommissions: false, + rank: 99 + }, + { + name: "LIC", + description: "Landmark Investment Center", + permissions: [], + hasCommissions: true, + rank: 1 + }, + { + name: "RegionalManager", + description: "Regional Manager", + permissions: [], + hasCommissions: true, + rank: 97 + }, + { + name: "BM", + description: "Branch Manager", + permissions: [], + hasCommissions: true, + rank: 1 + }, + { + name: "ABM", + description: "Assistant Branch Manager", + permissions: [], + hasCommissions: true, + rank: 1 + }, + { + name: "BOM", + description: "Branch Operations Manager", + permissions: [], + hasCommissions: true, + rank: 1 + }, + { + name: "PFO", + description: "PERS FIN OFFICER", + permissions: [], + hasCommissions: true, + rank: 1 + } +]; + +const productDetails = ([ + { + product: "ATMCard", + title: "ATM Card", + isSellable: false, + isReferrable: true, + isNonPaying: true + }, + { + product: "CreditCardBalanceTransfer", + title: "CCBT", + isSellable: false, + isReferrable: true, + isNonPaying: false + }, + { + product: "NewChecking", + title: "New Checking", + isSellable: true, + isReferrable: true, + isNonPaying: false + }, + { + product: "GAPInsurance", + title: "Gap Insurance", + isSellable: true, + isReferrable: false, + isNonPaying: false + }, + { + product: "CreditCard", + title: "Credit Card", + isSellable: false, + isReferrable: true, + isNonPaying: false + } +] as unknown) as IProductDetail[]; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/components/goal/dispersal/custom-weighted-dispersal/CustomWeightedDispersalForm.tsx b/LCU.WebApps.Operations.IncentiveManager/src/components/goal/dispersal/custom-weighted-dispersal/CustomWeightedDispersalForm.tsx new file mode 100644 index 0000000..da49266 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/components/goal/dispersal/custom-weighted-dispersal/CustomWeightedDispersalForm.tsx @@ -0,0 +1,188 @@ +import React, { useState, useEffect } from "react"; +import { IProductDetail } from "../../../../shared/enumerations/Product"; +import { + AllowedGoalDispersalStrategy, + IRoleDispersalWeight, + ICustomWeightedDispersalRequest, + ICustomWeightedDispersalFormData +} from "../../../../shared/models/GoalDispersal"; +import { Button, Col, FormFeedback, FormGroup, Input, Row } from "reactstrap"; +import DispersalWeightInput from "./DispersalWeightInput"; +import { IAppRole } from "../../../../shared/models/Settings"; +import _ from "lodash"; +import Translate from "../../../../translate/Translate"; + +export interface ICustomWeightedDispersalForm { + selectedStrategy: AllowedGoalDispersalStrategy; + products: IProductDetail[]; + roles: IAppRole[]; + confirmDispersal: (formData: ICustomWeightedDispersalFormData) => void; +} + +const PlaceHolderRoleName = "PLACE-HOLDER-ROW"; + +const getDefaultFormState = (): ICustomWeightedDispersalFormData => { + return { + dispersalProduct: "", + weightsByRole: [{ roleName: PlaceHolderRoleName, value: 0 }] + }; +}; + +const _translate = + Translate.Get.components.goal.goalDispersal.customWeightedDispersal + .customWeightedDispersalForm; + +const CustomWeightedDispersalForm: React.FC = ( + props +) => { + useEffect(() => { + setFormState(getDefaultFormState()); + }, [props.selectedStrategy]); + + const [ + formState, + setFormState + ] = useState(getDefaultFormState()); + + const hasPlaceholderRow = (): boolean => { + return formState.weightsByRole.some( + (x) => x.roleName === PlaceHolderRoleName + ); + }; + + const weightsAddUpTo100 = (): boolean => { + return formState.weightsByRole.reduce((a, b) => a + b.value, 0) === 100; + }; + + const addNewPlaceholderRow = () => { + if (hasPlaceholderRow()) { + return; + } + setFormState({ + ...formState, + weightsByRole: [ + ...formState.weightsByRole, + { roleName: PlaceHolderRoleName, value: 0 } + ] + }); + }; + + const handleWeightUpdate = (newWeights: IRoleDispersalWeight[]) => { + setFormState({ + ...formState, + weightsByRole: newWeights + }); + }; + + const isFormStateValid = () => { + const noPlaceHolderRows = !hasPlaceholderRow(); + const weightsAreBetween0and100 = formState.weightsByRole.every( + (x) => x.value >= 0 && x.value <= 100 + ); + + const dispersalProductSelected = formState.dispersalProduct != ""; + + return ( + noPlaceHolderRows && + weightsAreBetween0and100 && + dispersalProductSelected && + weightsAddUpTo100() + ); + }; + + const products = props.products.filter((x) => x.hasGoals); + + return ( +
+ + + + + { + setFormState({ + ...formState, + dispersalProduct: event.target.value + }); + }} + > + + {products.map((product) => { + return ( + + ); + })} + + + {_translate.productTypeInput.validation} + + + + + { + handleWeightUpdate(newWeights); + }} + /> + + + + +
+ {weightsAddUpTo100() ? ( + "" + ) : ( + + {_translate.addTo100Total} + + )} +
+
+ +
+ +

+ + {_translate.submitButtonWarning} + +
+
+
+ ); +}; + +export default CustomWeightedDispersalForm; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/components/goal/dispersal/custom-weighted-dispersal/DispersalWeightInput.test.tsx b/LCU.WebApps.Operations.IncentiveManager/src/components/goal/dispersal/custom-weighted-dispersal/DispersalWeightInput.test.tsx new file mode 100644 index 0000000..86f20c6 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/components/goal/dispersal/custom-weighted-dispersal/DispersalWeightInput.test.tsx @@ -0,0 +1,228 @@ +import React from "react"; +import { ShallowWrapper, shallow } from "enzyme"; +import DispersalWeightInput from "./DispersalWeightInput"; +import { IAppRole } from "../../../../shared/models/Settings"; +import { IRoleDispersalWeight } from "./../../../../shared/models/GoalDispersal"; + +let wrapper: ShallowWrapper; + +let weightsByRole: IRoleDispersalWeight[] = []; + +beforeEach(() => { + weightsByRole = []; + wrapper = shallow( + (weightsByRole = formData)} + /> + ); +}); + +describe("A User selects a Role", () => { + it("Only allows a role to be selected once", () => { + //Admin will be input row at index 0 + //LIC will be input row at index 1 + wrapper.setProps({ + weightsByRole: [ + { roleName: "Admin", value: 100 }, + { roleName: "LIC", value: 50 } + ], + selectedRole: "LIC" + }); + wrapper.update(); + expect( + (wrapper + .find('[data-test="dispersal-weight-input-row-1"]') + .prop("roles") as IAppRole[]).find((x) => x.name === "Admin") + ).toBe(undefined); + expect( + (wrapper + .find('[data-test="dispersal-weight-input-row-0"]') + .prop("roles") as IAppRole[]).find((x) => x.name === "LIC") + ).toBe(undefined); + }); + it("Allows a appUser to-reselect the currently selected row", () => { + //Admin will be input row at index 0 + //LIC will be input row at index 1 + wrapper.setProps({ + weightsByRole: [ + { roleName: "Admin", value: 100 }, + { roleName: "LIC", value: 50 } + ] + }); + wrapper.update(); + expect( + (wrapper + .find('[data-test="dispersal-weight-input-row-1"]') + .prop("roles") as IAppRole[]).find((x) => x.name === "LIC") + ).toEqual({ + name: "LIC", + description: "Landmark Investment Center", + permissions: [], + hasCommissions: true, + rank: 1 + }); + expect( + (wrapper + .find('[data-test="dispersal-weight-input-row-0"]') + .prop("roles") as IAppRole[]).find((x) => x.name === "Admin") + ).toEqual({ + name: "Admin", + description: "Admin", + permissions: [], + hasCommissions: false, + rank: 99 + }); + }); + it("Updates the correct role name", () => { + wrapper.setProps({ + weightsByRole: [ + { roleName: "Admin", value: 100 }, + { roleName: "LIC", value: 50 }, + { roleName: "RegionalManager", value: 100 } + ] + }); + wrapper.update(); + wrapper + .find('[data-test="dispersal-weight-input-row-1"]') + .invoke("setRole")("BM"); + expect(weightsByRole.some((x) => x.roleName == "BM")).toBe(true); + expect(weightsByRole.some((x) => x.roleName == "Admin")).toBe(true); + expect(weightsByRole.some((x) => x.roleName == "RegionalManager")).toBe( + true + ); + expect(weightsByRole.some((x) => x.roleName == "LIC")).toBe(false); + }); +}); + +describe("A User Updates a RoleWeight", () => { + it("Updates the correct role value", () => { + wrapper.setProps({ + weightsByRole: [ + { roleName: "Admin", value: 100 }, + { roleName: "LIC", value: 50 }, + { roleName: "RegionalManager", value: 99 } + ] + }); + wrapper.update(); + wrapper + .find('[data-test="dispersal-weight-input-row-1"]') + .invoke("setValue")(79); + expect(weightsByRole.find((x) => x.roleName == "LIC").value).toBe(79); + expect(weightsByRole.find((x) => x.roleName == "Admin").value).toBe( + 100 + ); + expect( + weightsByRole.find((x) => x.roleName == "RegionalManager").value + ).toBe(99); + }); +}); + +describe("A User removes a Row", () => { + it("Removes the Correct Row", () => { + wrapper.setProps({ + weightsByRole: [ + { roleName: "Admin", value: 100 }, + { roleName: "LIC", value: 50 }, + { roleName: "RegionalManager", value: 99 } + ] + }); + wrapper.update(); + wrapper + .find('[data-test="dispersal-weight-input-row-1"]') + .invoke("removeRow")(); + expect(weightsByRole.length).toBe(2); + expect(weightsByRole.some((x) => x.roleName == "Admin")).toBe(true); + expect(weightsByRole.some((x) => x.roleName == "RegionalManager")).toBe( + true + ); + expect(weightsByRole.some((x) => x.roleName == "LIC")).toBe(false); + }); + it("Does not allow the last row to be removed", () => { + wrapper.setProps({ + weightsByRole: [ + { roleName: "Admin", value: 100 }, + { roleName: "LIC", value: 50 }, + { roleName: "RegionalManager", value: 99 } + ] + }); + wrapper.update(); + expect( + wrapper + .find('[data-test="dispersal-weight-input-row-0"]') + .prop("disableDelete") + ).toBe(false); + expect( + wrapper + .find('[data-test="dispersal-weight-input-row-1"]') + .prop("disableDelete") + ).toBe(false); + expect( + wrapper + .find('[data-test="dispersal-weight-input-row-2"]') + .prop("disableDelete") + ).toBe(false); + wrapper.setProps({ + weightsByRole: [{ roleName: "RegionalManager", value: 99 }] + }); + wrapper.update(); + expect( + wrapper + .find('[data-test="dispersal-weight-input-row-0"]') + .prop("disableDelete") + ).toBe(true); + }); +}); + +const roles: IAppRole[] = [ + { + name: "Admin", + description: "Admin", + permissions: [], + hasCommissions: false, + rank: 99 + }, + { + name: "LIC", + description: "Landmark Investment Center", + permissions: [], + hasCommissions: true, + rank: 1 + }, + { + name: "RegionalManager", + description: "Regional Manager", + permissions: [], + hasCommissions: true, + rank: 97 + }, + { + name: "BM", + description: "Branch Manager", + permissions: [], + hasCommissions: true, + rank: 1 + }, + { + name: "ABM", + description: "Assistant Branch Manager", + permissions: [], + hasCommissions: true, + rank: 1 + }, + { + name: "BOM", + description: "Branch Operations Manager", + permissions: [], + hasCommissions: true, + rank: 1 + }, + { + name: "PFO", + description: "PERS FIN OFFICER", + permissions: [], + hasCommissions: true, + rank: 1 + } +]; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/components/goal/dispersal/custom-weighted-dispersal/DispersalWeightInput.tsx b/LCU.WebApps.Operations.IncentiveManager/src/components/goal/dispersal/custom-weighted-dispersal/DispersalWeightInput.tsx new file mode 100644 index 0000000..cd93830 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/components/goal/dispersal/custom-weighted-dispersal/DispersalWeightInput.tsx @@ -0,0 +1,84 @@ +import _ from "lodash"; +import React, { useEffect } from "react"; +import { IRoleDispersalWeight } from "../../../../shared/models/GoalDispersal"; +import { IAppRole } from "../../../../shared/models/Settings"; +import DispersalWeightInputRow from "./DispersalWeightInputRow"; + +export interface IDispersalWeightInputFormProps { + roles: IAppRole[]; + weightsByRole: IRoleDispersalWeight[]; + handleChange: (fromData: IRoleDispersalWeight[]) => void; +} + +const DispersalWeightInput: React.FC = ( + props +) => { + const determineRolesToDisplay = (currentRoleName: string): IAppRole[] => { + const currentRole = props.roles.find( + (x) => x.name.toLowerCase() === currentRoleName.toLowerCase() + ); + if (currentRole !== undefined) { + return [currentRole, ...unselectedRoles()]; + } else { + return [...unselectedRoles()]; + } + }; + + const unselectedRoles = (): IAppRole[] => { + return props.roles.filter( + (role) => + props.weightsByRole + .map((existing) => existing.roleName) + .find((roleName) => role.name === roleName) === undefined + ); + }; + + const updateRowRole = (index: number, newRoleName: string) => { + const newRole = props.roles.find( + (x) => x.name.toLowerCase() === newRoleName.toLowerCase() + ); + if (newRole === undefined) { + return; + } + const newWeightsByRole = [...props.weightsByRole]; + newWeightsByRole[index].roleName = newRole.name; + props.handleChange(newWeightsByRole); + }; + + const updateRowValue = (index: number, newValue: number) => { + const newWeightsByRole = [...props.weightsByRole]; + newWeightsByRole[index].value = newValue; + props.handleChange(newWeightsByRole); + }; + + const removeRow = (index: number) => { + const newWeightsByRole = [...props.weightsByRole]; + newWeightsByRole.splice(index, 1); + props.handleChange(newWeightsByRole); + }; + + return ( +
+ {props.weightsByRole.map((roleWeight, i) => { + return ( + { + removeRow(i); + }} + setRole={(role) => updateRowRole(i, role)} + setValue={(percent) => updateRowValue(i, percent)} + /> + ); + })} +
+ ); +}; + +export default DispersalWeightInput; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/components/goal/dispersal/custom-weighted-dispersal/DispersalWeightInputRow.tsx b/LCU.WebApps.Operations.IncentiveManager/src/components/goal/dispersal/custom-weighted-dispersal/DispersalWeightInputRow.tsx new file mode 100644 index 0000000..0c1efd6 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/components/goal/dispersal/custom-weighted-dispersal/DispersalWeightInputRow.tsx @@ -0,0 +1,110 @@ +import React from "react"; +import { + Button, + Col, + FormFeedback, + FormGroup, + Input, + InputGroup, + InputGroupAddon, + Row +} from "reactstrap"; +import { IAppRole } from "../../../../shared/models/Settings"; +import Translate from "../../../../translate/Translate"; + +export interface IDispersalWeightInputRowProps { + rowIndex: number; + roles: IAppRole[]; + selectedRole: string; + percentage: number; + removeRow: () => void; + setRole: (role: string) => void; + setValue: (newValue: number) => void; + disableDelete: boolean; +} + +const _translate = + Translate.Get.components.goal.goalDispersal.customWeightedDispersal + .dispersalWeightInputRow; + +const DispersalWeightInputRow: React.FC = ( + props +) => { + return ( + + + + + props.setRole(event.target.value)} + value={props.selectedRole} + > + + {props.roles.map((role, i) => { + return ( + + ); + })} + + + {_translate.roleInput.validation} + + + + + + + + 100 + } + onChange={(event) => + props.setValue(parseInt(event.target.value)) + } + value={props.percentage} + > + + + + + {_translate.valueInput.validation} + + + + + + ); +}; +export default DispersalWeightInputRow; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/components/layout/AppContainer.tsx b/LCU.WebApps.Operations.IncentiveManager/src/components/layout/AppContainer.tsx new file mode 100644 index 0000000..823dd54 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/components/layout/AppContainer.tsx @@ -0,0 +1,16 @@ +import React from "react"; +import Container from "./Container"; +import Footer from "./Footer"; +import Navigation from "./Navigation"; + +const AppContainer: React.FC = (props) => { + return ( +
+ + {props.children} +
+
+ ); +}; + +export default AppContainer; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/components/layout/Container.tsx b/LCU.WebApps.Operations.IncentiveManager/src/components/layout/Container.tsx new file mode 100644 index 0000000..11e5b9c --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/components/layout/Container.tsx @@ -0,0 +1,12 @@ +import React from "react"; +import "./Layout.scss"; + +const Container: React.FC = (props) => { + return ( +
+ {props.children} +
+ ); +}; + +export default Container; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/components/layout/Footer.tsx b/LCU.WebApps.Operations.IncentiveManager/src/components/layout/Footer.tsx new file mode 100644 index 0000000..4a3aa82 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/components/layout/Footer.tsx @@ -0,0 +1,22 @@ +import "./Layout.scss"; +import {BUILD_STAMP, VERSION} from "../../environments/version"; +import React from "react"; + +const Footer: React.FC = () => { + const versionHint = VERSION.version + "-" + BUILD_STAMP; + const currentYear = new Date().getFullYear(); + return ( +
+
+
+

+ Copyright © {currentYear} Landmark Credit Union +

+
+
+
+ ); +}; + +export default Footer; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/components/layout/Layout.scss b/LCU.WebApps.Operations.IncentiveManager/src/components/layout/Layout.scss new file mode 100644 index 0000000..bb947d9 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/components/layout/Layout.scss @@ -0,0 +1,10 @@ +footer { + position: relative; + left: 0px; + bottom: 0px; + width: 100vw; +} + +.container-height { + min-height: 88vh; +} diff --git a/LCU.WebApps.Operations.IncentiveManager/src/components/layout/Navigation.tsx b/LCU.WebApps.Operations.IncentiveManager/src/components/layout/Navigation.tsx new file mode 100644 index 0000000..a0c53b1 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/components/layout/Navigation.tsx @@ -0,0 +1,386 @@ +import React from "react"; +import { + DropdownItem, + DropdownMenu, + DropdownToggle, + Navbar, + NavbarBrand, + UncontrolledDropdown +} from "reactstrap"; +import Translate from "../../translate/Translate"; +import Configuration from "./../../shared/configuration/Configuration"; +import { useMsal } from "@azure/msal-react"; +import PermissionGuard from "../../containers/shared/PermissionGuard"; + +export interface NavigationProps { + linkCallback?: (selection: Element) => void; +} + +const NavigationTranslate = Translate.Get.components.layout.navigation; + +const Navigation: React.FC = () => { + const msal = useMsal(); + const account = msal.instance.getActiveAccount(); + const name = account ? account.name : ""; + const isProd = Configuration.Get.name === "prod"; + + return ( + + + {Translate.Get.components.layout.navigation.navbarBrand} + {isProd ? ( + "" + ) : ( + + )} + + + + + {NavigationTranslate.adminDropDown.label} + + + + <> + + { + NavigationTranslate.adminDropDown + .headers.dashboard + } + + + + { + NavigationTranslate.adminDropDown + .links.dashboardBusiness + } + + + + + { + NavigationTranslate.adminDropDown + .links.dashboardRegion + } + + + + + { + NavigationTranslate.adminDropDown + .links.reports + } + + + + + { + NavigationTranslate.adminDropDown.links + .dashboardDepartment + } + + + + { + NavigationTranslate.adminDropDown.links + .dashboardEmployee + } + + + + + + + { + NavigationTranslate.adminDropDown.headers + .activity + } + + + + + { + NavigationTranslate.adminDropDown.links + .activityAudit + } + + + + + { + NavigationTranslate.adminDropDown.links + .newActivity + } + + + + + { + NavigationTranslate.adminDropDown.links + .deadActivityAudit + } + + + + <> + + + { + NavigationTranslate.adminDropDown + .headers.goals + } + + + + { + NavigationTranslate.adminDropDown + .links.departmentGoals + } + + + + + { + NavigationTranslate.adminDropDown + .links.personnelGoals + } + + + + + { + NavigationTranslate.adminDropDown + .links.commissions + } + + + + + + <> + + + { + NavigationTranslate.adminDropDown + .headers.personnel + } + + + + { + NavigationTranslate.adminDropDown.links + .profileSearch + } + + + + + <> + + { + NavigationTranslate.adminDropDown.links + .payoutFile + } + + + { + NavigationTranslate.adminDropDown.links + .eoeList + } + + + + + + { + NavigationTranslate.adminDropDown.headers + .configuration + } + + + { + NavigationTranslate.adminDropDown.links + .organizationHierarchy + } + + + { + NavigationTranslate.adminDropDown.links + .roleManagement + } + + + + { + NavigationTranslate.adminDropDown.links + .roleMapping + } + + + + { + NavigationTranslate.adminDropDown.links + .payoutProfiles + } + + + + + + {name} + + + ); +}; + +export default Navigation; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/components/paged-content-controls/PagedContentControls.test.tsx b/LCU.WebApps.Operations.IncentiveManager/src/components/paged-content-controls/PagedContentControls.test.tsx new file mode 100644 index 0000000..bbd5632 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/components/paged-content-controls/PagedContentControls.test.tsx @@ -0,0 +1,172 @@ +import PagedContentControls from "./PagedContentControls"; +import IPagedResponse from "./../../shared/types/IPagedResponse"; +import React from "react"; +import { mount } from "enzyme"; +import Translate from "../../translate/Translate"; + +const pagedContent: IPagedResponse = { + nextPage: "nextpage.com/2", + totalPages: 100, + currentPage: 1, + items: [], + totalItems: 100000 +}; + +const PCCTranslate = Translate.Get.components.pagedContentControls; + +describe("A appUser wants to page through paged results", () => { + it("Displays Properly", () => { + const wrapper = mount( + + ); + + expect( + wrapper + .find(`[aria-label="back-button"]`) + .hostNodes() + .prop("disabled") + ).toBe(true); + expect( + wrapper + .find(`[aria-label="next-button"]`) + .hostNodes() + .prop("disabled") + ).toBe(false); + expect( + wrapper + .find(`[data-test="page-info"]`) + .hostNodes() + .find("button") + .text() + ).toContain(PCCTranslate.pageLabel + " 1 of 100 (100000)"); + }); + + it("Disables back when at the start of the paged list", () => { + const wrapper = mount( + + ); + wrapper.setProps({ + pagedResponse: { + ...pagedContent, + nextPage: null, + previousPage: "prevpage.com/1", + currentPage: 100 + } + }); + wrapper.update(); + expect( + wrapper + .find(`[aria-label="back-button"]`) + .hostNodes() + .prop("disabled") + ).toBe(false); + expect( + wrapper + .find(`[aria-label="next-button"]`) + .hostNodes() + .prop("disabled") + ).toBe(true); + expect( + wrapper + .find(`[data-test="page-info"]`) + .hostNodes() + .find("button") + .text() + ).toContain(PCCTranslate.pageLabel + " 100 of 100 (100000)"); + }); + it("Disables buttons when paged response is undefined", () => { + const wrapper = mount( + + ); + expect( + wrapper + .find(`[aria-label="back-button"]`) + .hostNodes() + .prop("disabled") + ).toBe(true); + expect( + wrapper + .find(`[aria-label="next-button"]`) + .hostNodes() + .prop("disabled") + ).toBe(true); + expect( + wrapper + .find(`[data-test="page-info"]`) + .hostNodes() + .find("button") + .text() + ).toContain(PCCTranslate.pageLabel + " 0 of 0 (0)"); + }); + it("Displays the last page values when loading the next page values", () => { + const wrapper = mount( + + ); + expect( + wrapper + .find(`[aria-label="back-button"]`) + .hostNodes() + .prop("disabled") + ).toBe(true); + expect( + wrapper + .find(`[aria-label="next-button"]`) + .hostNodes() + .prop("disabled") + ).toBe(false); + expect( + wrapper + .find(`[data-test="page-info"]`) + .hostNodes() + .find("button") + .text() + ).toContain(PCCTranslate.pageLabel + " 1 of 100 (100000)"); + wrapper.setProps({ + pagedResponse: { + ...pagedContent, + nextPage: null, + previousPage: "prevpage.com/1", + currentPage: 50 + } + }); + wrapper.update(); + + wrapper.setProps({ pagedResponse: undefined }); + wrapper.update(); + expect( + wrapper + .find(`[aria-label="back-button"]`) + .hostNodes() + .prop("disabled") + ).toBe(true); + expect( + wrapper + .find(`[aria-label="next-button"]`) + .hostNodes() + .prop("disabled") + ).toBe(true); + expect( + wrapper + .find(`[data-test="page-info"]`) + .hostNodes() + .find("button") + .text() + ).toContain(PCCTranslate.pageLabel + " 50 of 100 (100000)"); + }); +}); diff --git a/LCU.WebApps.Operations.IncentiveManager/src/components/paged-content-controls/PagedContentControls.tsx b/LCU.WebApps.Operations.IncentiveManager/src/components/paged-content-controls/PagedContentControls.tsx new file mode 100644 index 0000000..9caccd0 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/components/paged-content-controls/PagedContentControls.tsx @@ -0,0 +1,68 @@ +import React, { useEffect, useState } from "react"; +import IPagedResponse from "./../../shared/types/IPagedResponse"; +import { Button, ButtonGroup } from "reactstrap"; +import Translate from "./../../translate/Translate"; + +export interface IPagedContentControlsProps { + nextPage: () => void; + prevPage: () => void; + pagedResponse?: IPagedResponse; +} + +const PCCTranslate = Translate.Get.components.pagedContentControls; + +const PagedContentControls: React.FC = (props) => { + const [cachedPageCount, setCachedPageCount] = useState( + props.pagedResponse?.totalPages ?? 0 + ); + const [cachedTotal, setCachedTotal] = useState( + props.pagedResponse?.totalItems ?? 0 + ); + const [cachedCurrentPage, setCachedCurrentPage] = useState( + props.pagedResponse?.currentPage ?? 0 + ); + + useEffect(() => { + if (props.pagedResponse && props.pagedResponse.totalPages) { + setCachedPageCount(props.pagedResponse.totalPages); + } + if (props.pagedResponse && props.pagedResponse.totalItems) { + setCachedTotal(props.pagedResponse.totalItems); + } + if (props.pagedResponse && props.pagedResponse.currentPage) { + setCachedCurrentPage(props.pagedResponse.currentPage); + } + }, [props.pagedResponse]); + + return ( + + + + + + ); +}; + +export default PagedContentControls; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/components/payouts/EndOfEmploymentPayoutView.tsx b/LCU.WebApps.Operations.IncentiveManager/src/components/payouts/EndOfEmploymentPayoutView.tsx new file mode 100644 index 0000000..eff6af7 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/components/payouts/EndOfEmploymentPayoutView.tsx @@ -0,0 +1,67 @@ +import React from "react"; +import { IPayoutLineItem } from "../../shared/models/Payout"; +import PayoutLineItemTableRow from "./PayoutLineItemTableRow"; +import { Alert } from "reactstrap"; +import { IEmployeeSummary } from "../../shared/models/Employee"; +import moment from "moment"; + +export interface IPayoutDisplayProps { + eoeEmployees: IEmployeeSummary[]; +} + +const EndOfEmploymentPayoutView: React.FC = ( + props: IPayoutDisplayProps +) => { + if (!props.eoeEmployees || props.eoeEmployees.length === 0) { + return No employees found for period; + } + + let total = 0; + const rows: any[] = []; + props.eoeEmployees.forEach((employee, idx) => { + rows.push( + + +
+ {employee.fullName} ({employee.employeeId}) ended + employment on {moment(employee.terminated).format("ll")} +
+ + + ); + employee.associatedPayouts.forEach((lineItem: IPayoutLineItem) => { + total += lineItem.payoutTotal; + rows.push( + + ); + }); + }); + + return ( + + + + + + + + + + + + {rows} + + + + + + + +
NameProductTypeQuantityPercentageTotal
Total${total}.00
+ ); +}; + +export default EndOfEmploymentPayoutView; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/components/payouts/PayoutEntryTable.test.tsx b/LCU.WebApps.Operations.IncentiveManager/src/components/payouts/PayoutEntryTable.test.tsx new file mode 100644 index 0000000..056f37f --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/components/payouts/PayoutEntryTable.test.tsx @@ -0,0 +1,38 @@ +import { shallow } from "enzyme"; +import React from "react"; +import PayoutEntryTable from "./PayoutEntryTable"; +import { IPayoutEntry } from "../../shared/models/Payout"; + +describe("a table for showing payout line items", () => { + it("Displays a message when there is no data to load", () => { + let wrapper = shallow(); + expect(wrapper.find("tbody").length).toBe(1); + expect(wrapper.find("tr").length).toBe(1); + }); + + it("Does not display message when there is data", () => { + const lineItems: IPayoutEntry[] = [ + { + code: "Incentive", + employeeId: "C00002", + employeeName: "John Doe", + payoutTotal: 144 + }, + { + code: "Incentive", + employeeId: "C00003", + employeeName: "Jane Doe", + payoutTotal: 234 + }, + { + code: "Incentive", + employeeId: "C00004", + employeeName: "Jim Tests", + payoutTotal: 1433 + } + ]; + let wrapper = shallow(); + expect(wrapper.find("tbody").length).toBe(1); + expect(wrapper.find("tr").length).toBe(lineItems.length + 1); + }); +}); diff --git a/LCU.WebApps.Operations.IncentiveManager/src/components/payouts/PayoutEntryTable.tsx b/LCU.WebApps.Operations.IncentiveManager/src/components/payouts/PayoutEntryTable.tsx new file mode 100644 index 0000000..7d87602 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/components/payouts/PayoutEntryTable.tsx @@ -0,0 +1,41 @@ +import React from "react"; +import { IPayoutEntry } from "../../shared/models/Payout"; + +export interface IPayoutEntryTableProps { + payoutEntries: IPayoutEntry[]; +} + +const PayoutEntryTable: React.FC = ( + props: IPayoutEntryTableProps +) => { + const payouts = props.payoutEntries; + + return ( +
+ + + + + + + + + + + {payouts.map((lineItem: IPayoutEntry, index) => { + return ( + + + + + + + ); + })} + +
CodeFull NameEmployee IdTotal
{lineItem.code}{lineItem.employeeName}{lineItem.employeeId}${lineItem.payoutTotal}.00
+
+ ); +}; + +export default PayoutEntryTable; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/components/payouts/PayoutLineItemDetailRow.tsx b/LCU.WebApps.Operations.IncentiveManager/src/components/payouts/PayoutLineItemDetailRow.tsx new file mode 100644 index 0000000..67d7249 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/components/payouts/PayoutLineItemDetailRow.tsx @@ -0,0 +1,28 @@ +import React from "react"; +import { IPayoutLineItem } from "../../shared/models/Payout"; +import ActivitySummaryTable from "../activity/activity-table/ActivitySummaryTable"; + +export interface IPayoutDisplayProps { + lineItem: IPayoutLineItem; +} + +// const TabViewTranslate = Translate.Get.containers.payoutView; + +const PayoutLineItemDetailRow: React.FC = ( + props: IPayoutDisplayProps +) => { + const lineItem = props.lineItem; + return ( + + + x.activity) || [] + } + /> + + + ); +}; + +export default PayoutLineItemDetailRow; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/components/payouts/PayoutLineItemTableRow.test.tsx b/LCU.WebApps.Operations.IncentiveManager/src/components/payouts/PayoutLineItemTableRow.test.tsx new file mode 100644 index 0000000..c51460e --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/components/payouts/PayoutLineItemTableRow.test.tsx @@ -0,0 +1,26 @@ +import React from "react"; +import { shallow } from "enzyme"; +import { IPayoutLineItem } from "../../shared/models/Payout"; +import moment from "moment"; +import PayoutLineItemTableRow from "./PayoutLineItemTableRow"; + +describe("Show payout line items ", () => { + it("Displays a valid line item row", () => { + const payout: IPayoutLineItem = { + activityPeriod: moment(), + activityTotal: 0, + commissionId: 0, + commissionValue: 0, + departmentId: 0, + employeeId: "", + goalId: 0, + goalValue: 0, + payoutTotal: 0, + product: "", + slidingPayout: 0 + }; + const wrapper = shallow(); + + expect(wrapper.find("tr").length).toBe(1); + }); +}); diff --git a/LCU.WebApps.Operations.IncentiveManager/src/components/payouts/PayoutLineItemTableRow.tsx b/LCU.WebApps.Operations.IncentiveManager/src/components/payouts/PayoutLineItemTableRow.tsx new file mode 100644 index 0000000..fcd1f1f --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/components/payouts/PayoutLineItemTableRow.tsx @@ -0,0 +1,52 @@ +import React from "react"; +import { IPayoutLineItem } from "../../shared/models/Payout"; +import DepartmentDisplay from "../../containers/shared/DepartmentDisplay"; +import ProductDisplay from "../../containers/shared/ProductDisplay"; + +export interface IPayoutDisplayProps { + lineItem: IPayoutLineItem; +} + +const PayoutLineItemTableRow: React.FC = ( + props: IPayoutDisplayProps +) => { + const lineItem = props.lineItem; + + return lineItem.commissionId ? ( + + + + + + + + Commission + + ${lineItem.commissionValue}.00 x {lineItem.activityTotal} + + {lineItem.slidingPayout}% + ${lineItem.payoutTotal}.00 + + ) : ( + + + + + + + + Payout + + {lineItem.activityTotal}/{lineItem.goalValue} ( + {Math.floor( + ((lineItem.activityTotal / lineItem.goalValue) * 1000) / 10 + )} + %) + + {lineItem.slidingPayout}% + ${lineItem.payoutTotal}.00 + + ); +}; + +export default PayoutLineItemTableRow; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/components/payouts/PayoutProfileCard.scss b/LCU.WebApps.Operations.IncentiveManager/src/components/payouts/PayoutProfileCard.scss new file mode 100644 index 0000000..127da73 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/components/payouts/PayoutProfileCard.scss @@ -0,0 +1,4 @@ +.card { + margin: 1em; + max-width: 400px; +} diff --git a/LCU.WebApps.Operations.IncentiveManager/src/components/payouts/PayoutProfileCard.tsx b/LCU.WebApps.Operations.IncentiveManager/src/components/payouts/PayoutProfileCard.tsx new file mode 100644 index 0000000..0817963 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/components/payouts/PayoutProfileCard.tsx @@ -0,0 +1,104 @@ +import { IPayoutProfile, IPayoutRange } from "../../shared/models/Settings"; +import React from "react"; +import { Card, CardTitle, Table } from "reactstrap"; +import "./PayoutProfileCard.scss"; +import Translate from "../../translate/Translate"; +import Product2 from "../../shared/enumerations/Product"; +import moment from "moment"; + +export interface IPayoutProfileCardProps { + showRoles: boolean; + profile: IPayoutProfile; +} + +interface IPayoutProfileTableProps { + payEach: boolean; + product: Product2; + payoutRange: IPayoutRange[] | undefined; + effectiveDate: string; +} + +const payoutTranslate = Translate.Get.components.payouts; + +const defaultDate = "0001-01-01T00:00:00"; + +const PayoutProfileCard: React.FC = (props) => { + const roleNames = props.profile.roles.map((x) => x.name); + const roleText = roleNames.length > 1 ? roleNames.join(", ") : roleNames[0]; + const myMap = new Map( + Object.entries(props.profile.payouts) + ); + const payoutKeys = Object.keys(props.profile.payouts); + + return ( + + + + {props.profile.product} - {props.profile.type} - {roleText} + + + {payoutKeys.map((key, index) => { + return ( + + ); + })} + + ); +}; + +const DetermineEffectiveDateTitle = (effectiveDate: string) => { + if (effectiveDate === defaultDate) { + return "Standard"; + } + const effectiveMoment = moment(effectiveDate); + return `Override For ${effectiveMoment.format("MMM YYYY")}`; +}; + +const PayoutProfileTable: React.FC = (props) => { + const amtText = props.payEach ? payoutTranslate.each : ""; + + return ( +
+
{DetermineEffectiveDateTitle(props.effectiveDate)}
+ + + + + + + + + {props.payoutRange?.map((payout, mapIdx) => ( + + + + + ))} + +
{payoutTranslate.range}{payoutTranslate.pays}
+ {payout.minimumPercentage * 100 + "%"} + {payout.maximumPercentage === -1 ? "+" : " - "} + {payout.maximumPercentage === -1 + ? "" + : payout.maximumPercentage * 100 + "%"} + + ${payout.payout}.00{amtText} +
+
+ ); +}; + +export default PayoutProfileCard; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/components/payouts/PayoutView.test.tsx b/LCU.WebApps.Operations.IncentiveManager/src/components/payouts/PayoutView.test.tsx new file mode 100644 index 0000000..18de674 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/components/payouts/PayoutView.test.tsx @@ -0,0 +1,38 @@ +import React from "react"; +import { shallow } from "enzyme"; +import PayoutView from "./PayoutView"; +import { IPayoutLineItem } from "../../shared/models/Payout"; +import moment from "moment"; + +describe("Show All Payouts for an Employee and their departments during a time period.", () => { + it("Displays all departments", () => { + const payouts: IPayoutLineItem[] = []; + const wrapper = shallow(); + expect(wrapper.html()).toBe( + '' + ); + }); + + it("Displays all departments", () => { + const payouts: IPayoutLineItem[] = [ + { + activityPeriod: moment(), + activityTotal: 0, + commissionId: 0, + commissionValue: 0, + departmentId: 0, + employeeId: "", + goalId: 0, + goalValue: 0, + payoutTotal: 0, + product: "", + slidingPayout: 0 + } + ]; + const wrapper = shallow(); + expect( + wrapper.find("[data-test='department-payout-view-container']") + .length + ).toBe(1); + }); +}); diff --git a/LCU.WebApps.Operations.IncentiveManager/src/components/payouts/PayoutView.tsx b/LCU.WebApps.Operations.IncentiveManager/src/components/payouts/PayoutView.tsx new file mode 100644 index 0000000..be82949 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/components/payouts/PayoutView.tsx @@ -0,0 +1,66 @@ +import React from "react"; +import { IPayoutLineItem } from "../../shared/models/Payout"; +import PayoutLineItemTableRow from "./PayoutLineItemTableRow"; +import { Alert } from "reactstrap"; +import PayoutLineItemDetailRow from "./PayoutLineItemDetailRow"; + +export interface IPayoutDisplayProps { + userPayouts: IPayoutLineItem[]; + showDetails?: boolean; +} + +const PayoutView: React.FC = ( + props: IPayoutDisplayProps +) => { + if (!props.userPayouts || props.userPayouts.length === 0) { + return ( + + No payouts or commissions found for period. + + ); + } + + let total = 0; + const rows: any[] = []; + props.userPayouts.forEach((lineItem: IPayoutLineItem, idx) => { + total += lineItem.payoutTotal; + rows.push( + + ); + if (props.showDetails) { + rows.push( + + ); + } + }); + + return ( +
+ + + + + + + + + + + + {rows} + + + + + + + +
DepartmentProductTypeQuantityPercentageTotal
Total${total}.00
+
+ ); +}; + +export default PayoutView; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/components/permission/AdditionalPermissionAlertBar.tsx b/LCU.WebApps.Operations.IncentiveManager/src/components/permission/AdditionalPermissionAlertBar.tsx new file mode 100644 index 0000000..0b0d9c7 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/components/permission/AdditionalPermissionAlertBar.tsx @@ -0,0 +1,51 @@ +import React from "react"; +import { Alert, Spinner } from "reactstrap"; +import AsyncState from "./../../shared/enumerations/AsyncState"; +import Translate from "./../../translate/Translate"; + +export interface IAdditionalPermissionAlertBarProps { + submitState: AsyncState; +} + +const abTranslate = + Translate.Get.components.permissions.additionalPermissionsAlertBar; + +const AdditionalPermissionAlertBar: React.FC = ( + props +) => { + if (props.submitState === AsyncState.Success) { + return ( + +

{abTranslate.success}

+
+ ); + } + + if (props.submitState === AsyncState.Loading) { + return ( + + +

{abTranslate.loading}

+
+ ); + } + + if (props.submitState === AsyncState.Failure) { + return ( + + +

{abTranslate.failure}

+
+ ); + } + + return
; +}; + +export default AdditionalPermissionAlertBar; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/components/permission/AdditionalPermissionForm.test.tsx b/LCU.WebApps.Operations.IncentiveManager/src/components/permission/AdditionalPermissionForm.test.tsx new file mode 100644 index 0000000..3d8c033 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/components/permission/AdditionalPermissionForm.test.tsx @@ -0,0 +1,253 @@ +import React from "react"; +import { mount } from "enzyme"; +import AdditionalPermissionsForm from "./AdditionalPermissionForms"; +import AsyncState from "./../../shared/enumerations/AsyncState"; +import { IPermission } from "../../shared/models/Settings"; + +let wrapper = mount(

); +let submitMock = jest.fn(); + +const PermA = { + code: "PA", + description: "PermissionA" +}; +const PermB = { + code: "PB", + description: "PermissionB" +}; +const PermC = { + code: "PC", + description: "PermissionC" +}; +const PermX = { + code: "PX", + description: "PermissionX" +}; +const PermY = { + code: "PY", + description: "PermissionY" +}; + +const allPermissions: IPermission[] = [PermA, PermB, PermC, PermX, PermY]; + +//These permissions must exist in all permissions, but not in common with eachother +const additionalPermissions: IPermission[] = [PermX]; +const rolePermissions: IPermission[] = [PermA, PermB]; + +beforeEach(() => { + wrapper = mount( + { + submitMock(assign, unassign); + }} + submitState={AsyncState.Inactive} + /> + ); +}); + +describe("User views the initial AdditionalPermissionForm", () => { + it("does not populate either badge groups with permissions from role", () => { + const available = wrapper.find('[data-test="available-badge-list"]'); + available.prop("existingItems").forEach((perm) => { + expect(perm).not.toBe(rolePermissions); + }); + + const additional = wrapper.find('[data-test="additional-badge-list"]'); + additional.prop("existingItems").forEach((perm) => { + expect(perm).not.toBe(rolePermissions); + }); + }); + + it("populates Available permissions with the correct permissions", () => { + const available = wrapper.find('[data-test="available-badge-list"]'); + //All perms not in role and not already assigned + expect(available.prop("existingItems").length).toBe(2); + expect(available.prop("existingItems")).toContain(PermY); + expect(available.prop("existingItems")).toContain(PermC); + expect(available.prop("existingItems")).not.toContain(PermX); + }); + + it("populates Additional permissions with the correct permissions", () => { + const additional = wrapper.find('[data-test="additional-badge-list"]'); + //All assigned additional perms + expect(additional.prop("existingItems").length).toBe(1); + expect(additional.prop("existingItems")).toContain(PermX); + }); + + it("disables submit until state would create a change", () => { + expect(wrapper.find("button").prop("disabled")).toBe(true); + wrapper.find('[data-test="available-badge-list"]').invoke("onClick")( + PermY + ); + expect(wrapper.find("button").prop("disabled")).toBe(false); + }); + + it("clears state after sucessful submit", () => { + expect(wrapper.find("button").prop("disabled")).toBe(true); + wrapper.find('[data-test="available-badge-list"]').invoke("onClick")( + PermY + ); + wrapper.find('[data-test="additional-badge-list"]').invoke("onClick")( + PermX + ); + expect( + wrapper + .find('[data-test="additional-badge-list"]') + .prop("modifications") + ).toContain(PermY); + expect( + wrapper + .find('[data-test="available-badge-list"]') + .prop("modifications") + ).toContain(PermX); + expect(wrapper.find("button").prop("disabled")).toBe(false); + wrapper.setProps({ submitState: AsyncState.Success }); + wrapper.update(); + expect( + wrapper + .find('[data-test="additional-badge-list"]') + .prop("modifications").length + ).toBe(0); + expect( + wrapper + .find('[data-test="available-badge-list"]') + .prop("modifications").length + ).toBe(0); + }); +}); + +describe("Assigning a role displays properly", () => { + //USE PermY AS IT IS AVAILABLE AND NOT ADDITIONALLY APPLIED + test("Newly assigned roles do not appear in Available Permissions", () => { + wrapper.find('[data-test="available-badge-list"]').invoke("onClick")( + PermY + ); + expect( + wrapper + .find('[data-test="available-badge-list"]') + .prop("existingItems") + ).not.toContain(PermY); + }); + + test("Newly assigned roles appear as a modification to Additional Permissions", () => { + wrapper.find('[data-test="available-badge-list"]').invoke("onClick")( + PermY + ); + expect( + wrapper + .find('[data-test="additional-badge-list"]') + .prop("modifications") + ).toContain(PermY); + }); + + test("Reassigning a role after unassigning appears as a existing additional permission", () => { + wrapper.find('[data-test="available-badge-list"]').invoke("onClick")( + PermY + ); + expect( + wrapper + .find('[data-test="available-badge-list"]') + .prop("existingItems") + ).not.toContain(PermY); + expect( + wrapper + .find('[data-test="additional-badge-list"]') + .prop("modifications") + ).toContain(PermY); + wrapper.find('[data-test="additional-badge-list"]').invoke("onClick")( + PermY + ); + + expect( + wrapper + .find('[data-test="additional-badge-list"]') + .prop("modifications") + ).not.toContain(PermY); + expect( + wrapper + .find('[data-test="available-badge-list"]') + .prop("modifications") + ).not.toContain(PermY); + expect( + wrapper + .find('[data-test="available-badge-list"]') + .prop("existingItems") + ).toContain(PermY); + }); +}); +describe("UnAssigning a role displays properly", () => { + //USE PermX AS IT IS ALREADY ADDITIONALLY APPLIED + test("Newly unassigned roles do not appear in Additional Permissions", () => { + wrapper.find('[data-test="additional-badge-list"]').invoke("onClick")( + PermX + ); + expect( + wrapper + .find('[data-test="additional-badge-list"]') + .prop("existingItems") + ).not.toContain(PermX); + }); + test("Newly unassigned roles appear as modificationtestto Available Permissions", () => { + wrapper.find('[data-test="additional-badge-list"]').invoke("onClick")( + PermX + ); + expect( + wrapper + .find('[data-test="available-badge-list"]') + .prop("modifications") + ).toContain(PermX); + }); + test("re-unassigning a role after assigning appears as a existing available permission", () => { + wrapper.find('[data-test="additional-badge-list"]').invoke("onClick")( + PermX + ); + expect( + wrapper + .find('[data-test="additional-badge-list"]') + .prop("existingItems") + ).not.toContain(PermX); + expect( + wrapper + .find('[data-test="available-badge-list"]') + .prop("modifications") + ).toContain(PermX); + wrapper.find('[data-test="available-badge-list"]').invoke("onClick")( + PermX + ); + + expect( + wrapper + .find('[data-test="available-badge-list"]') + .prop("modifications") + ).not.toContain(PermX); + expect( + wrapper + .find('[data-test="additional-badge-list"]') + .prop("modifications") + ).not.toContain(PermX); + expect( + wrapper + .find('[data-test="additional-badge-list"]') + .prop("existingItems") + ).toContain(PermX); + }); +}); + +describe("User submits changes", () => { + it("Submits Correct Data", () => { + wrapper.find('[data-test="additional-badge-list"]').invoke("onClick")( + PermX + ); + wrapper.find('[data-test="available-badge-list"]').invoke("onClick")( + PermY + ); + wrapper.find('[data-test="available-badge-list"]').invoke("onClick")( + PermC + ); + wrapper.find("button").simulate("click"); + expect(submitMock).toBeCalledWith([PermY, PermC], [PermX]); + }); +}); diff --git a/LCU.WebApps.Operations.IncentiveManager/src/components/permission/AdditionalPermissionForms.tsx b/LCU.WebApps.Operations.IncentiveManager/src/components/permission/AdditionalPermissionForms.tsx new file mode 100644 index 0000000..f49b6aa --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/components/permission/AdditionalPermissionForms.tsx @@ -0,0 +1,130 @@ +import React, { useEffect } from "react"; +import { Button, Col, Row } from "reactstrap"; +import { IPermission } from "../../shared/models/Settings"; +import { useState } from "react"; +import PermissionsFormBadgeList from "./PermissionFormBadgeList"; +import AsyncState from "../../shared/enumerations/AsyncState"; +import Translate from "../../translate/Translate"; + +interface IAdditionalPermissionsFormProps { + allPermissions: IPermission[]; + rolePermissions: IPermission[]; + additionalPermissions: IPermission[]; + submitChanges: ( + permsToAssign: IPermission[], + permsToUnassign: IPermission[] + ) => void; + submitState: AsyncState; +} + +const apfTranslate = + Translate.Get.components.permissions.additionalPermissionsForm; + +const AdditionalPermissionsForm: React.FC = ( + props +) => { + const [permsToAssign, updatePermsToAssign] = useState([]); + const [permsToUnassign, updatePermsToUnassign] = useState( + [] + ); + + useEffect(() => { + if (props.submitState === AsyncState.Success) { + updatePermsToAssign([]); + updatePermsToUnassign([]); + } + }, [props.submitState]); + + //get permissions that aren't part of role, and are not newly assigned + const getExistingUnassignedPermissions = () => { + return props.allPermissions.filter( + (x) => + !props.rolePermissions.some((rp) => rp.code === x.code) && + !permsToAssign.some((rp) => rp.code === x.code) && + !props.additionalPermissions.some((rp) => rp.code === x.code) + ); + }; + + //get permissions that are not newly unassigned + const getExistingAssignedPermissions = () => { + return props.additionalPermissions.filter( + (x) => !permsToUnassign.some((rp) => rp.code === x.code) + ); + }; + + const onAddPerm = (perm: IPermission) => { + //Remove this permission from the unassign list + updatePermsToUnassign( + permsToUnassign.filter((ptu) => ptu.code != perm.code) + ); + + //Only assign if it is not already included in existing additional perms + if (!props.additionalPermissions.some((ap) => ap.code === perm.code)) { + updatePermsToAssign([ + ...permsToAssign.filter((pta) => pta.code !== perm.code), + perm + ]); + } + }; + + const onRemovePerm = (perm: IPermission) => { + //Remove the permission from the assign list + updatePermsToAssign( + permsToAssign.filter((pta) => pta.code != perm.code) + ); + //Only unassign if it is already included in existing additional perms + if (props.additionalPermissions.some((ap) => ap.code === perm.code)) { + updatePermsToUnassign([ + ...permsToUnassign.filter((ptu) => ptu.code !== perm.code), + perm + ]); + } + }; + + return ( +
+ + +
+ {apfTranslate.availablePermissionsTitle} + onAddPerm(perm)} + data-test="available-badge-list" + /> +
+ + +
+ {apfTranslate.additionalPermissionsTitle} + onRemovePerm(perm)} + data-test="additional-badge-list" + /> +
+ +
+ +
+ +
+
+ ); +}; + +export default AdditionalPermissionsForm; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/components/permission/AdditionalPermissionsDisplay.tsx b/LCU.WebApps.Operations.IncentiveManager/src/components/permission/AdditionalPermissionsDisplay.tsx new file mode 100644 index 0000000..8110396 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/components/permission/AdditionalPermissionsDisplay.tsx @@ -0,0 +1,37 @@ +import moment from "moment"; +import React from "react"; +import { IUserPermission } from "../../shared/models/Employee"; +import Translate from "../../translate/Translate"; + +export interface IAdditionalPermissionDisplayProps { + additionalPermissions: IUserPermission[]; +} + +const apdTranslate = + Translate.Get.components.permissions.additionalPermissionsDisplay; + +const AdditionalPermissionDisplay: React.FC = ( + props +) => { + if (!props.additionalPermissions.length) { + return {apdTranslate.noPermissions}; + } + + return ( + <> + {props.additionalPermissions.map((ap, index) => ( + <> + {ap.permission.description} -{" "} + + {apdTranslate.granted} {ap.createdBy}{" "} + {apdTranslate.dateDivider}{" "} + {moment(ap.created).format("MM-DD-YYYY")} + + {index !== props.additionalPermissions.length - 1 &&
} + + ))} + + ); +}; + +export default AdditionalPermissionDisplay; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/components/permission/Permission.scss b/LCU.WebApps.Operations.IncentiveManager/src/components/permission/Permission.scss new file mode 100644 index 0000000..2758f1c --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/components/permission/Permission.scss @@ -0,0 +1,3 @@ +.pointer { + cursor: pointer; +} diff --git a/LCU.WebApps.Operations.IncentiveManager/src/components/permission/PermissionDisplay.test.tsx b/LCU.WebApps.Operations.IncentiveManager/src/components/permission/PermissionDisplay.test.tsx new file mode 100644 index 0000000..0a8e793 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/components/permission/PermissionDisplay.test.tsx @@ -0,0 +1,34 @@ +import { shallow } from "enzyme"; +import React from "react"; +import { IPermission } from "../../shared/models/Settings"; +import PermissionDisplay from "./PermissionDisplay"; + +describe("Permission Display component", () => { + it("Displays permissions codes with
between each", () => { + const mockPerms: IPermission[] = [ + { + code: "PERM_ONE", + description: "Do Stuff" + }, + { + code: "PERM_TWO", + description: "Do Other" + } + ]; + + const wrapper = shallow( + + ); + + expect(wrapper.html()).toBe( + "Do Stuff
Do Other
" + ); + }); + + it("Displays No Permissions if permissions props is empty", () => { + const wrapper = shallow( + + ); + expect(wrapper.html()).toBe("No Permissions
"); + }); +}); diff --git a/LCU.WebApps.Operations.IncentiveManager/src/components/permission/PermissionDisplay.tsx b/LCU.WebApps.Operations.IncentiveManager/src/components/permission/PermissionDisplay.tsx new file mode 100644 index 0000000..9bd586b --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/components/permission/PermissionDisplay.tsx @@ -0,0 +1,31 @@ +import React from "react"; +import { IPermission } from "../../shared/models/Settings"; + +export interface IPermissionDisplayProps { + permissions: IPermission[]; +} + +const PermissionDisplay: React.FC = (props) => { + if (!props.permissions.length) { + return ( + <> + No Permissions +
+ + ); + } + + return ( + <> + {props.permissions.map((permission, index) => ( + + {permission.description} + {index !== props.permissions.length - 1 &&
} +
+ ))} +
+ + ); +}; + +export default PermissionDisplay; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/components/permission/PermissionFormBadgeList.tsx b/LCU.WebApps.Operations.IncentiveManager/src/components/permission/PermissionFormBadgeList.tsx new file mode 100644 index 0000000..87e2e0e --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/components/permission/PermissionFormBadgeList.tsx @@ -0,0 +1,74 @@ +import "./Permission.scss"; +import React from "react"; +import { Badge } from "reactstrap"; +import { IPermission } from "../../shared/models/Settings"; + +export interface IPermissionFormBadgeListProps { + existingItems: IPermission[]; + modifications: IPermission[]; + modificationColor: "success" | "danger" | "warn" | "primary"; + caretDirection?: "right" | "left"; + onClick: (perm: IPermission) => void; +} + +const PermissionFormBadgeList: React.FC = ( + props +) => { + const formatDescription = (description: string) => { + if (props.caretDirection === "right") { + return ( +

+ {description} {" "} +

+ ); + } + if (props.caretDirection === "left") { + return ( +

+ {description} +

+ ); + } + return

{description}

; + }; + + return ( +
+ {props.existingItems.map((perm) => { + return ( + { + props.onClick(perm); + }} + color="primary" + > + {formatDescription(perm.description)} + + ); + })} + {props.modifications.map((perm) => { + return ( + { + props.onClick(perm); + }} + > + {formatDescription(perm.description)} + + ); + })} +
+ ); +}; + +export default PermissionFormBadgeList; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/components/permission/PermissionsFormBadgeList.test.tsx b/LCU.WebApps.Operations.IncentiveManager/src/components/permission/PermissionsFormBadgeList.test.tsx new file mode 100644 index 0000000..e93610e --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/components/permission/PermissionsFormBadgeList.test.tsx @@ -0,0 +1,104 @@ +import React from "react"; +import PermissionFormBadgeList from "./PermissionFormBadgeList"; +import { IPermission } from "./../../shared/models/Settings"; +import { shallow } from "enzyme"; +import { ShallowWrapper } from "enzyme"; + +let existingItems: IPermission[] = [ + { description: "PermissionA", code: "PA" }, + { description: "PermissionB", code: "PB" }, + { description: "PermissionC", code: "PC" }, + { description: "PermissionD", code: "PD" } +]; + +let modifiedItems: IPermission[] = [ + { description: "PermissionX", code: "PX" }, + { description: "PermissionY", code: "PY" } +]; + +let wrapper: ShallowWrapper; + +describe("A appUser views PermissionFormBadgeList", () => { + beforeEach(() => { + wrapper = shallow( + + ); + }); + + it("displays badges properly", () => { + expect(wrapper.find(`[data-test="existing-badge"]`).length).toBe(4); + expect( + wrapper + .find(`[data-test="existing-badge"]`) + .findWhere((x) => x.text().includes("PermissionA")) + .exists() + ).toBe(true); + expect( + wrapper + .find(`[data-test="existing-badge"]`) + .findWhere((x) => x.text().includes("PermissionB")) + .exists() + ).toBe(true); + expect( + wrapper + .find(`[data-test="existing-badge"]`) + .findWhere((x) => x.text().includes("PermissionC")) + .exists() + ).toBe(true); + expect( + wrapper + .find(`[data-test="existing-badge"]`) + .findWhere((x) => x.text().includes("PermissionD")) + .exists() + ).toBe(true); + expect(wrapper.find(`[data-test="modified-badge"]`).length).toBe(2); + expect( + wrapper + .find(`[data-test="modified-badge"]`) + .findWhere((x) => x.text().includes("PermissionX")) + .exists() + ).toBe(true); + expect( + wrapper + .find(`[data-test="modified-badge"]`) + .findWhere((x) => x.text().includes("PermissionY")) + .exists() + ).toBe(true); + }); + it("displays modifications in the proper color", () => { + wrapper.find(`[data-test="modified-badge"]`).forEach((badge) => { + expect(badge.prop("color")).toBe("danger"); + }); + wrapper.setProps({ modificationColor: "warn" }); + wrapper.update(); + wrapper.find(`[data-test="modified-badge"]`).forEach((badge) => { + expect(badge.prop("color")).toBe("warn"); + }); + wrapper.setProps({ modificationColor: "success" }); + wrapper.update(); + wrapper.find(`[data-test="modified-badge"]`).forEach((badge) => { + expect(badge.prop("color")).toBe("success"); + }); + wrapper.setProps({ modificationColor: "primary" }); + wrapper.update(); + wrapper.find(`[data-test="modified-badge"]`).forEach((badge) => { + expect(badge.prop("color")).toBe("primary"); + }); + }); + it("displays the proper carots", () => { + expect(wrapper.find("span").length).toBe(0); + wrapper.setProps({ caretDirection: "left" }); + wrapper.update(); + expect(wrapper.find(`[data-test="caret-left-desc"]`).length).toBe(6); + expect(wrapper.find(`[data-test="caret-right-desc"]`).length).toBe(0); + wrapper.setProps({ caretDirection: "right" }); + wrapper.update(); + expect(wrapper.find(`[data-test="caret-right-desc"]`).length).toBe(6); + expect(wrapper.find(`[data-test="caret-left-desc"]`).length).toBe(0); + }); +}); diff --git a/LCU.WebApps.Operations.IncentiveManager/src/components/permission/PermissionsPage.tsx b/LCU.WebApps.Operations.IncentiveManager/src/components/permission/PermissionsPage.tsx new file mode 100644 index 0000000..2d7760b --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/components/permission/PermissionsPage.tsx @@ -0,0 +1,85 @@ +import React, { useState } from "react"; +import { IPermission } from "../../shared/models/Settings"; +import PermissionDisplay from "./PermissionDisplay"; +import { Col, Row } from "reactstrap"; +import AdditionalPermissionsForm from "./AdditionalPermissionForms"; +import AdditionalPermissionsDisplay from "./AdditionalPermissionsDisplay"; +import AsyncState from "./../../shared/enumerations/AsyncState"; +import { useEffect } from "react"; +import EmployeeService from "../../shared/services/EmployeeService"; +import AdditionalPermissionsAlertBar from "./AdditionalPermissionAlertBar"; +import { IUserPermission } from "../../shared/models/Employee"; +import Translate from "./../../translate/Translate"; + +export interface IPermissionsPageProps { + additionalPermissions: IUserPermission[]; + rolePermissions: IPermission[]; + allPermissions: IPermission[]; + refreshProfile: () => void; + employeeId: string; +} + +const permTranslate = Translate.Get.components.permissions.permissionPage; + +const PermissionsPage: React.FC = (props) => { + const [submitState, setSubmitState] = useState( + AsyncState.Inactive + ); + + const submitForm = async ( + permCodesToAdd: IPermission[], + permCodesToRemove: IPermission[] + ) => { + setSubmitState(AsyncState.Loading); + const res = await EmployeeService.updatePermissions( + permCodesToAdd.map((x) => x.code), + permCodesToRemove.map((x) => x.code), + props.employeeId + ); + setSubmitState(res.state); + }; + + useEffect(() => { + if (submitState === AsyncState.Success) { + props.refreshProfile(); + } + }, [submitState]); + + return ( +
+ + +
{permTranslate.defaultTitle}
+ + + +
{permTranslate.additionalTitle}
+ + +
+ +
+
{permTranslate.modifyTitle}
+ + x.permission + )} + rolePermissions={props.rolePermissions} + allPermissions={props.allPermissions.filter( + (x) => x.code !== "UNKNOWN" + )} + submitChanges={(permsToAssign, permsToUnassign) => { + submitForm(permsToAssign, permsToUnassign); + }} + submitState={submitState} + /> +
+
+
+ ); +}; + +export default PermissionsPage; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/components/profile/ProfileView.test.tsx b/LCU.WebApps.Operations.IncentiveManager/src/components/profile/ProfileView.test.tsx new file mode 100644 index 0000000..78f75b2 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/components/profile/ProfileView.test.tsx @@ -0,0 +1,68 @@ +import { shallow } from "enzyme"; +import React from "react"; +import { IPermission } from "../../shared/models/Settings"; +import ProfileView from "./ProfileView"; +import { IEmployee } from "../../shared/models/Employee"; +import moment from "moment"; +import { UserType } from "../../shared/models/Auditable"; + +describe("Profile Display component", () => { + it("Display a basic profile", () => { + const mockPerms: IPermission[] = [ + { + code: "PERM_ONE", + description: "Do Stuff" + }, + { + code: "PERM_TWO", + description: "Do Other" + } + ]; + const mockProfile: IEmployee = { + adEmail: "johntester@test.com", + baseRole: "Test Role", + departments: [ + { + departmentId: 55, + employeeId: "11771", + isHome: true, + note: "fake home assignment", + startDate: moment("2021-09-29T10:00:00Z"), + endDate: moment("2021-09-29T10:00:00Z"), + created: moment("2021-09-29T10:00:00Z"), + createdBy: "nobody", + createdByType: UserType.System, + lastUpdated: moment("2021-09-29T10:00:00Z"), + id: 5, + lastUpdatedBy: "nobody", + lastUpdatedByType: UserType.System, + role: { + name: "role", + description: "role", + permissions: mockPerms, + hasCommissions: true, + rank: 1 + }, + slidingPayout: 100 + } + ], + employeeId: "12345", + fullName: "John Tester", + homeDepartment: "", + personId: 0, + role: { + name: "myrole", + permissions: [], + description: "My Role", + hasCommissions: true, + rank: 1 + }, + additionalPermissions: [], + assignments: [] + }; + + const wrapper = shallow(); + + expect(wrapper.find("[data-test='profile-page-table']").length).toBe(1); + }); +}); diff --git a/LCU.WebApps.Operations.IncentiveManager/src/components/profile/ProfileView.tsx b/LCU.WebApps.Operations.IncentiveManager/src/components/profile/ProfileView.tsx new file mode 100644 index 0000000..59097e0 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/components/profile/ProfileView.tsx @@ -0,0 +1,87 @@ +import React from "react"; +import PermissionDisplay from "../permission/PermissionDisplay"; +import Translate from "../../translate/Translate"; +import { IEmployee } from "../../shared/models/Employee"; +import EmployeeDepartmentAssignments from "../employee/EmployeeDepartmentAssignments"; +import DepartmentDisplay from "../../containers/shared/DepartmentDisplay"; + +interface IProfileProps { + profile: IEmployee; +} + +const profileTranslate = Translate.Get.components.profile; + +const ProfileView: React.FC = (props: IProfileProps) => { + const profile = props.profile; + const additionalPermissions = + profile?.additionalPermissions?.map((x) => x.permission) ?? []; + + return ( +
+ + + + + + + + + + + + + + + + + + + + + + +
{profileTranslate.employeeId} + {profile.employeeId} ({profile.adEmail}){" "} +
{profileTranslate.dayforceRole} + {profile.homeDepartment} - {profile.baseRole} +
+ {" "} +
{profileTranslate.permissions} + {profile.departments.map((department, index) => { + return ( + + Granted By{" "} + +
+ +
+ ); + })} +
+ {"Additional"}
{profileTranslate.permissions} +
+ +
+
+
+ ); +}; + +export default ProfileView; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/components/region/RegionTable.test.tsx b/LCU.WebApps.Operations.IncentiveManager/src/components/region/RegionTable.test.tsx new file mode 100644 index 0000000..8733277 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/components/region/RegionTable.test.tsx @@ -0,0 +1,183 @@ +import React from "react"; +import { mount } from "enzyme"; +import * as reactRedux from "react-redux"; +import moment from "moment"; +import AsyncState from "../../shared/enumerations/AsyncState"; +import { UserType } from "../../shared/models/Auditable"; +import RegionTable from "./RegionTable"; +import PermissionCode from "../../shared/enumerations/PermissionCode"; +import { IEmployee } from "../../shared/models/Employee"; + +const useSelectorMock = jest.spyOn(reactRedux, "useSelector"); + +beforeEach(() => { + useSelectorMock.mockClear(); +}); + +describe("table to display and edit regions", () => { + it("should not display extra header column if appUser has no permission to assign", () => { + const profileWithoutPermission = profile(); + useSelectorMock.mockReturnValue(profileWithoutPermission); + + const wrapper = mount( + jest.fn()} + /> + ); + expect(wrapper.find("th").length).toBe(4); + }); + + it("should display extra header column if appUser has permission to assign", () => { + const profileWithPermission = profile(); + profileWithPermission.profile.data.additionalPermissions[0].permission = { + code: PermissionCode.ASSIGN_REGIONAL_MANAGER, + description: "" + }; + useSelectorMock.mockReturnValue(profileWithPermission); + + const wrapper = mount( + jest.fn()} + /> + ); + + expect(wrapper.find("th").length).toBe(5); + }); + + it("should map regions to table rows", () => { + const profileWithPermission = profile(); + profileWithPermission.profile.data.additionalPermissions[0].permission = { + code: PermissionCode.ASSIGN_REGIONAL_MANAGER, + description: "" + }; + useSelectorMock.mockReturnValue(profileWithPermission); + + const wrapper = mount( + jest.fn()} + /> + ); + + const tableRows = wrapper.find("RegionTableRow"); + expect(tableRows.length).toBe(2); + expect(tableRows.at(0).prop("region")["code"]).toBe("NORTH"); + expect(tableRows.at(1).prop("region")["code"]).toBe("SOUTH"); + }); + + it("should pass the region the manager that is assigned", () => { + const profileWithPermission = profile(); + profileWithPermission.profile.data.additionalPermissions[0].permission = { + code: PermissionCode.ASSIGN_REGIONAL_MANAGER, + description: "" + }; + useSelectorMock.mockReturnValue(profileWithPermission); + + const wrapper = mount( + jest.fn()} + /> + ); + + const rows = wrapper.find("RegionTableRow"); + expect(rows.at(0).prop("manager")).toBeUndefined(); + expect(rows.at(1).prop("manager")["employeeId"]).toBe("123"); + }); +}); + +const profile = () => { + return { + profile: { + data: { + adEmail: "test@test.com", + personId: 11, + employeeId: "11771", + homeDepartment: "HOME", + baseRole: "QA", + fullName: "John Tester", + role: { + name: "Admin", + description: "admin", + permissions: [], + hasCommissions: false + }, + additionalPermissions: [ + { + id: 1, + created: moment().utc(), + createdByType: UserType.System, + createdBy: "Tester1", + permission: { + code: "VIEW_DASHBOARD", + description: "View Dashboard" + } + } + ], + departments: [], + userAssignments: [] + }, + state: AsyncState.Success + }, + searchResults: { + state: AsyncState.Success, + data: [] + } + }; +}; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/components/region/RegionTable.tsx b/LCU.WebApps.Operations.IncentiveManager/src/components/region/RegionTable.tsx new file mode 100644 index 0000000..aeb7fa8 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/components/region/RegionTable.tsx @@ -0,0 +1,61 @@ +import React from "react"; +import { IRegion } from "../../shared/models/Settings"; +import { IUserAssignment } from "../../shared/models/UserAssignment"; +import Translate from "../../translate/Translate"; +import RegionTableRow from "./RegionTableRow"; +import PermissionGuard from "../../containers/shared/PermissionGuard"; +import PermissionCode from "../../shared/enumerations/PermissionCode"; +import IAsyncStateContainer from "../../shared/types/IAsyncStateContainer"; +import { IEmployeeSummary } from "../../shared/models/Employee"; + +export interface RegionTableProps { + regions: IRegion[]; + assignedManagers: IUserAssignment[]; + availableManagers: IEmployeeSummary[]; + submitState: IAsyncStateContainer; + updateManager: (employeeId: string, regionCode: string) => void; +} + +const regionTableTranslate = Translate.Get.containers.regionTable; + +const RegionTable: React.FC = (props) => { + return ( +
+
{regionTableTranslate.title}
+ + + + + + + + + + + + + + {props.regions.map((region) => ( + manager.key == region.code + )?.appUser + } + managers={props.availableManagers} + updateManager={props.updateManager} + /> + ))} + +
{regionTableTranslate.headers.code}{regionTableTranslate.headers.name}{regionTableTranslate.headers.businessLineCode}{regionTableTranslate.headers.manager}
+
+ ); +}; + +export default RegionTable; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/components/region/RegionTableRow.test.tsx b/LCU.WebApps.Operations.IncentiveManager/src/components/region/RegionTableRow.test.tsx new file mode 100644 index 0000000..aa630af --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/components/region/RegionTableRow.test.tsx @@ -0,0 +1,194 @@ +import React from "react"; +import * as enzyme from "enzyme"; +import RegionTableRow from "./RegionTableRow"; +import * as reactRedux from "react-redux"; +import AsyncState from "../../shared/enumerations/AsyncState"; +import moment from "moment"; +import { UserType } from "../../shared/models/Auditable"; +import PermissionCode from "../../shared/enumerations/PermissionCode"; +import { IEmployee } from "../../shared/models/Employee"; + +const useSelectorMock = jest.spyOn(reactRedux, "useSelector"); + +beforeEach(() => { + useSelectorMock.mockClear(); +}); + +describe("Row in region table", () => { + it("should just display values if appUser has no permission to assign", () => { + const profileWithoutPermission = profile(); + useSelectorMock.mockReturnValue(profileWithoutPermission); + const wrapper = enzyme.mount( + + + jest.fn()} + manager={ + { + fullName: "Manager Doe", + employeeId: "1" + } as IEmployee + } + /> + +
+ ); + + const cells = wrapper.find("td"); + expect(cells.length).toBe(4); + expect(cells.at(0).text()).toBe("NORTH"); + expect(cells.at(1).text()).toBe("North"); + expect(cells.at(2).text()).toBe("BCHOPS"); + expect(cells.at(3).text()).toBe("Manager Doe"); + }); + + it("should display edit option if appUser has permission to assign", () => { + const profileWithPermission = profile(); + profileWithPermission.profile.data.additionalPermissions[0].permission = { + code: PermissionCode.ASSIGN_REGIONAL_MANAGER, + description: "" + }; + useSelectorMock.mockReturnValue(profileWithPermission); + const wrapper = enzyme.mount( + + + jest.fn()} + manager={ + { + fullName: "Manager Doe", + employeeId: "1" + } as IEmployee + } + /> + +
+ ); + + const cells = wrapper.find("td"); + expect(cells.length).toBe(5); + + expect(wrapper.find("[data-test='edit-button'] button").length).toBe(1); + wrapper.find("[data-test='edit-button'] button").simulate("click"); + wrapper.update(); + + const submitButton = wrapper.find("[data-test='submit-button'] button"); + const clearButton = wrapper.find("[data-test='clear-button'] button"); + expect(submitButton.length).toBe(1); + expect(clearButton.length).toBe(1); + }); + + it("should call update manager on save", () => { + const profileWithPermission = profile(); + profileWithPermission.profile.data.additionalPermissions[0].permission = { + code: PermissionCode.ASSIGN_REGIONAL_MANAGER, + description: "" + }; + useSelectorMock.mockReturnValue(profileWithPermission); + const updateManagerMock = jest.fn(); + const wrapper = enzyme.mount( + + + + +
+ ); + + // click edit + expect(wrapper.find("[data-test='edit-button'] button").length).toBe(1); + wrapper.find("[data-test='edit-button'] button").simulate("click"); + wrapper.update(); + + // select new manager + wrapper + .find("[data-test='regional-manager-input'] select") + .simulate("change", { target: { value: "123" } }); + wrapper.update(); + + // click save + wrapper.find("[data-test='submit-button'] button").simulate("click"); + wrapper.update(); + + expect(updateManagerMock).toBeCalledTimes(1); + expect(updateManagerMock).toHaveBeenCalledWith("123", "NORTH"); + }); +}); + +const profile = () => { + return { + profile: { + data: { + adEmail: "test@test.com", + personId: 11, + employeeId: "11771", + homeDepartment: "HOME", + baseRole: "QA", + fullName: "John Tester", + role: { + name: "Admin", + description: "admin", + permissions: [], + hasCommissions: false + }, + additionalPermissions: [ + { + id: 1, + created: moment().utc(), + createdByType: UserType.System, + createdBy: "Tester1", + permission: { + code: "VIEW_DASHBOARD", + description: "View Dashboard" + } + } + ], + departments: [], + userAssignments: [] + }, + state: AsyncState.Success + }, + searchResults: { + state: AsyncState.Success, + data: [] + } + }; +}; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/components/region/RegionTableRow.tsx b/LCU.WebApps.Operations.IncentiveManager/src/components/region/RegionTableRow.tsx new file mode 100644 index 0000000..a57600e --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/components/region/RegionTableRow.tsx @@ -0,0 +1,127 @@ +import React, { useState } from "react"; +import { Button, ButtonGroup, Input } from "reactstrap"; +import PermissionGuard from "../../containers/shared/PermissionGuard"; +import PermissionCode from "../../shared/enumerations/PermissionCode"; +import { IRegion } from "../../shared/models/Settings"; +import Translate from "../../translate/Translate"; +import { IEmployee, IEmployeeSummary } from "../../shared/models/Employee"; + +export interface IRegionTableRowProps { + region: IRegion; + manager?: IEmployee; + managers: IEmployeeSummary[]; + updateManager: (employeeId: string, regionCode: string) => void; +} + +const tableRowTranslate = Translate.Get.containers.regionTable.row; + +const RegionTableRow: React.FC = (props) => { + const initialManagerEmployeeId = + props.managers.find( + (manager) => manager.employeeId === props.manager?.employeeId + )?.employeeId ?? ""; + + const [isEditing, setEditing] = useState(false); + const [ + selectedManagerEmployeeId, + setSelectedManagerEmployeeId + ] = useState(initialManagerEmployeeId); + + const isSaveDisabled = + selectedManagerEmployeeId === "" || + selectedManagerEmployeeId === props.manager?.employeeId; + + const editButton = ( + + + + ); + + const saveButtonGroup = ( + + + + + + + ); + + return ( + + {props.region.code} + {props.region.name} + {props.region.businessLineCode} + + {isEditing ? ( + + setSelectedManagerEmployeeId(event.target.value) + } + > + + {props.managers && + props.managers.map((profile) => ( + + ))} + + ) : ( + props.manager?.fullName + )} + + + {isEditing ? saveButtonGroup : editButton} + + + ); +}; + +export default RegionTableRow; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/components/region/UnassignedRegionalManagerAlert.test.tsx b/LCU.WebApps.Operations.IncentiveManager/src/components/region/UnassignedRegionalManagerAlert.test.tsx new file mode 100644 index 0000000..c681d4f --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/components/region/UnassignedRegionalManagerAlert.test.tsx @@ -0,0 +1,10 @@ +import React from "react"; +import { shallow } from "enzyme"; +import UnassignedRegionalManagerAlert from "./UnassignedRegionalManagerAlert"; + +describe("UnassignedRegionalManagerAlert", () => { + it("should render", () => { + const wrapper = shallow(); + expect(wrapper.length).toBe(1); + }); +}); diff --git a/LCU.WebApps.Operations.IncentiveManager/src/components/region/UnassignedRegionalManagerAlert.tsx b/LCU.WebApps.Operations.IncentiveManager/src/components/region/UnassignedRegionalManagerAlert.tsx new file mode 100644 index 0000000..36b6d4c --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/components/region/UnassignedRegionalManagerAlert.tsx @@ -0,0 +1,12 @@ +import React from "react"; +import { Alert } from "reactstrap"; +import Translate from "./../../translate/Translate"; + +const alertTranslate = + Translate.Get.containers.employeeDashboard.unassignedRegionalManagerAlert; + +const UnassignedRegionalManagerAlert: React.FC = () => { + return {alertTranslate.message}; +}; + +export default UnassignedRegionalManagerAlert; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/components/role-mapping/RoleMappingTable.tsx b/LCU.WebApps.Operations.IncentiveManager/src/components/role-mapping/RoleMappingTable.tsx new file mode 100644 index 0000000..e780c23 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/components/role-mapping/RoleMappingTable.tsx @@ -0,0 +1,140 @@ +import React, { useState } from "react"; +import { + IAuditableRoleMapping, + IRoleMappingChange +} from "../../shared/models/RoleMapping"; +import Translate from "../../translate/Translate"; +import { SettingsContext } from "../../shared/providers/SettingsProvider"; +import RoleMappingTableRow from "./RoleMappingTableRow"; +import SettingsService from "../../shared/services/SettingsService"; +import AsyncState from "../../shared/enumerations/AsyncState"; + +interface IRoleMappingTable { + roleMappings: IAuditableRoleMapping[]; + submitRoleMappingUpdate: (roleMappingUpdate: IRoleMappingChange) => void; +} + +const RoleMappingTable: React.FC = ( + props: IRoleMappingTable +) => { + const settings = React.useContext(SettingsContext); + const appRoles = + SettingsService.getAssignableRoles(settings?.appRoles, 99, -1) ?? []; + const roleMappingTableTranslate = + Translate.Get.components.roleMappings.roleMappingTable; + + const [ + updateEmployeesMessage, + setUpdateEmployeesMessage + ] = useState(""); + const [submitState, setSubmitState] = useState( + AsyncState.Inactive + ); + + const runEmployeeUpdate = async () => { + setSubmitState(AsyncState.Loading); + + setUpdateEmployeesMessage( + roleMappingTableTranslate.employeeUpdateRunningMessage + ); + + await SettingsService.runEmployeeUpdateSync().then((responseState) => { + setSubmitState(responseState.state); + if (responseState.state === AsyncState.Success) { + setUpdateEmployeesMessage( + roleMappingTableTranslate.employeeUpdateSuccessMessage + ); + } else { + setUpdateEmployeesMessage( + roleMappingTableTranslate.employeeUpdateFailureMessage + ); + } + }); + }; + + const canSubmit = () => { + return ( + submitState === AsyncState.Failure || + submitState === AsyncState.Inactive || + submitState === AsyncState.Success + ); + }; + + if (!props.roleMappings) { + return No Data; + } + + return ( + <> +
+ + + + + + + + + + + + + + + + + + + {props.roleMappings.map((roleMapping) => { + return ( + + ); + })} + +
+

{roleMappingTableTranslate.title}

+
+ + +
+ {updateEmployeesMessage} +
+
{roleMappingTableTranslate.headers.id} + { + roleMappingTableTranslate.headers + .applicationRole + } + + {roleMappingTableTranslate.headers.businessRole} + + {roleMappingTableTranslate.headers.lastUpdated} + + { + roleMappingTableTranslate.headers + .lastUpdatedBy + } + {roleMappingTableTranslate.headers.created} + {roleMappingTableTranslate.headers.createdBy} +
+
+ + ); +}; + +export default RoleMappingTable; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/components/role-mapping/RoleMappingTableCell.tsx b/LCU.WebApps.Operations.IncentiveManager/src/components/role-mapping/RoleMappingTableCell.tsx new file mode 100644 index 0000000..9c41eea --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/components/role-mapping/RoleMappingTableCell.tsx @@ -0,0 +1,45 @@ +import React, { useState } from "react"; +import { IAppRole } from "../../shared/models/Settings"; + +interface IRoleMappingTableCellProps { + id: number; + value: IAppRole; + onRoleMappingChange: (newRole: IAppRole, id: number) => void; + tabIndex: number; +} + +const cssValidClass = (originalValue: string, newValue: string) => { + if (originalValue == newValue || (originalValue === "_" && !newValue)) + return "form-control"; + if (!newValue) return "form-control is-invalid"; + return "form-control is-valid"; +}; + +const RoleMappingTableCell: React.FC = ( + props: IRoleMappingTableCellProps +) => { + const originalValue = props.value + ""; + const [newValue, setNewValue] = useState(originalValue); + + return ( +
+ +
+ Unsaved: {originalValue} {"->"} {newValue} +
+
+ Required +
+
+ ); +}; + +export default RoleMappingTableCell; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/components/role-mapping/RoleMappingTableRow.tsx b/LCU.WebApps.Operations.IncentiveManager/src/components/role-mapping/RoleMappingTableRow.tsx new file mode 100644 index 0000000..ecd7789 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/components/role-mapping/RoleMappingTableRow.tsx @@ -0,0 +1,89 @@ +import React, { useState } from "react"; +import moment from "moment"; +import RoleMappingTableRowEdit from "./RoleMappingTableRowEdit"; +import { Button } from "reactstrap"; +import Translate from "../../translate/Translate"; +import { + IAuditableRoleMapping, + IRoleMappingChange +} from "../../shared/models/RoleMapping"; +import { IAppRole } from "../../shared/models/Settings"; + +export interface IRoleMappingTableRowProps { + roleMapping: IAuditableRoleMapping; + submitRoleMappingUpdate: (update: IRoleMappingChange) => void; + roles: IAppRole[]; +} + +const RoleMappingTranslate = + Translate.Get.components.roleMappings.roleMappingTable; + +const RoleMappingRow: React.FC = (props) => { + const createFormStateFromAssignment = ( + roleMapping: IAuditableRoleMapping + ): IRoleMappingChange => { + return { + id: roleMapping.id, + applicationRole: roleMapping.applicationRole.name, + businessRole: roleMapping.businessRole + }; + }; + + const initialFormState = createFormStateFromAssignment(props.roleMapping); + const [isEditing, setEdit] = useState(false); + const [formState, setFormState] = useState( + initialFormState + ); + + const enableEdit = () => { + setFormState(createFormStateFromAssignment(props.roleMapping)); + setEdit(true); + }; + + if (!isEditing) { + return ( + + {props.roleMapping.id} + {props.roleMapping.applicationRole.name} + {props.roleMapping.businessRole} + + {moment + .utc(props.roleMapping.lastUpdated) + .local() + .format("lll")} + + {props.roleMapping.lastUpdatedBy} + + {moment + .utc(props.roleMapping.created) + .local() + .format("lll")} + + {props.roleMapping.createdBy} + + + + + ); + } + + return ( + setEdit(false)} + updateFormState={(newState) => setFormState(newState)} + data-test="edit-row" + /> + ); +}; + +export default RoleMappingRow; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/components/role-mapping/RoleMappingTableRowEdit.tsx b/LCU.WebApps.Operations.IncentiveManager/src/components/role-mapping/RoleMappingTableRowEdit.tsx new file mode 100644 index 0000000..79fcb89 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/components/role-mapping/RoleMappingTableRowEdit.tsx @@ -0,0 +1,112 @@ +import React from "react"; +import { Button, ButtonGroup, Input } from "reactstrap"; +import { tryUpdateStateWithInput } from "../../shared/services/InputService"; +import moment from "moment"; +import Translate from "../../translate/Translate"; +import { + IAuditableRoleMapping, + IRoleMappingChange +} from "../../shared/models/RoleMapping"; +import { IAppRole } from "../../shared/models/Settings"; + +export interface IRoleMappingTableRowEditProps { + roleMapping: IAuditableRoleMapping; + formState: IRoleMappingChange; + submitRoleMappingUpdate: (update: IRoleMappingChange) => void; + disableEdit: () => void; + updateFormState: (newState: IRoleMappingChange) => void; + roles: IAppRole[]; +} + +const RoleMappingTranslate = + Translate.Get.components.roleMappings.roleMappingTable; + +const RoleMappingTableRowEdit: React.FC = ( + props +) => { + const submitForm = () => { + props.submitRoleMappingUpdate(props.formState); + props.disableEdit(); + }; + + return ( + + {props.roleMapping.id} + + { + props.updateFormState( + tryUpdateStateWithInput( + props.formState, + "applicationRole", + event.target.value + ) + ); + }} + > + + {props.roles.map((role) => { + return ( + + ); + })} + + + {props.roleMapping.businessRole} + + {moment + .utc(props.roleMapping.lastUpdated) + .local() + .format("lll")} + + {props.roleMapping.lastUpdatedBy} + + {moment.utc(props.roleMapping.created).local().format("lll")} + + {props.roleMapping.createdBy} + + + + + + + + ); +}; + +export default RoleMappingTableRowEdit; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/components/shared/ProductTableHeader.tsx b/LCU.WebApps.Operations.IncentiveManager/src/components/shared/ProductTableHeader.tsx new file mode 100644 index 0000000..a65e9fc --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/components/shared/ProductTableHeader.tsx @@ -0,0 +1,19 @@ +import React from "react"; +import { Product2 } from "../../shared/enumerations/Product"; + +const ProductTableHeader: React.FC = () => { + const headers = Product2.Products.map((product) => ( + {product.title} + )); + + return ( + + + + {headers} + + + ); +}; + +export default ProductTableHeader; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/components/shared/ProgressPercentageTableRow.tsx b/LCU.WebApps.Operations.IncentiveManager/src/components/shared/ProgressPercentageTableRow.tsx new file mode 100644 index 0000000..3f04d59 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/components/shared/ProgressPercentageTableRow.tsx @@ -0,0 +1,49 @@ +import React from "react"; +import { Product2 } from "../../shared/enumerations/Product"; +import { IPerProductActivity } from "../../shared/models/Activity"; +import GoalService from "../../shared/services/GoalService"; +import { IPerProductGoal } from "../../shared/models/Goal"; + +export interface IDepartmentProgressProps { + label: any; + progress: IPerProductActivity; + goals: IPerProductGoal; +} + +const ProgressPercentageTableRow: React.FC = ( + props: IDepartmentProgressProps +) => { + const progress = props.progress; + const goals = props.goals; + + const calculateOutput = (product: Product2): string => { + if (product.code === Product2.DebtProtectionEligibleLoan.code) { + return GoalService.convertToPercentage( + progress.productActivity[product.code], + progress.productOther[product.code] + ); + } + + if (!product.hasPersonnelGoals) { + return "N/A"; + } + + return GoalService.convertToPercentage( + progress.productActivity[product.code], + goals.productGoals[product.code] + ); + }; + + const columns = Product2.Products.map((product) => ( + {calculateOutput(product)} + )); + + return ( + + {props.label} + {columns} + + ); +}; + +export default ProgressPercentageTableRow; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/components/shared/ProgressTableRow.tsx b/LCU.WebApps.Operations.IncentiveManager/src/components/shared/ProgressTableRow.tsx new file mode 100644 index 0000000..2a7d95a --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/components/shared/ProgressTableRow.tsx @@ -0,0 +1,86 @@ +import React from "react"; +import { Product2 } from "../../shared/enumerations/Product"; +import { IPerProductActivity } from "../../shared/models/Activity"; +import GoalService from "../../shared/services/GoalService"; +import { IPerProductGoal } from "../../shared/models/Goal"; + +export interface IDepartmentProgressProps { + label: any; + progress: IPerProductActivity; + goals: IPerProductGoal; + showPercentage?: boolean; +} + +const ProgressTableRow: React.FC = ( + props: IDepartmentProgressProps +) => { + const progress = props.progress; + const goals = props.goals; + + // Choose if you want to show a goal based or "other" based + const getDisplay = (product: Product2, showPercent?: boolean) => { + if (product.code === Product2.DebtProtectionEligibleLoan.code) { + return getOtherDisplay(product, showPercent); + } + return getGoalDisplay(product, showPercent); + }; + + const getGoalDisplay = (product: Product2, showPercent?: boolean) => { + const activityValue = progress.productActivity[product.code]; + const goalValue = goals.productGoals[product.code]; + if (!product.hasPersonnelGoals) { + return GoalService.formatMonthlyGoal(activityValue, product); + } + return createValueDisplay( + activityValue, + goalValue, + product, + showPercent + ); + }; + + const getOtherDisplay = (product: Product2, showPercent?: boolean) => { + const activityValue = progress.productActivity[product.code]; + const goalValue = progress.productOther[product.code]; + return createValueDisplay( + activityValue, + goalValue, + product, + showPercent + ); + }; + + const createValueDisplay = ( + activityValue: number, + goalValue: number, + product: Product2, + showPercent?: boolean + ): string => { + if (showPercent) { + return GoalService.convertToPercentage(activityValue, goalValue); + } + return ( + GoalService.formatMonthlyGoal(activityValue, product) + + "/" + + GoalService.formatMonthlyGoal(goalValue, product) + ); + }; + + const columns = Product2.Products.map((product, index) => ( + + {getDisplay(product, props.showPercentage)} + + )); + + return ( + + {props.label} + {columns} + + ); +}; + +export default ProgressTableRow; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/components/status-pages/NotFound404Page.tsx b/LCU.WebApps.Operations.IncentiveManager/src/components/status-pages/NotFound404Page.tsx new file mode 100644 index 0000000..c89c895 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/components/status-pages/NotFound404Page.tsx @@ -0,0 +1,22 @@ +import "./StatusPage.scss"; + +import React from "react"; +import { RouteComponentProps } from "react-router-dom"; +import logo from "../../shared/assets/LMCU-Blue-Logo-Only.png"; +import Translate from "./../../translate/Translate"; + +const NotFound404Page: React.FC = () => { + return ( +
+
+ +
+

+ {Translate.Get.components.statusPages.notFound404Page.title} +

+

{Translate.Get.components.statusPages.notFound404Page.message}

+
+ ); +}; + +export default NotFound404Page; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/components/status-pages/StatusPage.scss b/LCU.WebApps.Operations.IncentiveManager/src/components/status-pages/StatusPage.scss new file mode 100644 index 0000000..7588e28 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/components/status-pages/StatusPage.scss @@ -0,0 +1,12 @@ +.status-page { + padding-top: 10em; + + h1 { + text-align: center; + } + p { + font-size: 1.5em; + font-weight: 100; + text-align: center; + } +} diff --git a/LCU.WebApps.Operations.IncentiveManager/src/components/status-pages/TestPage.tsx b/LCU.WebApps.Operations.IncentiveManager/src/components/status-pages/TestPage.tsx new file mode 100644 index 0000000..2ae818b --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/components/status-pages/TestPage.tsx @@ -0,0 +1,13 @@ +import "./StatusPage.scss"; +import React from "react"; +import { RouteComponentProps } from "react-router-dom"; + +const TestPage: React.FC = () => { + return ( +
+ +
+ ); +}; + +export default TestPage; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/components/tab-view/TabView.css b/LCU.WebApps.Operations.IncentiveManager/src/components/tab-view/TabView.css new file mode 100644 index 0000000..db99b92 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/components/tab-view/TabView.css @@ -0,0 +1,7 @@ +.nav-link { + cursor: pointer; +} + +.nav-item p { + margin-bottom: 0; +} diff --git a/LCU.WebApps.Operations.IncentiveManager/src/components/tab-view/TabView.test.tsx b/LCU.WebApps.Operations.IncentiveManager/src/components/tab-view/TabView.test.tsx new file mode 100644 index 0000000..9fd2d89 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/components/tab-view/TabView.test.tsx @@ -0,0 +1,66 @@ +import { ShallowWrapper, shallow } from "enzyme"; +import TabView, { ITabViewPage } from "./TabView"; + +import React from "react"; + +describe("TabView component", () => { + let wrapper: ShallowWrapper; + + const pages: ITabViewPage[] = [ + { + header: "Page 1", + element:
+ }, + { + header: "Page 1", + element:
+ } + ]; + + beforeEach(() => { + wrapper = shallow(); + }); + + it("Renders initial page only", () => { + const pageZeroComponent = wrapper.find("[data-test='page-0-element']"); + expect(pageZeroComponent.length).toBe(1); + const pageOneComponent = wrapper.find("[data-test='page-1-element']"); + expect(pageOneComponent.length).toBe(0); + }); + + it("Renders secondary page only on click", () => { + const navLinkComponent = wrapper.find("[data-test='nav-link-1']"); + navLinkComponent.simulate("click"); + const pageOneComponent = wrapper.find("[data-test='page-1-element']"); + expect(pageOneComponent.length).toBe(1); + const pageZeroComponent = wrapper.find("[data-test='page-0-element']"); + expect(pageZeroComponent.length).toBe(0); + }); + + it("Calls Tab Callback on Tab Change", () => { + let tabOneValue = 0; + let tabTwoValue = 5; + + const tabViewPages: ITabViewPage[] = [ + { + header: "Page 1", + element:
, + tabOpenCallback: () => { + tabOneValue = 42; + } + }, + { + header: "Page 1", + element:
, + tabOpenCallback: () => { + tabTwoValue = 1945; + } + } + ]; + wrapper = shallow(); + expect(tabOneValue).toBe(42); + const navLinkComponent = wrapper.find("[data-test='nav-link-1']"); + navLinkComponent.simulate("click"); + expect(tabTwoValue).toBe(1945); + }); +}); diff --git a/LCU.WebApps.Operations.IncentiveManager/src/components/tab-view/TabView.tsx b/LCU.WebApps.Operations.IncentiveManager/src/components/tab-view/TabView.tsx new file mode 100644 index 0000000..6eafb63 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/components/tab-view/TabView.tsx @@ -0,0 +1,83 @@ +import React, { ReactElement } from "react"; +import "./TabView.css"; + +export interface ITabViewPage { + header: string; + element: ReactElement; + tabOpenCallback?: () => any; +} + +export interface TabViewProps { + pages: ITabViewPage[]; +} + +interface TabViewState { + currentPageIndex: number; +} + +class TabView extends React.Component { + constructor(props: TabViewProps) { + super(props); + this.state = { + currentPageIndex: 0 + }; + this.callPageCallback(0); + } + + private getClassStyle(index: number): string { + return index === this.state.currentPageIndex + ? "active " + : "text-primary "; + } + + private setCurrentPage(index: number): void { + const newState: TabViewState = Object.assign({}, this.state); + newState.currentPageIndex = index; + this.setState(newState); + this.callPageCallback(index); + } + + private getCurrentPageElement(): ReactElement { + const currentPage = this.props.pages[this.state.currentPageIndex]; + if (currentPage) { + return currentPage.element; + } + return

Missing

; + } + + private callPageCallback(index: number) { + if (index < this.props.pages.length) { + const tabPageCallback = this.props.pages[index].tabOpenCallback; + if (tabPageCallback !== undefined) { + tabPageCallback(); + } + } + } + + public render() { + return ( +
+
    + {this.props.pages.map((page, index) => { + return ( +
  • +

    this.setCurrentPage(index)} + data-test={"nav-link-" + index} + > + {page.header} +

    +
  • + ); + })} +
+ {this.getCurrentPageElement()} +
+ ); + } +} + +export default TabView; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/containers/activity-detail-page/ActivityDetailPage.test.tsx b/LCU.WebApps.Operations.IncentiveManager/src/containers/activity-detail-page/ActivityDetailPage.test.tsx new file mode 100644 index 0000000..c7d688d --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/containers/activity-detail-page/ActivityDetailPage.test.tsx @@ -0,0 +1,545 @@ +import React from "react"; +import { mount } from "enzyme"; +import * as reactRedux from "react-redux"; +import ActivityDetailPage from "./ActivityDetailPage"; +import ReactRouter from "react-router"; +import ActivityService from "../../shared/services/ActivityService"; +import AsyncState from "../../shared/enumerations/AsyncState"; +import { act } from "react-dom/test-utils"; +import Product2 from "../../shared/enumerations/Product"; +import { + ActivityState, + ActivityTransferState, + ActivityType, + IActivityStateChange, + IActivityTransferAction, + IAuditableActivity, + SystemType +} from "../../shared/models/Activity"; +import moment from "moment"; +import { UserType } from "../../shared/models/Auditable"; +import { IDepartmentState } from "../../reducers/DepartmentReducer"; +import { IProfileState } from "../../reducers/ProfileReducer"; +import PermissionCode from "../../shared/enumerations/PermissionCode"; +import Translate from "../../translate/Translate"; +import { getMockAppSettings } from "../../shared/testing/mocks/MockAppSettings"; + +const useSelectorMock = jest.spyOn(reactRedux, "useSelector"); +const useDispatchMock = jest.spyOn(reactRedux, "useDispatch"); +const pageTranslate = Translate.Get.containers.activityDetailPage; + +beforeEach(() => { + useSelectorMock.mockClear(); + useDispatchMock.mockClear(); +}); + +describe("ActivityDetailPage", () => { + it("should display components if data is retrieved successfully", async () => { + const useContextMock = jest.spyOn(React, "useContext"); + useContextMock.mockReturnValue(getMockAppSettings()); + jest.spyOn(ReactRouter, "useParams").mockReturnValue({ id: "1" }); + useDispatchMock.mockImplementation(() => jest.fn()); + const getActivitySpy = jest.spyOn( + ActivityService, + "getViewableActivity" + ); + useSelectorMock.mockReturnValue({ + ...mockDepartmentState, + ...mockProfileState + }); + getActivitySpy.mockResolvedValue({ + state: AsyncState.Success, + data: { + activity: activity(), + authorized: { + canApproveTransferTo: true, + canApproveTransferFrom: true, + allowedChanges: [] + }, + transferAction: mockActivityTransferAction(), + stateChanges: changes() + } + }); + + let wrapper: any; + + await act(async () => { + wrapper = mount(); + }); + wrapper.update(); + + expect( + wrapper.find("[data-test='activity-details-components']").length + ).toBeTruthy(); + }); + + it("should call the activity service when button is clicked", async () => { + const useContextMock = jest.spyOn(React, "useContext"); + useContextMock.mockReturnValue(getMockAppSettings()); + jest.spyOn(ReactRouter, "useParams").mockReturnValue({ id: "1" }); + useDispatchMock.mockImplementation(() => jest.fn()); + const getActivitySpy = jest.spyOn( + ActivityService, + "getViewableActivity" + ); + const postStateChangeSpy = jest.spyOn( + ActivityService, + "postActivityStateChange" + ); + + useSelectorMock.mockReturnValue({ + ...mockDepartmentState, + ...mockProfileState + }); + + const activityWithoutPayouts = { ...activity(), activityPayouts: [] }; + getActivitySpy.mockResolvedValue({ + state: AsyncState.Success, + data: { + activity: activityWithoutPayouts, + authorized: { + canApproveTransferTo: true, + canApproveTransferFrom: true, + allowedChanges: [ActivityState.Rejected] + }, + transferAction: mockActivityTransferAction(), + stateChanges: changes() + } + }); + + postStateChangeSpy.mockResolvedValue({ + state: AsyncState.Success, + data: activityWithoutPayouts + }); + + let wrapper: any; + + await act(async () => { + wrapper = mount(); + }); + wrapper.update(); + + expect(getActivitySpy).toHaveBeenCalledTimes(1); + + await act(async () => { + wrapper + .find("button[data-test='Rejected-activity-button']") + .simulate("click"); + }); + + expect(postStateChangeSpy).toHaveBeenCalledTimes(1); + expect(postStateChangeSpy).toHaveBeenCalledWith({ + activityId: 1, + note: pageTranslate.stateChangeNote, + state: "Rejected" + }); + wrapper.update(); + expect(getActivitySpy).toHaveBeenCalledTimes(2); + }); + + it("should not call fetch activity again if post fails", async () => { + const useContextMock = jest.spyOn(React, "useContext"); + useContextMock.mockReturnValue(getMockAppSettings()); + jest.spyOn(ReactRouter, "useParams").mockReturnValue({ id: "1" }); + useDispatchMock.mockImplementation(() => jest.fn()); + const getActivitySpy = jest.spyOn( + ActivityService, + "getViewableActivity" + ); + const postStateChangeSpy = jest.spyOn( + ActivityService, + "postActivityStateChange" + ); + + useSelectorMock.mockReturnValue({ + ...mockDepartmentState, + ...mockProfileState + }); + + const activityWithoutPayouts = { ...activity(), activityPayouts: [] }; + getActivitySpy.mockResolvedValue({ + state: AsyncState.Success, + data: { + activity: activityWithoutPayouts, + authorized: { + canApproveTransferTo: true, + canApproveTransferFrom: true, + allowedChanges: [ActivityState.Rejected] + }, + transferAction: mockActivityTransferAction(), + stateChanges: changes() + } + }); + + // return failure when posting + postStateChangeSpy.mockResolvedValue({ + state: AsyncState.Failure + }); + + let wrapper: any; + + await act(async () => { + wrapper = mount(); + }); + wrapper.update(); + + expect(getActivitySpy).toHaveBeenCalledTimes(1); + + await act(async () => { + wrapper + .find("button[data-test='Rejected-activity-button']") + .simulate("click"); + }); + + wrapper.update(); + expect(getActivitySpy).toHaveBeenCalledTimes(1); + }); + + it("should post the activity transfer request", async () => { + const useContextMock = jest.spyOn(React, "useContext"); + useContextMock.mockReturnValue(getMockAppSettings()); + jest.spyOn(ReactRouter, "useParams").mockReturnValue({ id: "1" }); + useDispatchMock.mockImplementation(() => jest.fn()); + const getActivitySpy = jest.spyOn( + ActivityService, + "getViewableActivity" + ); + const postRequestTransferSpy = jest.spyOn( + ActivityService, + "postRequestTransfer" + ); + + useSelectorMock.mockReturnValue({ + ...mockDepartmentState, + ...mockProfileState + }); + + const activityWithoutPayouts = { ...activity(), activityPayouts: [] }; + getActivitySpy.mockResolvedValue({ + state: AsyncState.Success, + data: { + activity: activityWithoutPayouts, + authorized: { + canApproveTransferTo: true, + canApproveTransferFrom: true, + allowedChanges: [] + }, + transferAction: mockActivityTransferAction(), + stateChanges: changes() + } + }); + + const returnedAction: IActivityTransferAction = { + id: 1, + activityStateBeforeTransfer: ActivityState.Accepted, + requestedActivityId: 2, + state: ActivityTransferState.Requested, + newActivityDepartmentId: 3, + newActivityEmployeeId: "C000" + }; + postRequestTransferSpy.mockResolvedValue({ + state: AsyncState.Success, + data: returnedAction + }); + + let wrapper: any; + + await act(async () => { + wrapper = mount(); + }); + wrapper.update(); + + expect(getActivitySpy).toHaveBeenCalledTimes(1); + + // select department and appUser + await act(async () => { + wrapper + .find("select[data-test='appUser-search-select']") + .simulate("change", { target: { value: "C0002" } }); + wrapper + .find("select[data-test='dept-input']") + .simulate("change", { target: { value: "25" } }); + }); + + // submit transfer request + await act(async () => { + wrapper + .find("button[data-test='activity-transfer-request-button']") + .simulate("click"); + }); + + wrapper.update(); + expect(postRequestTransferSpy).toHaveBeenCalledTimes(1); + expect(postRequestTransferSpy).toHaveBeenCalledWith({ + activityStateBeforeTransfer: "Accepted", + newDepartmentId: 25, + newEmployeeId: "C0002", + requestedActivityId: 1 + }); + wrapper.update(); + expect(getActivitySpy).toHaveBeenCalledTimes(2); + }); + + it("should post transfer approval request", async () => { + const useContextMock = jest.spyOn(React, "useContext"); + useContextMock.mockReturnValue(getMockAppSettings()); + jest.spyOn(ReactRouter, "useParams").mockReturnValue({ + id: "1" + }); + useDispatchMock.mockImplementation(() => jest.fn()); + const getActivitySpy = jest.spyOn( + ActivityService, + "getViewableActivity" + ); + const postApproveTransferSpy = jest.spyOn( + ActivityService, + "postApproveTransfer" + ); + + useSelectorMock.mockReturnValue({ + ...mockDepartmentState, + ...mockProfileState + }); + + const pendingTransferActivity = activity(); + pendingTransferActivity.state = ActivityState.PendingTransfer; + getActivitySpy.mockResolvedValue({ + state: AsyncState.Success, + data: { + activity: activity(), + authorized: { + canApproveTransferTo: true, + canApproveTransferFrom: true, + allowedChanges: [] + }, + transferAction: mockActivityTransferAction(), + stateChanges: changes() + } + }); + + let wrapper: any; + + await act(async () => { + wrapper = mount(); + }); + wrapper.update(); + + expect(getActivitySpy).toHaveBeenCalledTimes(1); + + expect( + wrapper.find("[data-test='activity-approval-component']").length + ).toBeTruthy(); + + await act(async () => { + wrapper + .find("button[data-test='reject-transfer-button']") + .simulate("click"); + }); + + expect(postApproveTransferSpy).toHaveBeenCalledTimes(1); + expect(postApproveTransferSpy).toHaveBeenCalledWith({ + id: 11, + approvalState: ActivityTransferState.Rejected + }); + }); +}); + +const activity = (): IAuditableActivity => { + return { + id: 1, + product: ("ATMCard" as unknown) as Product2, + type: ActivityType.Referral, + sourceSystem: SystemType.Dna, + sourceId: "11", + sourceCreated: moment.utc(), + departmentId: "25", + employeeId: "2", + employeeEmail: "update@landmarkcu.com", + accepted: moment.utc(), + state: ActivityState.Accepted, + created: moment.utc(), + createdBy: "Created Doe", + createdByType: UserType.Associate, + lastUpdated: moment.utc(), + lastUpdatedBy: "Updated Doe", + lastUpdatedByType: UserType.Associate, + detail: { + id: 1, + accountNumber: "11", + dnaPersonNbr: "12", + memberName: "Member Doe", + accountStatus: "Open", + accountBalance: 21, + accountNewBalance: 11 + }, + metaData: [ + { + id: 1, + metaData: "test data", + activityRef: 1, + metaDataType: "Unknown" + } + ], + activityPayouts: [ + { + payoutLineItem: { + id: 1, + activityPeriod: moment.utc(), + departmentId: 25, + role: { + description: "Teller", + name: "Teller Doe", + permissions: [], + hasCommissions: true, + rank: 99 + }, + product: "ATMCard", + goalId: 2, + goalValue: 3, + commissionId: 4, + commissionValue: 5, + employeeId: "123", + activityTotal: 100, + payoutTotal: 200, + slidingPayout: 100 + } + }, + { + payoutLineItem: { + id: 2, + activityPeriod: moment.utc(), + departmentId: 25, + role: { + description: "Branch Manager", + name: "B.M. Doe", + permissions: [], + hasCommissions: true, + rank: 99 + }, + product: "ATMCard", + goalId: 2, + goalValue: 3, + commissionId: 4, + commissionValue: 5, + employeeId: "456", + activityTotal: 100, + payoutTotal: 200, + slidingPayout: 100 + } + } + ] + }; +}; + +const changes = () => { + return [ + { + id: 1, + state: "Created", + note: "Auto created", + created: moment.utc() + }, + { + id: 2, + state: "Created", + note: "Auto created", + created: moment.utc() + } + ] as IActivityStateChange[]; +}; + +const mockDepartmentState: IDepartmentState = { + departments: { + data: [ + { + departmentId: 25, + id: 25, + departmentName: "WAUWATOSA", + businessLineCode: "OPS", + state: "Active", + dnaOrgNbr: "111", + regionCode: "2", + dayForceGlCode: "33" + } + ], + state: AsyncState.Success + }, + submitState: { state: AsyncState.Inactive } +}; + +const mockProfileState: IProfileState = { + profile: { + data: { + adEmail: "johntester@test.com", + baseRole: "Test Role", + departments: [ + { + employeeId: "", + departmentId: 25, + startDate: moment(), + note: "", + isHome: true, + slidingPayout: 100, + role: { + name: "Manager", + description: "Manager", + hasCommissions: true, + permissions: [ + { + code: + PermissionCode.TRANSFER_ACTIVITIES_BETWEEN_DEPARTMENTS, + description: "" + }, + { + code: + PermissionCode.SET_ACTIVITY_TO_MANUAL_REVIEW, + description: "" + } + ], + rank: 99 + }, + created: moment(), + createdBy: "", + createdByType: UserType.Associate, + lastUpdated: moment(), + lastUpdatedBy: "", + lastUpdatedByType: UserType.Associate + } + ], + employeeId: "12345", + fullName: "John Tester", + homeDepartment: "", + personId: 0, + role: { + name: "myrole", + permissions: [ + { + code: + PermissionCode.TRANSFER_ACTIVITIES_BETWEEN_DEPARTMENTS, + description: "" + } + ], + description: "My Role", + hasCommissions: true, + rank: 99 + }, + additionalPermissions: [], + assignments: [] + }, + state: AsyncState.Success + }, + searchResults: { + state: AsyncState.Inactive + } +}; + +const mockActivityTransferAction = (): IActivityTransferAction => { + return { + id: 11, + activityStateBeforeTransfer: ActivityState.Accepted, + newActivityDepartmentId: 25, + newActivityEmployeeId: "C0002", + requestedActivityId: 1, + state: ActivityTransferState.Requested + }; +}; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/containers/activity-detail-page/ActivityDetailPage.tsx b/LCU.WebApps.Operations.IncentiveManager/src/containers/activity-detail-page/ActivityDetailPage.tsx new file mode 100644 index 0000000..2d0a2b9 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/containers/activity-detail-page/ActivityDetailPage.tsx @@ -0,0 +1,272 @@ +import React, { useEffect, useState } from "react"; +import { useParams } from "react-router-dom"; +import { Col, Row } from "reactstrap"; +import AsyncState from "../../shared/enumerations/AsyncState"; +import { + ActivityState, + ActivityTransferState, + IActivityStateChangeDto, + IActivityTransferAction, + IActivityTransferApprovalRequestDto, + IActivityTransferRequestDto, + IAuditableActivity, + IViewableActivityDetail +} from "../../shared/models/Activity"; +import ActivityService from "../../shared/services/ActivityService"; +import IAsyncStateContainer from "../../shared/types/IAsyncStateContainer"; +import ActivityDetailsAlertBar from "../../components/activity-detail/ActivityDetailAlertBar"; +import ActivityDisplay from "../../components/activity-detail/ActivityDisplay"; +import ActivityDetailDisplay from "../../components/activity-detail/ActivityDetailDisplay"; +import ActivityStateChangesTable from "../../components/activity-detail/ActivityStateChangesTable"; +import ActivityMetaDataDisplay from "../../components/activity-detail/ActivityMetaDataDisplay"; +import ActivityPayoutsDisplay from "../../components/activity-detail/ActivityPayoutsDisplay"; +import ActivityTransferRequest from "../../components/activity-detail/ActivityTransferRequest"; +import ActivityTransferApproval from "../../components/activity-detail/ActivityTransferApproval"; +import { useSelector } from "react-redux"; +import { IAppState } from "../../store/Store"; +import { IProfileState } from "../../reducers/ProfileReducer"; +import Translate from "../../translate/Translate"; +import { SettingsContext } from "../../shared/providers/SettingsProvider"; +import ActivityTransferToOwnDepartmentRequest from "../../components/activity-detail/ActivityTransferToOwnDepartmentRequest"; +import { GetCurrentActiveDepartments } from "../../shared/models/Employee"; +import ActivityDetailUpdatesTable from "../../components/activity-detail/ActivityDetailUpdatesTable"; +import { Moment } from "moment"; + +const pageTranslate = Translate.Get.containers.activityDetailPage; + +const ActivityDetailPage: React.FC = () => { + const { id } = useParams<{ id: string }>(); + const settings = React.useContext(SettingsContext); + const [activity, setActivity] = useState< + IAsyncStateContainer + >({ + state: AsyncState.Inactive + }); + + const [activityStateChange, setActivityStateChange] = useState< + IAsyncStateContainer + >({ + state: AsyncState.Inactive + }); + + const [activityTransferChange, setActivityTransferChange] = useState< + IAsyncStateContainer + >({ state: AsyncState.Inactive }); + + const profileState = useSelector( + (state) => state.profileState + ); + const myProfile = profileState.profile; + const departments = settings?.departments.filter( + (x) => x.state == "Active" + ); + + useEffect(() => { + loadActivity(); + }, []); + + useEffect(() => { + if (activityStateChange.state === AsyncState.Success) { + loadActivity(); + } + }, [activityStateChange]); + + useEffect(() => { + if (activityTransferChange.state === AsyncState.Success) { + loadActivity(); + } + }, [activityTransferChange]); + + const loadActivity = async () => { + setActivity({ state: AsyncState.Loading }); + const activity = await ActivityService.getViewableActivity(+id); + setActivity(activity); + }; + + const changeActivityState = async ( + newState: ActivityState, + newDate: Moment | undefined + ) => { + if (activity.data?.activity) { + setActivityStateChange({ state: AsyncState.Loading }); + const change: IActivityStateChangeDto = { + activityId: activity.data.activity.id, + note: pageTranslate.stateChangeNote, + acceptedDate: newDate, + state: newState + }; + const changedActivity = await ActivityService.postActivityStateChange( + change + ); + setActivityStateChange(changedActivity); + } + }; + + const submitTransferRequest = async ( + departmentId: string, + employeeId: string + ) => { + if (activity.data && activity.data.activity) { + setActivityTransferChange({ state: AsyncState.Loading }); + const transferRequest: IActivityTransferRequestDto = { + activityStateBeforeTransfer: activity.data.activity.state, + requestedActivityId: activity.data.activity.id, + newDepartmentId: +departmentId, + newEmployeeId: employeeId + }; + const requestResult = await ActivityService.postRequestTransfer( + transferRequest + ); + setActivityTransferChange(requestResult); + } + return; + }; + + const submitTransferToOwnDepartmentRequest = async ( + departmentId: string, + employeeId: string + ) => { + if (activity.data && activity.data.activity) { + setActivityTransferChange({ state: AsyncState.Loading }); + const transferRequest: IActivityTransferRequestDto = { + activityStateBeforeTransfer: activity.data.activity.state, + requestedActivityId: activity.data.activity.id, + newDepartmentId: +departmentId, + newEmployeeId: employeeId + }; + const requestResult = await ActivityService.postRequestTransferToOwnDepartment( + transferRequest + ); + setActivityTransferChange(requestResult); + } + return; + }; + + const approveTransferRequest = async ( + requestId: number, + decision: ActivityTransferState + ) => { + setActivityTransferChange({ state: AsyncState.Loading }); + const transferApprovalRequest: IActivityTransferApprovalRequestDto = { + id: requestId, + approvalState: decision + }; + const requestResult = await ActivityService.postApproveTransfer( + transferApprovalRequest + ); + setActivityTransferChange(requestResult); + }; + + return ( + <> + +

{pageTranslate.title}

+ {activity.data && + activity.data.activity && + activity.state === AsyncState.Success && + myProfile.data && + departments && ( +
+ + + + + + changeActivityState( + newState, + newDate + ) + } + profile={myProfile.data} + authorization={ + activity.data.authorized + } + /> + + + + + + + + + + activityPayouts.payoutLineItem + )} + /> + + + data + )} + /> + + + + + + + + + + + + + + + + +
+ )} + + ); +}; + +export default ActivityDetailPage; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/containers/activity-form-page/ActivityFormPage.tsx b/LCU.WebApps.Operations.IncentiveManager/src/containers/activity-form-page/ActivityFormPage.tsx new file mode 100644 index 0000000..4956f48 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/containers/activity-form-page/ActivityFormPage.tsx @@ -0,0 +1,52 @@ +import React, { useState } from "react"; +import { ManualActivityWizard } from "../../components/activity/manual-activity-wizard/ManualActivityWizard"; +import { + generateDefaultActivityForm, + IManualActivityFormState +} from "../../shared/models/Activity"; +import ActivityService from "../../shared/services/ActivityService"; +import AsyncState from "../../shared/enumerations/AsyncState"; +import { SettingsContext } from "../../shared/providers/SettingsProvider"; +import { IDepartment } from "../../shared/models/Department"; + +const ActivityFormPage: React.FC = () => { + const [activityForm, setActivityForm] = useState( + generateDefaultActivityForm() + ); + // This is an experimental approach to showing loading state locally without breaking the pattern alex set. + const [submitState, setSubmitState] = useState( + AsyncState.Inactive + ); + + const settings = React.useContext(SettingsContext); + + const submitActivity = async () => { + setSubmitState(AsyncState.Loading); + await ActivityService.submitManualActivityForm(activityForm); + setSubmitState(AsyncState.Success); + setActivityForm(generateDefaultActivityForm()); + }; + + let departments: IDepartment[] = []; + if (settings?.departments) { + departments = settings.departments.filter((x) => x.state == "Active"); + } + + return ( + { + setActivityForm(newState); + }} + submitManualActivity={submitActivity} + clearForm={() => { + setActivityForm(generateDefaultActivityForm()); + }} + departments={departments} + products={settings?.productDetails ?? []} + /> + ); +}; + +export default ActivityFormPage; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/containers/activity-page/ActivityPage.test.tsx b/LCU.WebApps.Operations.IncentiveManager/src/containers/activity-page/ActivityPage.test.tsx new file mode 100644 index 0000000..00f09dd --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/containers/activity-page/ActivityPage.test.tsx @@ -0,0 +1,54 @@ +import { shallow } from "enzyme"; +import * as React from "react"; +import * as reactRedux from "react-redux"; +import ActivityPage from "./ActivityPage"; +import { IActivityState } from "../../reducers/ActivityReducer"; +import AsyncState from "../../shared/enumerations/AsyncState"; +import ActivityPageFilterForm from "./ActivityPageFilterForm"; +import ActivityAuditLog from "../../components/activity/activity-audit-log/ActivityAuditLog"; + +describe("A component for finding activities", () => { + const useDispatchMock = jest.spyOn(reactRedux, "useDispatch"); + const useSelectorMock = jest.spyOn(reactRedux, "useSelector"); + const useEffectMock = jest.spyOn(React, "useEffect"); + + beforeEach(() => { + useSelectorMock.mockClear(); + useDispatchMock.mockClear(); + useEffectMock.mockClear(); + }); + + it("Displays a blank search form", () => { + const dummyDispatch = jest.fn(); + const mockActivities: IActivityState = { + activityStateFilter: [], + currentUserDepartmentProgress: { + state: AsyncState.Inactive + }, + currentUserProgress: { + state: AsyncState.Inactive + }, + filteredActivitiesPageCache: [], + filteredActivities: { + data: { + items: [], + totalPages: 1, + totalItems: 0, + currentPage: 1 + }, + state: AsyncState.Success + } + }; + useDispatchMock.mockReturnValue(dummyDispatch); + useSelectorMock.mockReturnValue(mockActivities); + const wrapper = shallow(); + + const found = wrapper.find(ActivityPageFilterForm); + expect(found.length).toBe(1); + + const found2 = wrapper.find(ActivityAuditLog); + expect(found2.length).toBe(1); + + expect(dummyDispatch).not.toHaveBeenCalled(); + }); +}); diff --git a/LCU.WebApps.Operations.IncentiveManager/src/containers/activity-page/ActivityPage.tsx b/LCU.WebApps.Operations.IncentiveManager/src/containers/activity-page/ActivityPage.tsx new file mode 100644 index 0000000..6ba7740 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/containers/activity-page/ActivityPage.tsx @@ -0,0 +1,77 @@ +import React from "react"; +import { IAppState } from "../../store/Store"; +import { + getAllActivities, + getLastPageOfActivities, + getNextPageOfActivities +} from "../../actions/ActivityActions"; +import { useDispatch, useSelector } from "react-redux"; +import { IActivityState } from "../../reducers/ActivityReducer"; +import ActivityAuditLog from "../../components/activity/activity-audit-log/ActivityAuditLog"; +import Configuration from "./../../shared/configuration/Configuration"; +import ActivityPageFilterForm from "./ActivityPageFilterForm"; +import Translate from "../../translate/Translate"; +import { debounce } from "debounce"; +import { IActivitySearchForm } from "../../shared/models/Activity"; + +const ActivityAuditLogTranslate = + Translate.Get.components.activity.activityAuditLog; + +const ActivityPage: React.FC = () => { + const dispatch = useDispatch(); + + const [form, setForm] = React.useState({ + state: [], + dateHigh: null, + dateLow: null, + query: "", + product: "", + type: "" + }); + + const activityState = useSelector( + (state) => state.activityState + ); + + React.useEffect(() => { + search(form); + }, [form]); + + const search = React.useCallback( + debounce((query: IActivitySearchForm) => { + dispatch( + getAllActivities( + 1, + Configuration.Get.defaultPaginationLimit, + query + ) + ); + }, 500), + [] + ); + + const changeSort = (headerName: string) => { + setForm({ + ...form, + sortBy: headerName, + sortDescending: + headerName === form.sortBy ? !form.sortDescending : false + }); + }; + + return ( +
+
{ActivityAuditLogTranslate.auditTableTitle}
+ + dispatch(getNextPageOfActivities(form))} + getPrevPage={() => dispatch(getLastPageOfActivities(form))} + changeSort={changeSort} + form={form} + /> +
+ ); +}; + +export default ActivityPage; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/containers/activity-page/ActivityPageFilterForm.test.tsx b/LCU.WebApps.Operations.IncentiveManager/src/containers/activity-page/ActivityPageFilterForm.test.tsx new file mode 100644 index 0000000..96fad3e --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/containers/activity-page/ActivityPageFilterForm.test.tsx @@ -0,0 +1,67 @@ +import React from "react"; +import { mount } from "enzyme"; +import ActivityPageFilterForm from "./ActivityPageFilterForm"; +import { IActivitySearchForm } from "../../shared/models/Activity"; +import { FormGroup } from "reactstrap"; + +describe("ActivePageFilterForm", () => { + it("should display form correctly", () => { + let testCalls = 0; + const testCall = (filter: IActivitySearchForm) => { + testCalls++; + }; + const wrapper = mount( + + ); + + expect(wrapper.find(FormGroup).length).toEqual(8); + expect(testCalls).toEqual(1); + }); + + it("should respond to changes correctly", () => { + let testCalls = 0; + let lastFilter: IActivitySearchForm = { + query: "", + dateHigh: null, + dateLow: null, + state: [], + product: "", + type: "", + employeeId: "", + departmentId: 0 + }; + const testCall = (filter: IActivitySearchForm) => { + lastFilter = filter; + testCalls++; + }; + + const wrapper = mount( + + ); + expect(wrapper.find(FormGroup).length).toEqual(8); + + wrapper + .find("[name='query']") + .first() + .simulate("change", { target: { value: "3" } }); + expect(testCalls).toEqual(2); + expect(lastFilter.query).toEqual("3"); + + wrapper + .find("[name='ActivityProduct']") + .first() + .simulate("change", { target: { value: "Vehicle" } }); + expect(testCalls).toEqual(3); + expect(lastFilter.product).toEqual("Vehicle"); + expect(lastFilter.query).toEqual("3"); + + wrapper + .find("[name='ActivityType']") + .first() + .simulate("change", { target: { value: "Sale" } }); + expect(testCalls).toEqual(4); + expect(lastFilter.type).toEqual("Sale"); + expect(lastFilter.product).toEqual("Vehicle"); + expect(lastFilter.query).toEqual("3"); + }); +}); diff --git a/LCU.WebApps.Operations.IncentiveManager/src/containers/activity-page/ActivityPageFilterForm.tsx b/LCU.WebApps.Operations.IncentiveManager/src/containers/activity-page/ActivityPageFilterForm.tsx new file mode 100644 index 0000000..bea3595 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/containers/activity-page/ActivityPageFilterForm.tsx @@ -0,0 +1,265 @@ +import React, { useEffect, useState } from "react"; +import { FormFeedback, FormGroup, Input, Label } from "reactstrap"; +import { SettingsContext } from "../../shared/providers/SettingsProvider"; +import { ActivityState, ActivityType } from "../../shared/models/Activity"; +import moment, { Moment } from "moment"; +import Translate from "../../translate/Translate"; +import { IActivitySearchForm } from "../../shared/models/Activity"; +import UserSearchSelect from "../../components/activity/manual-activity-wizard/UserSearchSelect"; +import Select from "react-select"; + +interface IActivityPageFilterFormProps { + filterResults: (filter: IActivitySearchForm) => void; +} + +const activityFilterFormTranslate = + Translate.Get.components.activity.filterForm; + +const ActivityPageFilterForm: React.FC = ({ + filterResults +}) => { + const [form, setForm] = useState({ + state: ["Accepted"], + dateHigh: null, + dateLow: null, + query: "", + product: "", + type: "", + departmentId: 0, + employeeId: "" + }); + + const settings = React.useContext(SettingsContext); + useEffect(() => { + filterResults(form); + }, [form]); + const departments = settings?.departments || []; + + const updateProduct = (newProduct: string) => { + setForm({ + ...form, + product: newProduct + }); + }; + + const updateType = (newType: string) => { + setForm({ + ...form, + type: newType + }); + }; + + const updateState = (newState: string[]) => { + setForm({ + ...form, + state: newState + }); + }; + + const setSearchQuery = (newQuery: string) => { + setForm({ + ...form, + query: newQuery + }); + }; + + const updateLowDate = (date: Moment) => { + setForm({ + ...form, + dateLow: date + }); + }; + + const updateHighDate = (date: Moment) => { + setForm({ + ...form, + dateHigh: date + }); + }; + + const updateDepartment = (departmentId: number) => { + setForm({ + ...form, + departmentId: departmentId + }); + }; + + const updateEmployee = (employeeId?: string) => { + setForm({ + ...form, + employeeId: employeeId + }); + }; + + const activityStateOptions = Object.values(ActivityState).map( + (state) => new Option(state, state) + ); + + const acceptedActivityStateOption = activityStateOptions.find( + (state) => state.value === "Accepted" + ); + + return ( +
+ + + { + updateProduct(event.target.value); + }} + > + + {settings?.productDetails.map((product) => { + return ( + + ); + })} + + + + + { + updateType(event.target.value); + }} + > + + {Object.values(ActivityType).map((activityType) => { + return ( + + ); + })} + + + + + { + updateDepartment(parseInt(event.target.value, 10)); + }} + > + + {departments.map((department) => { + return ( + + ); + })} + + Please select a value. + + + + { + updateEmployee(value); + }} + showValidation={false} + placeHolder={ + activityFilterFormTranslate.employeeId.placeHolder + } + /> + + + + { + updateLowDate(moment.utc(event.target.value)); + }} + /> + + DeptAssignmentTranslate.validation.endDate + + + + + { + updateHighDate(moment.utc(event.target.value)); + }} + /> + + DeptAssignmentTranslate.validation.endDate + + + + + { + setSearchQuery(event.target.value); + }} + type="text" + placeholder={ + activityFilterFormTranslate.searchQuery.placeHolder + } + /> + +
+ ); +}; + +export default ActivityPageFilterForm; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/containers/commissions-page/CommissionRatesEditor.test.tsx b/LCU.WebApps.Operations.IncentiveManager/src/containers/commissions-page/CommissionRatesEditor.test.tsx new file mode 100644 index 0000000..7c3e2bd --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/containers/commissions-page/CommissionRatesEditor.test.tsx @@ -0,0 +1,250 @@ +import React from "react"; +import { mount } from "enzyme"; +import CommissionRatesEditor from "./CommissionRatesEditor"; +import * as reactRedux from "react-redux"; +import { IAppRole, ISettings } from "../../shared/models/Settings"; +import AsyncState from "../../shared/enumerations/AsyncState"; +import moment from "moment"; +import { UserType } from "../../shared/models/Auditable"; +import { IProductDetail } from "../../shared/enumerations/Product"; +import CommissionService from "../../shared/services/CommissionService"; +import { act } from "react-dom/test-utils"; +import { getMockAppSettings } from "../../shared/testing/mocks/MockAppSettings"; + +describe("A container for setting commission rates", () => { + const useSelectorMock = jest.spyOn(reactRedux, "useSelector"); + const useDispatchMock = jest.spyOn(reactRedux, "useDispatch"); + const useContextMock = jest.spyOn(React, "useContext"); + + beforeEach(() => { + useSelectorMock.mockClear(); + useDispatchMock.mockClear(); + useContextMock.mockClear(); + }); + + it("Displays a table with a row and cell for every product and role with commissions", () => { + const mockSelectorValue = defaultMockSelectorValue(); + + const settings = getMockAppSettings(); + const dummyDispatch = jest.fn(); + useDispatchMock.mockReturnValue(dummyDispatch); + useSelectorMock.mockReturnValue(mockSelectorValue); + useContextMock.mockReturnValue(settings); + + const editor = mount(); + + const rolesWithCommissions = + settings?.appRoles.filter((role) => role.hasCommissions) ?? []; + + const productsWithCommissions = + settings?.productDetails.filter( + (product) => product.hasCommissions + ) ?? []; + + const totalCells = + productsWithCommissions.length * rolesWithCommissions.length; + + expect(editor.find("[data-test='commission-rates-table']").length).toBe( + 1 + ); + expect(editor.find("[data-test='commission-rate-cell']").length).toBe( + totalCells + ); + expect(editor.find("[data-test='commission-rate-row']").length).toBe( + productsWithCommissions.length + ); + }); + + it("saves when there are pending changes", async () => { + const mockSelectorValue = defaultMockSelectorValue(); + useDispatchMock.mockImplementation(() => jest.fn()); + useSelectorMock.mockReturnValue(mockSelectorValue); + useContextMock.mockReturnValue(getMockAppSettings()); + const setCommissionsMock = jest + .spyOn(CommissionService, "setCommissions") + .mockResolvedValue({ state: AsyncState.Success }); + + const editor = mount(); + + const saveButton = () => + editor.find("[data-test='commission-save-btn']"); + expect(saveButton().props().disabled).toBe(true); + + // make 2 pending commission rate changes + const cells = editor.find("[data-test='commission-rate-cell']"); + cells + .at(0) + .find("input") + .simulate("change", { target: { value: "3" } }); + cells + .at(1) + .find("input") + .simulate("change", { target: { value: "4" } }); + + // verify save button + expect(saveButton().props().disabled).toBe(false); + await act(async () => { + saveButton().simulate("click"); + }); + editor.update(); + + expect(setCommissionsMock).toHaveBeenCalledTimes(1); + expect(setCommissionsMock).toHaveBeenCalledWith([ + { + id: 0, + product: "ATMCard", + ratePerProduct: 3, + role: { + name: "Mortgage", + description: "Mortgage", + permissions: [ + { + code: "ELIGIBLE_FOR_REFERRALS", + description: "User is eligible for referrals" + }, + { + code: "VIEW_DASHBOARD", + description: + "User can see a dashboard showing their referral totals and their sales totals compared to goal" + } + ], + hasCommissions: true, + rank: 1 + } + }, + { + id: 0, + product: "ATMCard", + ratePerProduct: 4, + role: { + name: "Manager", + description: "Manager", + permissions: [ + { + code: "VIEW_PERFORMANCE_FOR_DEPARTMENT", + description: + "User can see performance for their department" + }, + { + code: "MANAGE_DEPARTMENT_GOALS", + description: + "User can manange their department goals" + }, + { + code: "ASSIGN_ASSOCIATES_TO_DEPARTMENT", + description: + "User can assign associates to additional departments" + }, + { + code: "TRANSFER_ACTIVITIES_BETWEEN_DEPARTMENTS", + description: + "User can transfer activities between deparments" + }, + { + code: "TRANSFER_ACTIVITIES_BETWEEN_ASSOCIATES", + description: + "User can transfer activities between associates" + } + ], + hasCommissions: true, + rank: 1 + } + } + ]); + + const saveSpan = editor.find("[data-test='commission-save-span']"); + expect(saveSpan.text()).toContain("Changes Saved"); + expect(saveButton().props().disabled).toBe(true); + }); + + it("Displays error if success not returned from commission service", async () => { + const mockSelectorValue = defaultMockSelectorValue(); + useDispatchMock.mockImplementation(() => jest.fn()); + useSelectorMock.mockReturnValue(mockSelectorValue); + useContextMock.mockReturnValue(getMockAppSettings()); + jest.spyOn(CommissionService, "setCommissions").mockResolvedValue({ + state: AsyncState.Failure, + data: "There was an error saving commissions" + }); + + const editor = mount(); + + const saveButton = () => + editor.find("[data-test='commission-save-btn']"); + expect(saveButton().props().disabled).toBe(true); + + // make a pending commission rate changes + const cells = editor.find("[data-test='commission-rate-cell']"); + cells + .at(0) + .find("input") + .simulate("change", { target: { value: "3" } }); + + // save + await act(async () => { + saveButton().simulate("click"); + }); + editor.update(); + + // verify failure message + const saveSpan = editor.find("[data-test='commission-save-span']"); + expect(saveSpan.text()).toEqual( + "There was an error saving commissions" + ); + }); + + it("Displays please wait... if no commissions returned from state", () => { + const mockSelectorValue = { + appSettings: { + data: { + appRoles: [] as IAppRole[], + productDetails: [] as IProductDetail[] + } as ISettings + }, + commissions: { + state: AsyncState.Inactive + }, + submitState: { + state: AsyncState.Inactive + } + }; + + useDispatchMock.mockReturnValue(jest.fn()); + useSelectorMock.mockReturnValue(mockSelectorValue); + useContextMock.mockReturnValue(getMockAppSettings()); + + const editor = mount(); + + expect(editor.find("[data-test='commissions-wait']").length).toBe(1); + }); +}); + +function defaultMockSelectorValue() { + return { + commissions: { + data: [ + { + product: "Commissions 1", + role: { + name: "BOM", + description: "Branch Operations Manager", + permissions: [{ code: "HAVE_SALES_GOALS" }], + hasCommissions: true + }, + id: 1, + ratePerProduct: 11, + created: moment(), + createdBy: "", + createdByType: UserType.Associate, + lastUpdated: moment(), + lastUpdatedBy: "", + lastUpdatedByType: UserType.Associate + } + ], + state: AsyncState.Inactive + }, + submitState: { + state: AsyncState.Inactive + } + }; +} diff --git a/LCU.WebApps.Operations.IncentiveManager/src/containers/commissions-page/CommissionRatesEditor.tsx b/LCU.WebApps.Operations.IncentiveManager/src/containers/commissions-page/CommissionRatesEditor.tsx new file mode 100644 index 0000000..7280774 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/containers/commissions-page/CommissionRatesEditor.tsx @@ -0,0 +1,139 @@ +import React from "react"; +import { ICommissionRateChange } from "../../shared/models/Commission"; +import { IAppState } from "../../store/Store"; +import CommissionRatesTable from "../../components/commissions/CommissionRatesTable"; +import { useState } from "react"; +import { useDispatch, useSelector } from "react-redux"; +import { getAllCommissions } from "../../actions/CommissionActions"; +import { ICommissionState } from "../../reducers/CommissionReducer"; +import CommissionService from "../../shared/services/CommissionService"; +import AsyncState from "../../shared/enumerations/AsyncState"; +import moment from "moment"; +import { SettingsContext } from "../../shared/providers/SettingsProvider"; + +const CommissionRatesEditor: React.FC = () => { + const dispatch = useDispatch(); + + const settings = React.useContext(SettingsContext); + + const commissionsState = useSelector( + (state) => state.commissionState + ); + + const commissions = CommissionService.mapToChange( + commissionsState.commissions.data ?? [] + ); + + const [pendingChanges, setPendingChanges] = useState< + ICommissionRateChange[] + >([]); + + const [saveMessage, setSaveMessage] = useState(""); + const [submitState, setSubmitState] = useState( + AsyncState.Inactive + ); + + const rolesWithCommissions = + settings?.appRoles.filter((role) => role.hasCommissions) ?? []; + + const productsWithCommissions = + settings?.productDetails.filter((product) => product.hasCommissions) ?? + []; + + const isValidChanges = (changes: ICommissionRateChange[]): boolean => { + let isValid = true; + changes.forEach((change) => { + if (!CommissionService.isValidRateChange(change)) { + isValid = false; + } + }); + return isValid; + }; + + const changeRate = (change: ICommissionRateChange) => { + setSubmitState(AsyncState.Inactive); + const changes = [...pendingChanges]; + + const original = commissions.find( + (com) => + com.role.name === change.role.name && + com.product === change.product + ); + + const index = pendingChanges.findIndex( + (com) => + com.role.name === change.role.name && + com.product === change.product + ); + + if (index < 0) { + changes.push(change); // add new + } else if (original?.ratePerProduct === change.ratePerProduct) { + changes.splice(index, 1); // remove change if it is the same as original value + } else { + changes[index] = { ...changes[index], ...change }; // update already pending change + } + + setPendingChanges(changes); + }; + + const save = async () => { + setSubmitState(AsyncState.Loading); + + await CommissionService.setCommissions(pendingChanges).then( + (responseState) => { + setSubmitState(responseState.state); + if (responseState.state === AsyncState.Success) { + setSaveMessage("Changes Saved " + moment().format("LTS")); + setPendingChanges([]); + dispatch(getAllCommissions()); + } else { + setSaveMessage(responseState.data ?? ""); + } + } + ); + }; + + const canSave = () => { + return ( + (submitState === AsyncState.Inactive || + submitState === AsyncState.Success) && + isValidChanges(pendingChanges) && + pendingChanges.length > 0 + ); + }; + + return ( + <> +
+ + {saveMessage} + + +
+ {commissionsState.commissions.state && + commissionsState.commissions.data && + rolesWithCommissions.length && + productsWithCommissions.length ? ( + + ) : ( +
Please Wait...
+ )} + + ); +}; + +export default CommissionRatesEditor; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/containers/commissions-page/CommissionsForRole.tsx b/LCU.WebApps.Operations.IncentiveManager/src/containers/commissions-page/CommissionsForRole.tsx new file mode 100644 index 0000000..3a7eb66 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/containers/commissions-page/CommissionsForRole.tsx @@ -0,0 +1,80 @@ +import React, { useEffect } from "react"; +import { useDispatch, useSelector } from "react-redux"; +import { getAllCommissions } from "../../actions/CommissionActions"; +import { ICommissionState } from "../../reducers/CommissionReducer"; +import { IAppState } from "../../store/Store"; +import { IAppRole } from "../../shared/models/Settings"; +import { Card, CardTitle, Table } from "reactstrap"; +import Translate from "../../translate/Translate"; +import ProductDisplay from "../shared/ProductDisplay"; + +export interface IPayoutProfileProps { + role: IAppRole | undefined; +} + +const payoutTranslate = Translate.Get.components.payouts; + +const CommissionsForRole: React.FC = (props) => { + const dispatch = useDispatch(); + + const commissionState = useSelector( + (state) => state.commissionState + ); + let commissions = commissionState.commissions.data ?? []; + + if (commissions) { + commissions = commissions.filter( + (commission) => + props.role?.name === commission.role.name && + commission.ratePerProduct > 0 + ); + } + + useEffect(() => { + dispatch(getAllCommissions()); + }, []); + + if ( + !commissions || + commissions.length === 0 || + !props.role?.hasCommissions + ) { + return <> No Commissions for {props.role?.name}.; + } + return ( + + + Commissions for {props.role?.name} + + + + + + + + + + {commissions.map((commission) => { + return ( + + + + + ); + })} + +
{payoutTranslate.product}{payoutTranslate.pays}
+ + ${commission.ratePerProduct}.00
+
+ ); +}; + +export default CommissionsForRole; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/containers/commissions-page/CommissionsPage.tsx b/LCU.WebApps.Operations.IncentiveManager/src/containers/commissions-page/CommissionsPage.tsx new file mode 100644 index 0000000..f423a74 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/containers/commissions-page/CommissionsPage.tsx @@ -0,0 +1,30 @@ +import React, { useEffect } from "react"; +import { useDispatch, useSelector } from "react-redux"; +import { getAllCommissions } from "../../actions/CommissionActions"; +import { ICommissionState } from "../../reducers/CommissionReducer"; +import Translate from "../../translate/Translate"; +import { IAppState } from "../../store/Store"; +import TabbedCommissionView from "../../components/commissions/TabbedCommissionView"; + +const CommissionPage: React.FC = () => { + const dispatch = useDispatch(); + + const commissionState = useSelector( + (state) => state.commissionState + ); + + const commissions = commissionState.commissions.data ?? []; + + useEffect(() => { + dispatch(getAllCommissions()); + }, []); + + return ( +
+

{Translate.Get.containers.commissionPage.title}

+ +
+ ); +}; + +export default CommissionPage; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/containers/dashboard/BusinessLineDashboard.tsx b/LCU.WebApps.Operations.IncentiveManager/src/containers/dashboard/BusinessLineDashboard.tsx new file mode 100644 index 0000000..459f96f --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/containers/dashboard/BusinessLineDashboard.tsx @@ -0,0 +1,94 @@ +import React, { useEffect, useState } from "react"; +import { IDashboardArgs } from "./DashboardPage"; +import { SettingsContext } from "../../shared/providers/SettingsProvider"; +import IAsyncStateContainer from "../../shared/types/IAsyncStateContainer"; +import { IBusinessLineProgress } from "../../shared/models/Activity"; +import AsyncState from "../../shared/enumerations/AsyncState"; +import ActivityService from "../../shared/services/ActivityService"; +import AsyncGuard from "../shared/AsyncGuard"; +import { Alert, Form, Input } from "reactstrap"; +import MultiDepartmentProgressDisplay from "../../components/department-progress/MultiDepartmentProgressDisplay"; +import BusinessLineSummaryTable from "./BusinessLineSummaryTable"; + +const BusinessLineDashboard: React.FC = (props) => { + const settings = React.useContext(SettingsContext); + const [blProgress, setBlProgress] = useState< + IAsyncStateContainer + >({ state: AsyncState.Inactive }); + const [selectedBl, setSelectedBl] = useState("ALL"); + const range = props.range; + const allBl = settings?.businessLines || []; + + useEffect(() => { + setBlProgress({ state: AsyncState.Loading }); + retrieveProgress(); + }, [ + range.toMonth, + range.toYear, + range.fromMonth, + range.fromYear, + selectedBl + ]); + + const retrieveProgress = () => { + if (range) { + ActivityService.getBusinessProgress(range, selectedBl) + .then((response) => { + setBlProgress(response); + }) + .catch(() => { + setBlProgress({ state: AsyncState.Failure }); + }); + } + }; + + return ( + + {blProgress.data ? ( + <> +
+ { + setSelectedBl(event.target.value); + }} + > + + {allBl.map((bl) => { + return ( + + ); + })} + +
+ {selectedBl === "ALL" ? ( + + ) : ( + + )} + + ) : ( + + Unable to load your business information. + + )} +
+ ); +}; + +export default BusinessLineDashboard; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/containers/dashboard/BusinessLineSummaryTable.tsx b/LCU.WebApps.Operations.IncentiveManager/src/containers/dashboard/BusinessLineSummaryTable.tsx new file mode 100644 index 0000000..48af81a --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/containers/dashboard/BusinessLineSummaryTable.tsx @@ -0,0 +1,57 @@ +import React from "react"; +import ActivityService from "../../shared/services/ActivityService"; +import { + IBusinessLineProgress, + IProgressTotal +} from "../../shared/models/Activity"; +import ProductTableHeader from "../../components/shared/ProductTableHeader"; +import ProgressTableRow from "../../components/shared/ProgressTableRow"; +import { IBusinessLine } from "../../shared/models/Settings"; +import ProgressPercentageTableRow from "../../components/shared/ProgressPercentageTableRow"; + +export interface IRegionSummaryArgs { + blProgress: IBusinessLineProgress[]; +} + +const BusinessLineSummaryTable: React.FC = (props) => { + const blTotals: IProgressTotal[] = []; + const bls: IBusinessLine[] = []; + props.blProgress.forEach((bl) => { + const totals = ActivityService.calculateTotalProgress(bl.departments); + blTotals.push(totals); + bls.push(bl.businessLine); + }); + + const allBusinessLineTotal = ActivityService.joinTotalProgress(blTotals); + + return ( + + + + {blTotals.map((eachTotal, idx) => ( + + ))} + + + +
+ ); +}; + +export default BusinessLineSummaryTable; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/containers/dashboard/DashboardPage.tsx b/LCU.WebApps.Operations.IncentiveManager/src/containers/dashboard/DashboardPage.tsx new file mode 100644 index 0000000..9c8de5e --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/containers/dashboard/DashboardPage.tsx @@ -0,0 +1,95 @@ +import React, { useState } from "react"; +import { IAppState } from "../../store/Store"; +import { useSelector } from "react-redux"; +import { IProfileState } from "../../reducers/ProfileReducer"; +import ProfileService from "../../shared/services/ProfileService"; +import AsyncGuard from "../shared/AsyncGuard"; +import PermissionCode from "../../shared/enumerations/PermissionCode"; +import DepartmentDashboard from "./DepartmentDashboard"; +import EmployeeDashboard from "./EmployeeDashboard"; +import EmptyDashboard from "./EmptyDashboard"; +import { IEmployee } from "../../shared/models/Employee"; +import { useLocation } from "react-router-dom"; +import RegionDashboard from "./RegionDashboard"; +import BusinessLineDashboard from "./BusinessLineDashboard"; +import GoalProgressControls from "../../components/goal/GoalProgressControls"; +import { IMonthRange } from "../../shared/types/IMonthRange"; +import moment from "moment"; + +export interface IDashboardArgs { + range: IMonthRange; +} + +const DashboardPage: React.FC = () => { + const [range, setRange] = useState({ + fromMonth: moment().month() + 1, + fromYear: moment().year(), + toMonth: moment().month() + 1, + toYear: moment().year() + }); + const profileState = useSelector( + (state) => state.profileState + ); + const myProfile = profileState.profile; + const search = useLocation().search; + const view = new URLSearchParams(search).get("dashboard"); + + if (!myProfile || !myProfile.data) { + return ; + } + + const noParamFinder = () => { + const permission = ProfileService.highestRole( + myProfile.data as IEmployee + ); + + switch (permission) { + case PermissionCode.VIEW_PERFORMANCE_FOR_REGION: + return ; + case PermissionCode.VIEW_PERFORMANCE_FOR_BUSINESS_LINE: + return ; + case PermissionCode.VIEW_PERFORMANCE_FOR_DEPARTMENT: + return ; + case PermissionCode.VIEW_DASHBOARD: + return ; + case PermissionCode.UNKNOWN: + default: + return ; + } + }; + + const paramFinder = (param: string) => { + switch (param) { + case "region": + return ; + case "business": + return ; + case "department": + return ; + case "employee": + return ; + case "empty": + default: + return ; + } + }; + const onGoalProgressRangeChange = (range: IMonthRange) => { + setRange(range); + }; + + const selectedDashboard = view ? paramFinder(view) : noParamFinder(); + return ( +
+ + <> + + {selectedDashboard} + + +
+ ); +}; + +export default DashboardPage; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/containers/dashboard/DepartmentDashboard.tsx b/LCU.WebApps.Operations.IncentiveManager/src/containers/dashboard/DepartmentDashboard.tsx new file mode 100644 index 0000000..f7ee8f5 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/containers/dashboard/DepartmentDashboard.tsx @@ -0,0 +1,62 @@ +import React, { useEffect, useState } from "react"; +import { IDepartmentProgress } from "../../shared/models/Activity"; +import AsyncState from "../../shared/enumerations/AsyncState"; +import TabbedDepartmentProgressView from "../../components/department-progress/TabbedDepartmentProgressView"; +import { IDashboardArgs } from "./DashboardPage"; +import IAsyncStateContainer from "../../shared/types/IAsyncStateContainer"; +import ActivityService from "../../shared/services/ActivityService"; +import AsyncGuard from "../shared/AsyncGuard"; +import { useLocation } from "react-router-dom"; + +const DepartmentDashboard: React.FC = (props) => { + const range = props.range; + const [myProgress, setMyProgress] = useState< + IAsyncStateContainer + >({ state: AsyncState.Loading }); + const search = useLocation().search; + const departmentId = + new URLSearchParams(search).get("departmentId") || undefined; + + useEffect(() => { + setMyProgress({ state: AsyncState.Loading }); + retrieveProgress(); + }, [ + range.toMonth, + range.toYear, + range.fromMonth, + range.fromYear, + departmentId + ]); + + const retrieveProgress = () => { + if (range) { + ActivityService.getMyDepartmentProgress(range, departmentId) + .then((response) => { + setMyProgress(response); + }) + .catch(() => { + setMyProgress({ state: AsyncState.Failure }); + }); + } + }; + + return ( + + {myProgress.data && myProgress.data.length > 0 ? ( + <> + {myProgress.data.map((progressData) => { + return ( + + ); + })} + + ) : ( + <> + )} + + ); +}; + +export default DepartmentDashboard; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/containers/dashboard/EmployeeDashboard.tsx b/LCU.WebApps.Operations.IncentiveManager/src/containers/dashboard/EmployeeDashboard.tsx new file mode 100644 index 0000000..4030b31 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/containers/dashboard/EmployeeDashboard.tsx @@ -0,0 +1,60 @@ +import React, { useEffect, useState } from "react"; +import AsyncState from "../../shared/enumerations/AsyncState"; +import TabbedProgressView from "../../components/goal/TabbedProgressView"; +import { IDashboardArgs } from "./DashboardPage"; +import ActivityService from "../../shared/services/ActivityService"; +import IAsyncStateContainer from "../../shared/types/IAsyncStateContainer"; +import { IEmployeeProgress } from "../../shared/models/Activity"; +import AsyncGuard from "../shared/AsyncGuard"; +import { useLocation } from "react-router-dom"; +import UserDisplay from "../shared/UserDisplay"; + +const EmployeeDashboard: React.FC = (props) => { + const range = props.range; + const [myProgress, setMyProgress] = useState< + IAsyncStateContainer + >({ state: AsyncState.Loading }); + const search = useLocation().search; + const employeeId = + new URLSearchParams(search).get("employeeId") || undefined; + + useEffect(() => { + setMyProgress({ state: AsyncState.Loading }); + retrieveProgress(); + }, [ + range.toMonth, + range.toYear, + range.fromMonth, + range.fromYear, + employeeId + ]); + + const retrieveProgress = () => { + if (range) { + ActivityService.getMyProgress(range, employeeId) + .then((response) => { + setMyProgress(response); + }) + .catch(() => { + setMyProgress({ state: AsyncState.Failure }); + }); + } + }; + + return ( + +
+ {employeeId ? ( +

+ +

+ ) : ( + "" + )} + +
+
+ ); +}; + +export default EmployeeDashboard; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/containers/dashboard/EmptyDashboard.test.tsx b/LCU.WebApps.Operations.IncentiveManager/src/containers/dashboard/EmptyDashboard.test.tsx new file mode 100644 index 0000000..a949ef7 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/containers/dashboard/EmptyDashboard.test.tsx @@ -0,0 +1,10 @@ +import React from "react"; +import { shallow } from "enzyme"; +import EmptyDashboard from "./EmptyDashboard"; + +describe("A Component For displaying an empty dashboard", () => { + it("Displays a basic component", () => { + const wrapper = shallow(); + expect(wrapper.find("[data-test='empty-dashboard']").length).toBe(1); + }); +}); diff --git a/LCU.WebApps.Operations.IncentiveManager/src/containers/dashboard/EmptyDashboard.tsx b/LCU.WebApps.Operations.IncentiveManager/src/containers/dashboard/EmptyDashboard.tsx new file mode 100644 index 0000000..c7e1fa0 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/containers/dashboard/EmptyDashboard.tsx @@ -0,0 +1,15 @@ +import React from "react"; +import { Alert } from "reactstrap"; + +const EmptyDashboard: React.FC = () => { + return ( +
+ + Sorry, you currently don’t have permission to access this + application. Contact IT if you require access. + +
+ ); +}; + +export default EmptyDashboard; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/containers/dashboard/RegionDashboard.tsx b/LCU.WebApps.Operations.IncentiveManager/src/containers/dashboard/RegionDashboard.tsx new file mode 100644 index 0000000..17d9a43 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/containers/dashboard/RegionDashboard.tsx @@ -0,0 +1,97 @@ +import React, { useEffect, useState } from "react"; +import ActivityService from "../../shared/services/ActivityService"; +import { IRegionProgress } from "../../shared/models/Activity"; +import IAsyncStateContainer from "../../shared/types/IAsyncStateContainer"; +import AsyncState from "../../shared/enumerations/AsyncState"; +import AsyncGuard from "../shared/AsyncGuard"; +import MultiDepartmentProgressDisplay from "../../components/department-progress/MultiDepartmentProgressDisplay"; +import { Alert, Form, Input } from "reactstrap"; +import { IDashboardArgs } from "./DashboardPage"; +import { SettingsContext } from "../../shared/providers/SettingsProvider"; +import RegionSummaryTable from "./RegionSummaryTable"; + +const RegionDashboard: React.FC = (props) => { + const settings = React.useContext(SettingsContext); + const [regionProgress, setRegionProgress] = useState< + IAsyncStateContainer + >({ state: AsyncState.Inactive }); + const [selectedRegion, setSelectedRegion] = useState("ALL"); + const range = props.range; + const allRegions = settings?.regions || []; + + useEffect(() => { + setRegionProgress({ state: AsyncState.Loading }); + retrieveProgress(); + }, [ + range.toMonth, + range.toYear, + range.fromMonth, + range.fromYear, + selectedRegion + ]); + + const retrieveProgress = () => { + if (range) { + ActivityService.getRegionProgress(range, selectedRegion) + .then((response) => { + setRegionProgress(response); + }) + .catch(() => { + setRegionProgress({ state: AsyncState.Failure }); + }); + } + }; + + return ( + + {regionProgress.data ? ( + <> +
+ { + setSelectedRegion(event.target.value); + }} + > + + {allRegions.map((region) => { + return ( + + ); + })} + +
+ {selectedRegion === "ALL" ? ( + + ) : ( + + )} + + ) : ( + + Unable to load your region information. + + )} +
+ ); +}; + +export default RegionDashboard; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/containers/dashboard/RegionSummaryTable.tsx b/LCU.WebApps.Operations.IncentiveManager/src/containers/dashboard/RegionSummaryTable.tsx new file mode 100644 index 0000000..02eea7a --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/containers/dashboard/RegionSummaryTable.tsx @@ -0,0 +1,54 @@ +import React from "react"; +import ActivityService from "../../shared/services/ActivityService"; +import { IProgressTotal, IRegionProgress } from "../../shared/models/Activity"; +import ProductTableHeader from "../../components/shared/ProductTableHeader"; +import ProgressTableRow from "../../components/shared/ProgressTableRow"; +import { IRegion } from "../../shared/models/Settings"; +import ProgressPercentageTableRow from "../../components/shared/ProgressPercentageTableRow"; + +export interface IRegionSummaryArgs { + regionProgress: IRegionProgress[]; +} +const RegionSummaryTable: React.FC = (props) => { + const regionTotals: IProgressTotal[] = []; + const regions: IRegion[] = []; + props.regionProgress.forEach((region) => { + const totals = ActivityService.calculateTotalProgress( + region.departments + ); + regionTotals.push(totals); + regions.push(region.region); + }); + + const allRegionTotal = ActivityService.joinTotalProgress(regionTotals); + + return ( + + + + {regionTotals.map((eachTotal, idx) => ( + + ))} + + + +
+ ); +}; + +export default RegionSummaryTable; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/containers/dead-activity-page/DeadActivityPage.test.tsx b/LCU.WebApps.Operations.IncentiveManager/src/containers/dead-activity-page/DeadActivityPage.test.tsx new file mode 100644 index 0000000..6a758bf --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/containers/dead-activity-page/DeadActivityPage.test.tsx @@ -0,0 +1,41 @@ +import { shallow } from "enzyme"; +import * as React from "react"; +import * as reactRedux from "react-redux"; +import DeadActivityPage from "./DeadActivityPage"; +import { IDeadActivityState } from "../../reducers/DeadActivityReducer"; +import AsyncState from "../../shared/enumerations/AsyncState"; +import DeadActivityAuditLog from "../../components/dead-activity/DeadActivityAuditLog"; +import DeadActivityPageFilterForm from "./DeadActivityPageFilterForm"; + +describe("A component for finding activities", () => { + const useDispatchMock = jest.spyOn(reactRedux, "useDispatch"); + const useSelectorMock = jest.spyOn(reactRedux, "useSelector"); + const useEffectMock = jest.spyOn(React, "useEffect"); + + beforeEach(() => { + useSelectorMock.mockClear(); + useDispatchMock.mockClear(); + useEffectMock.mockClear(); + }); + + it("Displays a blank search form", () => { + const dummyDispatch = jest.fn(); + const mockActivities: IDeadActivityState = { + filteredDeadActivities: { + state: AsyncState.Inactive + }, + deadActivityStateFilter: [] + }; + useDispatchMock.mockReturnValue(dummyDispatch); + useSelectorMock.mockReturnValue(mockActivities); + const wrapper = shallow(); + + const found = wrapper.find(DeadActivityPageFilterForm); + expect(found.length).toBe(1); + + const found2 = wrapper.find(DeadActivityAuditLog); + expect(found2.length).toBe(1); + + expect(dummyDispatch).not.toHaveBeenCalled(); + }); +}); diff --git a/LCU.WebApps.Operations.IncentiveManager/src/containers/dead-activity-page/DeadActivityPage.tsx b/LCU.WebApps.Operations.IncentiveManager/src/containers/dead-activity-page/DeadActivityPage.tsx new file mode 100644 index 0000000..4ee997f --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/containers/dead-activity-page/DeadActivityPage.tsx @@ -0,0 +1,78 @@ +import React from "react"; +import { IAppState } from "../../store/Store"; +import { useDispatch, useSelector } from "react-redux"; +import Configuration from "./../../shared/configuration/Configuration"; +import Translate from "../../translate/Translate"; +import { debounce } from "debounce"; +import { IDeadActivitySearchForm } from "../../shared/models/DeadActivity"; +import { + getAllDeadActivities, + getLastPageOfDeadActivities, + getNextPageOfDeadActivities +} from "../../actions/DeadActivityActions"; +import DeadActivityPageFilterForm from "./DeadActivityPageFilterForm"; +import DeadActivityAuditLog from "../../components/dead-activity/DeadActivityAuditLog"; +import { IDeadActivityState } from "../../reducers/DeadActivityReducer"; + +const ActivityAuditLogTranslate = + Translate.Get.components.deadActivity.deadActivityAuditLog; + +const DeadActivityPage: React.FC = () => { + const dispatch = useDispatch(); + + const [form, setForm] = React.useState({ + state: [], + dateHigh: null, + dateLow: null, + query: "", + product: "", + type: "" + }); + + const activityState = useSelector( + (state) => state.deadActivityState + ); + + React.useEffect(() => { + search(form); + }, [form]); + + const search = React.useCallback( + debounce((query: IDeadActivitySearchForm) => { + dispatch( + getAllDeadActivities( + 1, + Configuration.Get.defaultPaginationLimit, + query + ) + ); + }, 500), + [] + ); + + const changeSort = (headerName: string) => { + setForm({ + ...form, + sortBy: headerName, + sortDescending: + headerName === form.sortBy ? !form.sortDescending : false + }); + }; + + return ( +
+
{ActivityAuditLogTranslate.auditTableTitle}
+ + dispatch(getNextPageOfDeadActivities(form))} + getPrevPage={() => dispatch(getLastPageOfDeadActivities(form))} + refresh={() => search(form)} + changeSort={changeSort} + form={form} + /> +
+ ); +}; + +export default DeadActivityPage; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/containers/dead-activity-page/DeadActivityPageFilterForm.test.tsx b/LCU.WebApps.Operations.IncentiveManager/src/containers/dead-activity-page/DeadActivityPageFilterForm.test.tsx new file mode 100644 index 0000000..683bdc1 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/containers/dead-activity-page/DeadActivityPageFilterForm.test.tsx @@ -0,0 +1,67 @@ +import React from "react"; +import { mount } from "enzyme"; +import DeadActivityPageFilterForm from "./DeadActivityPageFilterForm"; +import { IDeadActivitySearchForm } from "../../shared/models/DeadActivity"; +import { FormGroup } from "reactstrap"; + +describe("ActivePageFilterForm", () => { + it("should display form correctly", () => { + let testCalls = 0; + const testCall = (filter: IDeadActivitySearchForm) => { + testCalls++; + }; + const wrapper = mount( + + ); + + expect(wrapper.find(FormGroup).length).toEqual(7); + expect(testCalls).toEqual(1); + }); + + it("should respond to changes correctly", () => { + let testCalls = 0; + let lastFilter: IDeadActivitySearchForm = { + query: "", + dateHigh: null, + dateLow: null, + state: [], + product: "", + type: "", + employeeId: "", + departmentId: 0 + }; + const testCall = (filter: IDeadActivitySearchForm) => { + lastFilter = filter; + testCalls++; + }; + + const wrapper = mount( + + ); + expect(wrapper.find(FormGroup).length).toEqual(7); + + wrapper + .find("[name='query']") + .first() + .simulate("change", { target: { value: "3" } }); + expect(testCalls).toEqual(2); + expect(lastFilter.query).toEqual("3"); + + wrapper + .find("[name='ActivityProduct']") + .first() + .simulate("change", { target: { value: "Vehicle" } }); + expect(testCalls).toEqual(3); + expect(lastFilter.product).toEqual("Vehicle"); + expect(lastFilter.query).toEqual("3"); + + wrapper + .find("[name='ActivityType']") + .first() + .simulate("change", { target: { value: "Sale" } }); + expect(testCalls).toEqual(4); + expect(lastFilter.type).toEqual("Sale"); + expect(lastFilter.product).toEqual("Vehicle"); + expect(lastFilter.query).toEqual("3"); + }); +}); diff --git a/LCU.WebApps.Operations.IncentiveManager/src/containers/dead-activity-page/DeadActivityPageFilterForm.tsx b/LCU.WebApps.Operations.IncentiveManager/src/containers/dead-activity-page/DeadActivityPageFilterForm.tsx new file mode 100644 index 0000000..86bb372 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/containers/dead-activity-page/DeadActivityPageFilterForm.tsx @@ -0,0 +1,237 @@ +import React, { useEffect, useState } from "react"; +import { FormFeedback, FormGroup, Input, Label } from "reactstrap"; +import { SettingsContext } from "../../shared/providers/SettingsProvider"; +import moment, { Moment } from "moment"; +import Translate from "../../translate/Translate"; +import { + ActivityType, + IActivitySearchForm +} from "../../shared/models/Activity"; +import UserSearchSelect from "../../components/activity/manual-activity-wizard/UserSearchSelect"; + +interface IDeadActivityPageFilterForm { + filterResults: (filter: IActivitySearchForm) => void; +} + +const activityFilterFormTranslate = + Translate.Get.components.deadActivity.deadActivityFilterForm; + +const DeadActivityPageFilterForm: React.FC = ({ + filterResults +}) => { + const [form, setForm] = useState({ + state: [], + dateHigh: null, + dateLow: null, + query: "", + product: "", + type: "", + departmentId: 0, + employeeId: "" + }); + + const settings = React.useContext(SettingsContext); + useEffect(() => { + filterResults(form); + }, [form]); + const departments = settings?.departments || []; + + const updateProduct = (newProduct: string) => { + setForm({ + ...form, + product: newProduct + }); + }; + + const updateType = (newType: string) => { + setForm({ + ...form, + type: newType + }); + }; + + const setSearchQuery = (newQuery: string) => { + setForm({ + ...form, + query: newQuery + }); + }; + + const updateLowDate = (date: Moment) => { + setForm({ + ...form, + dateLow: date + }); + }; + + const updateHighDate = (date: Moment) => { + setForm({ + ...form, + dateHigh: date + }); + }; + + const updateDepartment = (departmentId: number) => { + setForm({ + ...form, + departmentId: departmentId + }); + }; + + const updateEmployee = (employeeId?: string) => { + setForm({ + ...form, + employeeId: employeeId + }); + }; + + return ( +
+ + + { + updateProduct(event.target.value); + }} + > + + {settings?.productDetails.map((product) => { + return ( + + ); + })} + + + + + { + updateType(event.target.value); + }} + > + + {Object.values(ActivityType).map((activityType) => { + return ( + + ); + })} + + + + + { + updateDepartment(parseInt(event.target.value, 10)); + }} + > + + {departments.map((department) => { + return ( + + ); + })} + + Please select a value. + + + + { + updateEmployee(value); + }} + showValidation={false} + placeHolder={ + activityFilterFormTranslate.employeeId.placeHolder + } + /> + + + + { + updateLowDate(moment.utc(event.target.value)); + }} + /> + + DeptAssignmentTranslate.validation.endDate + + + + + { + updateHighDate(moment.utc(event.target.value)); + }} + /> + + DeptAssignmentTranslate.validation.endDate + + + + + { + setSearchQuery(event.target.value); + }} + type="text" + placeholder={ + activityFilterFormTranslate.searchQuery.placeHolder + } + /> + +
+ ); +}; + +export default DeadActivityPageFilterForm; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/containers/department-detail-page/DepartmentDetailPage.test.tsx b/LCU.WebApps.Operations.IncentiveManager/src/containers/department-detail-page/DepartmentDetailPage.test.tsx new file mode 100644 index 0000000..1c3f8ac --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/containers/department-detail-page/DepartmentDetailPage.test.tsx @@ -0,0 +1,135 @@ +import React from "react"; +import { mount } from "enzyme"; +import ReactRouter from "react-router"; +import DepartmentDetailPage from "./DepartmentDetailPage"; +import Translate from "../../translate/Translate"; +import { getMockAppSettings } from "../../shared/testing/mocks/MockAppSettings"; +import { getMockDepartments } from "../../shared/testing/mocks/MockDepartments"; +import DepartmentService from "../../shared/services/DepartmentService"; +import AsyncState from "../../shared/enumerations/AsyncState"; +import { act } from "react-dom/test-utils"; +import PermissionCode from "../../shared/enumerations/PermissionCode"; +import moment from "moment"; + +const pageTranslate = Translate.Get.containers.departmentDetailPage; +const useContextMock = jest.spyOn(React, "useContext"); + +beforeEach(() => { + useContextMock.mockClear(); +}); + +describe("DepartmentDetailPage", () => { + it("should display unavailable", async () => { + jest.spyOn(ReactRouter, "useParams").mockReturnValue({ id: "9999999" }); + jest.spyOn(DepartmentService, "getDepartmentDetails").mockResolvedValue( + { + state: AsyncState.Success, + data: { + assignments: [] + } + } + ); + let wrapper: any; + await act(async () => { + wrapper = mount(); + }); + + expect(wrapper.find("[data-test='department-name']").length).toEqual(0); + expect(wrapper.html()).toEqual(pageTranslate.unavailable); + }); + + it("should display the department details", async () => { + jest.spyOn(ReactRouter, "useParams").mockReturnValue({ id: "55" }); + jest.spyOn(DepartmentService, "getDepartmentDetails").mockResolvedValue( + { + state: AsyncState.Success, + data: { + assignments: [] + } + } + ); + let wrapper: any; + const settings = getMockAppSettings(); + settings.departments = getMockDepartments(); + useContextMock.mockReturnValue(settings); + + await act(async () => { + wrapper = mount(); + }); + wrapper.update(); + expect( + wrapper.find("[data-test='department-detail-display']").length + ).toEqual(1); + }); + + it("should display personnel", async () => { + jest.spyOn(ReactRouter, "useParams").mockReturnValue({ id: "55" }); + const manager = mockUser(); + const departmentAssignment = { + departmentId: 55, + role: { + name: "Manager", + description: "manager", + permissions: [], + hasCommissions: false + }, + employee: manager, + employeeId: manager.employeeId, + isHome: true, + note: "Manager", + slidingPayout: 100, + startDate: moment() + }; + manager.departments; + jest.spyOn(DepartmentService, "getDepartmentDetails").mockResolvedValue( + { + state: AsyncState.Success, + data: { + assignments: [departmentAssignment] + } + } + ); + let wrapper: any; + const settings = getMockAppSettings(); + settings.departments = getMockDepartments(); + useContextMock.mockReturnValue(settings); + + await act(async () => { + wrapper = mount(); + }); + wrapper.update(); + expect( + wrapper.find("[data-test='no-department-detail-assignments']") + .length + ).toEqual(1); + + expect( + wrapper.find("[data-test='department-detail-assignments']").length + ).toEqual(1); + }); +}); + +const mockUser = () => { + return { + adEmail: "test@test.com", + personId: 11, + employeeId: "11771", + homeDepartment: "HOME", + baseRole: "QA", + fullName: "John Tester", + role: { + name: "Admin", + description: "admin", + permissions: [ + { + code: PermissionCode.ASSIGN_ASSOCIATES_TO_DEPARTMENT, + description: "ASSIGN_ASSOCIATES_TO_DEPARTMENT" + } + ], + hasCommissions: false + }, + additionalPermissions: [], + departments: [], + assignments: [] + }; +}; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/containers/department-detail-page/DepartmentDetailPage.tsx b/LCU.WebApps.Operations.IncentiveManager/src/containers/department-detail-page/DepartmentDetailPage.tsx new file mode 100644 index 0000000..5f6470e --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/containers/department-detail-page/DepartmentDetailPage.tsx @@ -0,0 +1,86 @@ +import React, { useEffect, useState } from "react"; +import { useParams } from "react-router-dom"; +import Translate from "../../translate/Translate"; +import { SettingsContext } from "../../shared/providers/SettingsProvider"; +import IAsyncStateContainer from "../../shared/types/IAsyncStateContainer"; +import { IDepartmentDetails } from "../../shared/models/Department"; +import AsyncState from "../../shared/enumerations/AsyncState"; +import DepartmentService from "../../shared/services/DepartmentService"; +import DepartmentDetailDisplay from "../../components/department-detail/DepartmentDetailDisplay"; +import DepartmentDetailAssignments from "../../components/department-detail/DepartmentDetailAssignments"; +import ProfileService from "../../shared/services/ProfileService"; +import { Col, Row } from "reactstrap"; +import DepartmentDisplay, { + DepartmentDisplayTargets +} from "../shared/DepartmentDisplay"; + +const pageTranslate = Translate.Get.containers.departmentDetailPage; + +const DepartmentDetailPage: React.FC = () => { + const [details, setDetails] = useState< + IAsyncStateContainer + >({ state: AsyncState.Inactive }); + + const settings = React.useContext(SettingsContext); + const { id } = useParams<{ id: string }>(); + const departments = settings?.departments; + const department = departments?.find((x) => x.id.toString() === id); + + useEffect(() => { + loadDepartments(); + }, []); + + const loadDepartments = async () => { + setDetails({ state: AsyncState.Loading }); + const details = await DepartmentService.getDepartmentDetails(+id); + setDetails(details); + }; + + if (!department) { + return <>{pageTranslate.unavailable}; + } + + const managers = details.data?.assignments.filter((x) => + ProfileService.isDepartmentManager(x.employee, department.id) + ); + const personnel = details.data?.assignments.filter( + (x) => !ProfileService.isDepartmentManager(x.employee, department.id) + ); + + return ( + <> +

+ {pageTranslate.title} - {department.departmentName} +

+ + +
+
{pageTranslate.details}   
+
+ +
+
+ {details.data && ( + + )} + + +
{pageTranslate.managers}
+ + +
{pageTranslate.personnel}
+ + +
+ + ); +}; + +export default DepartmentDetailPage; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/containers/employee-page/EmployeeDetailsEditAlertBar.tsx b/LCU.WebApps.Operations.IncentiveManager/src/containers/employee-page/EmployeeDetailsEditAlertBar.tsx new file mode 100644 index 0000000..fddc0ab --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/containers/employee-page/EmployeeDetailsEditAlertBar.tsx @@ -0,0 +1,49 @@ +import React from "react"; +import { Alert, Spinner } from "reactstrap"; +import IAsyncStateContainer from "../../shared/types/IAsyncStateContainer"; +import AsyncState from "./../../shared/enumerations/AsyncState"; + +export interface IEmployeeDetailsEditAlertBarProps { + submitStatusMessage: string; + submitState: IAsyncStateContainer; +} + +const EmployeeDetailsEditAlertBar: React.FC = ( + props +) => { + if (props.submitState.state == AsyncState.Success) { + return ( + +

{props.submitStatusMessage}

+
+ ); + } + + if (props.submitState.state == AsyncState.Loading) { + return ( + + +

{props.submitStatusMessage}

+
+ ); + } + + if (props.submitState.state == AsyncState.Failure) { + return ( + + +

{props.submitStatusMessage}

+
+ ); + } + + return
; +}; + +export default EmployeeDetailsEditAlertBar; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/containers/employee-page/EmployeeDetailsEditPage.tsx b/LCU.WebApps.Operations.IncentiveManager/src/containers/employee-page/EmployeeDetailsEditPage.tsx new file mode 100644 index 0000000..9a84e0c --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/containers/employee-page/EmployeeDetailsEditPage.tsx @@ -0,0 +1,129 @@ +import React from "react"; +import { Button, ButtonGroup, Input } from "reactstrap"; +import Translate from "../../translate/Translate"; +import { + IEmployee, + IEmployeeDetailsUpdate +} from "../../shared/models/Employee"; +import { tryUpdateStateWithInput } from "../../shared/services/InputService"; + +export interface IEmployeeDetailsEditProps { + profile: IEmployee; + employeeDetailsUpdateResult: string; + formState: IEmployeeDetailsUpdate; + submitEmployeeDetailsUpdate: (update: IEmployeeDetailsUpdate) => void; + disableEdit: () => void; + updateFormState: (newState: IEmployeeDetailsUpdate) => void; +} + +const profileTranslate = Translate.Get.components.profileEdit; + +const EmployeeDetailsEdit: React.FC = (props) => { + const submitForm = () => { + props.submitEmployeeDetailsUpdate(props.formState); + props.disableEdit(); + }; + + return ( +
+
{profileTranslate.header}
+ + + + + + + + + + + + + + + +
{profileTranslate.employeeEmail} + { + props.updateFormState( + tryUpdateStateWithInput( + props.formState, + "newEmail", + event.target.value + ) + ); + }} + type="text" + placeholder={props.formState.newEmail} + /> +
{profileTranslate.employeeId} + { + props.updateFormState( + tryUpdateStateWithInput( + props.formState, + "newEmployeeId", + event.target.value + ) + ); + }} + type="text" + placeholder={props.formState.newEmployeeId} + /> +
{profileTranslate.employeeFullName} + { + props.updateFormState( + tryUpdateStateWithInput( + props.formState, + "newFullName", + event.target.value + ) + ); + }} + type="text" + placeholder={props.formState.newFullName} + /> +
+
+ + + + +
+ ); +}; + +export default EmployeeDetailsEdit; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/containers/employee-page/EmployeeDetailsPage.tsx b/LCU.WebApps.Operations.IncentiveManager/src/containers/employee-page/EmployeeDetailsPage.tsx new file mode 100644 index 0000000..a71a13b --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/containers/employee-page/EmployeeDetailsPage.tsx @@ -0,0 +1,109 @@ +import React, { useState } from "react"; +import { IEmployee } from "../../shared/models/Employee"; +import Translate from "../../translate/Translate"; +import { Button, FormFeedback } from "reactstrap"; +import { IEmployeeDetailsUpdate } from "../../shared/models/Employee"; +import EmployeeDetailsEdit from "./EmployeeDetailsEditPage"; +import EmployeeDetailsEditAlertBar from "./EmployeeDetailsEditAlertBar"; +import IAsyncStateContainer from "../../shared/types/IAsyncStateContainer"; + +export interface IEmployeeDetailsPageParams { + profile: IEmployee; + employeeDetailsUpdateResult: string; + submitStatus: IAsyncStateContainer; + refreshProfile: () => void; + submitEmployeeDetailsUpdate: (update: IEmployeeDetailsUpdate) => void; +} + +const profileTranslate = Translate.Get.components.profileEdit; + +const EmployeeDetailsPage: React.FC = (props) => { + const profile = props.profile; + + const createFormStateFromAssignment = ( + employee: IEmployee + ): IEmployeeDetailsUpdate => { + return { + oldEmail: employee.adEmail, + oldEmployeeId: employee.employeeId, + oldFullName: employee.fullName, + newEmail: employee.adEmail, + newEmployeeId: employee.employeeId, + newFullName: employee.fullName + }; + }; + + const initialFormState = createFormStateFromAssignment(props.profile); + const [isEditing, setEdit] = useState(false); + const [formState, setFormState] = useState( + initialFormState + ); + + const enableEdit = () => { + setFormState(createFormStateFromAssignment(props.profile)); + setEdit(true); + }; + + if (!isEditing) { + return ( +
+
{profileTranslate.header}
+ {props.employeeDetailsUpdateResult} + + + + + + + + + + + + + + + + + + + +
{profileTranslate.employeeEmail}{profile.adEmail}
{profileTranslate.employeeId}{profile.employeeId}
{profileTranslate.employeeFullName}{profile.fullName}
+ + + +
+
+
+ ); + } + + return ( + setEdit(false)} + updateFormState={(newState) => setFormState(newState)} + data-test="edit-row" + /> + ); +}; + +export default EmployeeDetailsPage; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/containers/employee-page/EmployeePage.tsx b/LCU.WebApps.Operations.IncentiveManager/src/containers/employee-page/EmployeePage.tsx new file mode 100644 index 0000000..13dbd77 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/containers/employee-page/EmployeePage.tsx @@ -0,0 +1,90 @@ +import "../../components/tab-view/TabView.css"; +import React, { useEffect } from "react"; +import { useDispatch, useSelector } from "react-redux"; +import { + submitDepartmentAssignment, + submitDepartmentAssignmentUpdate, + trySelectEmployee +} from "../../actions/EmployeeActions"; +import { IEmployeeState } from "./../../reducers/EmployeeReducer"; +import { IAppState } from "./../../store/Store"; +import AsyncState from "./../../shared/enumerations/AsyncState"; +import EmployeeAlertBar from "./../../components/employee/EmployeeAlertBar"; +import DeptAssignmentTabGroup from "./department-assignments/DeptAssignmentTabGroup"; +import { + IDepartmentAssignmentDto, + IDepartmentAssignmentUpdateDto +} from "../../shared/models/DepartmentAssignment"; +import EmployeeService from "../../shared/services/EmployeeService"; + +export interface IEmployeeParam { + employeeId: string; +} + +const EmployeePage: React.FC = (props) => { + const employeeId = props.employeeId; + const dispatch = useDispatch(); + const employeeState = useSelector( + (state) => state.employeeState + ); + + useEffect(() => { + dispatch(trySelectEmployee(employeeId)); + }, []); + + const submitNewDeptAssignment = async ( + deptAssignment: Partial + ) => { + if (employeeState.selectedEmployee?.data) { + await dispatch( + submitDepartmentAssignment( + employeeState.selectedEmployee.data.employeeId, + deptAssignment + ) + ); + await dispatch(trySelectEmployee(employeeId)); + } + }; + + const submitDeptAssignmentUpdate = async ( + update: IDepartmentAssignmentUpdateDto + ) => { + update.employeeId = employeeId; + if ( + employeeState.selectedEmployee?.data && + EmployeeService.isValidDepartmentAssignmentUpdate(update) + ) { + await dispatch(submitDepartmentAssignmentUpdate(update)); + await dispatch(trySelectEmployee(employeeId)); + } + }; + + return ( +
+ +
+ + submitNewDeptAssignment(assignmentData) + } + submitDeptAssignmentUpdate={( + deptUpdate: IDepartmentAssignmentUpdateDto + ) => submitDeptAssignmentUpdate(deptUpdate)} + /> +
+
+ ); +}; + +export default EmployeePage; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/containers/employee-page/department-assignments/BasicDeptAssignmentTable.tsx b/LCU.WebApps.Operations.IncentiveManager/src/containers/employee-page/department-assignments/BasicDeptAssignmentTable.tsx new file mode 100644 index 0000000..d2b362d --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/containers/employee-page/department-assignments/BasicDeptAssignmentTable.tsx @@ -0,0 +1,66 @@ +import Translate from "../../../translate/Translate"; +import { useDispatch, useSelector } from "react-redux"; +import { IAppState } from "../../../store/Store"; +import { IDepartmentState } from "../../../reducers/DepartmentReducer"; +import { AutoTable } from "../../../components/auto-table/AutoTable"; +import React, { useEffect } from "react"; +import { getAllDepartments } from "../../../actions/DepartmentActions"; +import moment from "moment"; +import { IDepartmentAssignment } from "../../../shared/models/DepartmentAssignment"; + +export interface IDepartmentAssignmentTableProps { + assignments: IDepartmentAssignment[]; +} + +const departmentAssignmentTranslate = + Translate.Get.containers.employeePage.deptAssignment.deptAssignmentTable; + +const BasicDeptAssignmentTable: React.FC = ( + props +) => { + const dispatch = useDispatch(); + const departmentState = useSelector( + (state) => state.departmentState + ); + + const getDepartmentNameFromId = (id: number): string => { + return ( + departmentState.departments.data?.find((dept) => dept.id === id) + ?.departmentName ?? "-" + ); + }; + + useEffect(() => { + dispatch(getAllDepartments()); + }, []); + + const headers = [ + departmentAssignmentTranslate.headers.isHome, + departmentAssignmentTranslate.headers.departmentId, + departmentAssignmentTranslate.headers.note, + departmentAssignmentTranslate.headers.startDate, + departmentAssignmentTranslate.headers.endDate + ]; + + const cells = [ + (x: IDepartmentAssignment) => (x.isHome ? "Home Dept" : ""), + (x: IDepartmentAssignment) => getDepartmentNameFromId(x.departmentId), + (x: IDepartmentAssignment) => x.note, + (x: IDepartmentAssignment) => + moment(x.startDate).format("MM-DD-YYYY").toString(), + (x: IDepartmentAssignment) => + x.endDate + ? moment(x.endDate).format("MM-DD-YYYY").toString() + : "active" + ]; + return ( + + ); +}; +export default BasicDeptAssignmentTable; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/containers/employee-page/department-assignments/DeptAssignmentAlertBar.tsx b/LCU.WebApps.Operations.IncentiveManager/src/containers/employee-page/department-assignments/DeptAssignmentAlertBar.tsx new file mode 100644 index 0000000..3975f3d --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/containers/employee-page/department-assignments/DeptAssignmentAlertBar.tsx @@ -0,0 +1,54 @@ +import React from "react"; +import { Alert, Spinner } from "reactstrap"; +import IAsyncStateContainer from "../../../shared/types/IAsyncStateContainer"; +import AsyncState from "./../../../shared/enumerations/AsyncState"; +import Translate from "./../../../translate/Translate"; + +export interface IDeptAssignmentAlertBarProps { + submitState: IAsyncStateContainer; +} + +const alertBarTranslate = + Translate.Get.containers.employeePage.deptAssignment.deptAssignmentAlertBar; + +const DeptAssignmentAlertBar: React.FC = ( + props +) => { + if (props.submitState.state == AsyncState.Success) { + return ( + +

{alertBarTranslate.success}

+
+ ); + } + + if (props.submitState.state == AsyncState.Loading) { + return ( + + +

{alertBarTranslate.loading}

+
+ ); + } + + if (props.submitState.state == AsyncState.Failure) { + return ( + + +

+ {alertBarTranslate.error} {props.submitState.data} +

+
+ ); + } + + return
; +}; + +export default DeptAssignmentAlertBar; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/containers/employee-page/department-assignments/DeptAssignmentAuditTable.tsx b/LCU.WebApps.Operations.IncentiveManager/src/containers/employee-page/department-assignments/DeptAssignmentAuditTable.tsx new file mode 100644 index 0000000..d372d44 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/containers/employee-page/department-assignments/DeptAssignmentAuditTable.tsx @@ -0,0 +1,95 @@ +import Translate from "../../../translate/Translate"; +import { useDispatch, useSelector } from "react-redux"; +import { IAppState } from "../../../store/Store"; +import { IDepartmentState } from "../../../reducers/DepartmentReducer"; +import { AutoTable } from "../../../components/auto-table/AutoTable"; +import React, { useEffect } from "react"; +import { getAllDepartments } from "../../../actions/DepartmentActions"; +import moment from "moment"; +import HomeIcon from "./HomeIcon"; +import { IDepartmentAssignment } from "../../../shared/models/DepartmentAssignment"; + +export interface IDeptAssignmentAuditTable { + assignments: IDepartmentAssignment[]; +} + +const departmentAssignmentTranslate = + Translate.Get.containers.employeePage.deptAssignment + .deptAssignmentAuditTable; + +const DeptAssignmentAuditTable: React.FC = ( + props +) => { + const dispatch = useDispatch(); + const departmentState = useSelector( + (state) => state.departmentState + ); + + const getDepartmentNameFromId = (id: number): string => { + return ( + departmentState.departments.data?.find((dept) => dept.id === id) + ?.departmentName ?? "-" + ); + }; + + const sortDataAudits = ( + a: IDepartmentAssignment, + b: IDepartmentAssignment + ): number => { + const momenta = moment(a.lastUpdated); + const momentb = moment(b.lastUpdated); + return momenta.isBefore(momentb) + ? 1 + : momenta.isAfter(momentb) + ? -1 + : 0; + }; + + useEffect(() => { + dispatch(getAllDepartments()); + }, []); + + const headers = [ + departmentAssignmentTranslate.headers.isHome, + departmentAssignmentTranslate.headers.departmentId, + departmentAssignmentTranslate.headers.note, + departmentAssignmentTranslate.headers.startDate, + departmentAssignmentTranslate.headers.endDate, + departmentAssignmentTranslate.headers.created, + departmentAssignmentTranslate.headers.createdBy, + departmentAssignmentTranslate.headers.createdByType, + departmentAssignmentTranslate.headers.lastUpdated, + departmentAssignmentTranslate.headers.lastUpdatedBy, + departmentAssignmentTranslate.headers.UpdatedByType + ]; + + const cells = [ + (x: IDepartmentAssignment) => , + (x: IDepartmentAssignment) => getDepartmentNameFromId(x.departmentId), + (x: IDepartmentAssignment) => x.note, + (x: IDepartmentAssignment) => + moment(x.startDate).format("MM-DD-YYY").toString(), + (x: IDepartmentAssignment) => + x.endDate + ? moment(x.endDate).format("MM-DD-YYY").toString() + : "active", + (x: IDepartmentAssignment) => moment(x.created).toISOString(), + (x: IDepartmentAssignment) => x.createdBy, + (x: IDepartmentAssignment) => x.createdByType, + (x: IDepartmentAssignment) => moment(x.lastUpdated).toISOString(), + (x: IDepartmentAssignment) => x.lastUpdatedBy, + (x: IDepartmentAssignment) => x.lastUpdatedByType + ]; + return ( + { + return sortDataAudits(a, b); + })} + /> + ); +}; +export default DeptAssignmentAuditTable; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/containers/employee-page/department-assignments/DeptAssignmentTabGroup.test.tsx b/LCU.WebApps.Operations.IncentiveManager/src/containers/employee-page/department-assignments/DeptAssignmentTabGroup.test.tsx new file mode 100644 index 0000000..82aef6c --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/containers/employee-page/department-assignments/DeptAssignmentTabGroup.test.tsx @@ -0,0 +1,162 @@ +import { mount } from "enzyme"; +import React from "react"; +import AsyncState from "../../../shared/enumerations/AsyncState"; +import DeptAssignmentTabGroup from "./DeptAssignmentTabGroup"; +import * as reactRedux from "react-redux"; +import { IDepartmentState } from "../../../reducers/DepartmentReducer"; +import PermissionCode from "../../../shared/enumerations/PermissionCode"; +import { IProfileState } from "../../../reducers/ProfileReducer"; +import { mockDeptAssignments } from "../../../shared/testing/mocks/MockDepartmentAssignments"; +import { mockRoles } from "../../../shared/testing/mocks/MockRoles"; +import { ISettings } from "../../../shared/models/Settings"; +import { getMockDepartments } from "../../../shared/testing/mocks/MockDepartments"; +import { IDepartmentAssignment } from "../../../shared/models/DepartmentAssignment"; + +const mockSettingsState: ISettings = { + appRoles: mockRoles, + permissions: [], + productDetails: [], + businessLines: [], + productSet: [], + regions: [], + payoutProfiles: [], + activityStateSet: {}, + departmentStateSet: {}, + activityTypeSet: {}, + organizationSet: {}, + systemTypeSet: {}, + userTypeSet: {}, + departments: getMockDepartments() +}; + +const mockDepartmentState: IDepartmentState = { + departments: { + data: getMockDepartments(), + state: AsyncState.Success + }, + submitState: { state: AsyncState.Inactive } +}; + +// setup appUser to get past permission guard +const mockProfileState: IProfileState = { + profile: { + data: { + adEmail: "test@test.com", + personId: 11, + employeeId: "11771", + homeDepartment: "HOME", + baseRole: "QA", + fullName: "John Tester", + role: { + name: "Admin", + description: "admin", + permissions: [ + { + code: PermissionCode.ASSIGN_ASSOCIATES_TO_DEPARTMENT, + description: "ASSIGN_ASSOCIATES_TO_DEPARTMENT" + } + ], + hasCommissions: false + }, + additionalPermissions: [], + departments: [], + assignments: [] + }, + state: AsyncState.Success + }, + searchResults: { + state: AsyncState.Success, + data: [] + } +}; +const useDispatchMock = jest.spyOn(reactRedux, "useDispatch"); +const useSelectorMock = jest.spyOn(reactRedux, "useSelector"); + +beforeEach(() => { + useDispatchMock.mockClear(); + useSelectorMock.mockClear(); +}); + +describe("User wants to switch views of department assignments", () => { + it("Only displays active department assignments in the active tab and sorts by end date desc", () => { + useDispatchMock.mockReturnValue(jest.fn()); + useSelectorMock.mockReturnValue({ + ...mockDepartmentState, + ...mockProfileState + }); + const wrapper = mount( + jest.fn()} + submitDeptAssignmentUpdate={() => jest.fn()} + submitState={{ state: AsyncState.Inactive }} + employeeDepartmentAssignments={mockDeptAssignments} + /> + ); + const assignments: [] = wrapper + .find("DeptAssignmentTable") + .prop("assignments"); + expect(assignments.length).toBe(2); + expect(assignments.map((x: IDepartmentAssignment) => x.id)).toEqual([ + 2, + 1 + ]); + }); + + it("Only displays inactive department assignments in the closed tab and sorts by end date desc", () => { + useDispatchMock.mockReturnValue(jest.fn()); + useSelectorMock.mockReturnValue({ + ...mockDepartmentState, + ...mockProfileState, + ...mockSettingsState + }); + const wrapper = mount( + jest.fn()} + submitDeptAssignmentUpdate={() => jest.fn()} + submitState={{ state: AsyncState.Inactive }} + employeeDepartmentAssignments={mockDeptAssignments} + /> + ); + //make tab group display 2nd tab + wrapper.find('[data-test="nav-link-1"]').simulate("click"); + wrapper.update(); + const assignments: [] = wrapper + .find("BasicDeptAssignmentTable") + .prop("assignments"); + expect(assignments.length).toBe(2); + expect(assignments.map((x: IDepartmentAssignment) => x.id)).toEqual([ + 3, + 0 + ]); + }); + + it("Displays everything in the audit", () => { + useDispatchMock.mockReturnValue(jest.fn()); + useSelectorMock.mockReturnValue({ + ...mockDepartmentState, + ...mockProfileState, + ...mockSettingsState + }); + const wrapper = mount( + jest.fn()} + submitDeptAssignmentUpdate={() => jest.fn()} + submitState={{ state: AsyncState.Inactive }} + employeeDepartmentAssignments={mockDeptAssignments} + /> + ); + //make tab group display 3rd tab + wrapper.find('[data-test="nav-link-2"]').simulate("click"); + wrapper.update(); + const assignments: [] = wrapper + .find("DeptAssignmentAuditTable") + .prop("assignments"); + expect(assignments.length).toBe(4); + expect(assignments.map((x: IDepartmentAssignment) => x.id)).toEqual([ + 2, + 3, + 1, + 0 + ]); + }); +}); diff --git a/LCU.WebApps.Operations.IncentiveManager/src/containers/employee-page/department-assignments/DeptAssignmentTabGroup.tsx b/LCU.WebApps.Operations.IncentiveManager/src/containers/employee-page/department-assignments/DeptAssignmentTabGroup.tsx new file mode 100644 index 0000000..dfb3abc --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/containers/employee-page/department-assignments/DeptAssignmentTabGroup.tsx @@ -0,0 +1,107 @@ +import React from "react"; +import TabView from "../../../components/tab-view/TabView"; +import DeptAssignmentTable from "./department-assignment-table/DeptAssignmentTable"; +import BasicDeptAssignmentTable from "./BasicDeptAssignmentTable"; +import DeptAssignmentAuditTable from "./DeptAssignmentAuditTable"; +import moment from "moment"; +import IAsyncStateContainer from "../../../shared/types/IAsyncStateContainer"; +import Translate from "./../../../translate/Translate"; +import { + IDepartmentAssignment, + IDepartmentAssignmentDto, + IDepartmentAssignmentUpdateDto +} from "../../../shared/models/DepartmentAssignment"; + +export interface IDeptAssignmentTabGroupProps { + employeeDepartmentAssignments: IDepartmentAssignment[]; + submitState: IAsyncStateContainer; + submitNewDeptAssignment: (data: Partial) => void; + submitDeptAssignmentUpdate: ( + deptUpdate: IDepartmentAssignmentUpdateDto + ) => void; +} + +const TabViewTranslate = + Translate.Get.containers.employeePage.deptAssignment.deptAssignmentTabGroup; + +const DeptAssignmentTabGroup: React.FC = ( + props +) => { + const activeAssignments = (): IDepartmentAssignment[] => { + return ( + props.employeeDepartmentAssignments + .filter( + (x) => + x.endDate == undefined || + moment(x.endDate).isAfter(moment.now()) + ) + .sort((a, b) => + b.endDate == undefined || + moment(b.endDate).isBefore(moment(a.endDate)) + ? 1 + : a.endDate == undefined || + moment(b.endDate).isBefore(moment(a.endDate)) + ? -1 + : 0 + ) ?? [] + ); + }; + + const inActiveAssignments = (): IDepartmentAssignment[] => { + return ( + props.employeeDepartmentAssignments + .filter( + (x) => + x.endDate !== undefined && + moment(x.endDate).isBefore(moment.now()) + ) + .sort((a, b) => + moment(a.endDate).isBefore(b.endDate) + ? 1 + : moment(a.endDate).isAfter(b.endDate) + ? -1 + : -0 + ) ?? [] + ); + }; + + return ( + + props.submitNewDeptAssignment(data) + } + submitDeptAssignmentUpdate={ + props.submitDeptAssignmentUpdate + } + assignments={activeAssignments()} + submitStatus={props.submitState} + /> + ) + }, + { + header: TabViewTranslate.basicDeptAssignmentTable, + element: ( + + ) + }, + { + header: TabViewTranslate.deptAssignmentAduitTable, + element: ( + + ) + } + ]} + /> + ); +}; + +export default DeptAssignmentTabGroup; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/containers/employee-page/department-assignments/HomeIcon.tsx b/LCU.WebApps.Operations.IncentiveManager/src/containers/employee-page/department-assignments/HomeIcon.tsx new file mode 100644 index 0000000..f0499b8 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/containers/employee-page/department-assignments/HomeIcon.tsx @@ -0,0 +1,24 @@ +import React from "react"; +import { Badge } from "reactstrap"; + +export interface IHomeIconProps { + isHome?: boolean; +} + +const HomeIcon: React.FC = (props) => { + return ( + + ); +}; + +export default HomeIcon; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/containers/employee-page/department-assignments/department-assignment-table/DeptAssignmentEdit.test.tsx b/LCU.WebApps.Operations.IncentiveManager/src/containers/employee-page/department-assignments/department-assignment-table/DeptAssignmentEdit.test.tsx new file mode 100644 index 0000000..c68d4a0 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/containers/employee-page/department-assignments/department-assignment-table/DeptAssignmentEdit.test.tsx @@ -0,0 +1,229 @@ +import React from "react"; +import moment from "moment"; +import { UserType } from "../../../../shared/models/Auditable"; +import { mount } from "enzyme"; +import DeptAssignmentEdit from "./DeptAssignmentEdit"; +import { + IDepartmentAssignment, + IDepartmentAssignmentUpdateDto +} from "../../../../shared/models/DepartmentAssignment"; +import { mockRoles } from "../../../../shared/testing/mocks/MockRoles"; + +const mockDeptAssignment: IDepartmentAssignment = { + id: 5, + employeeId: "5", + departmentId: 55, + startDate: moment("2021-01-02"), + endDate: moment("2021-01-03"), + note: "Note", + isHome: false, + createdByType: UserType.Associate, + createdBy: "me", + created: moment("2021-01-04"), + lastUpdated: moment("2021-01-04"), + lastUpdatedByType: UserType.Associate, + lastUpdatedBy: "me", + role: { + name: "Teller", + description: "Teller Desc", + hasCommissions: true, + permissions: [] + }, + slidingPayout: 100 +}; + +const mockGetDeptNameFromId = (id: any) => { + return "WAUWATOSA"; +}; +const mockDisableEdit = jest.fn(); +const mockUpdateFormState = jest.fn(); +const mockSubmit = jest.fn(); + +beforeEach(() => { + mockDisableEdit.mockClear(); + mockUpdateFormState.mockClear(); + mockSubmit.mockClear(); +}); + +describe("A appUser wants to edit an employee's department assignments", () => { + it("displays the expected data", () => { + const mockState: IDepartmentAssignmentUpdateDto = { + endDate: moment("2021-01-03"), + role: "Teller", + employeeId: "11771", + assignmentId: 99, + slidingPayout: 100 + }; + + const wrapper = mount( + mockSubmit()} + updateFormState={() => mockUpdateFormState()} + roles={mockRoles} + disableEdit={() => mockDisableEdit()} + /> + ); + expect(wrapper.find('[data-test="department"]').text()).toContain( + "WAUWATOSA" + ); + expect(wrapper.find('[data-test="note"]').text()).toContain("Note"); + expect(wrapper.find('[data-test="start-date"]').text()).toContain( + "01-02-2021" + ); + const endInput = wrapper.find('[data-test="end-date-input"]'); + // expect( endInput.first().html()).toEqual(""); + const endInputValue = endInput.first().prop("value"); + expect(endInputValue).toEqual("2021-01-03"); + expect( + wrapper + .find('[data-test="submit-button"]') + .hostNodes() + .prop("disabled") + ).toBe(false); + }); + + it("handles null end date", () => { + const mockState: IDepartmentAssignmentUpdateDto = { + endDate: undefined, + role: "Teller", + employeeId: "11771", + assignmentId: 99, + slidingPayout: 100 + }; + + const wrapper = mount( + mockSubmit()} + updateFormState={() => mockUpdateFormState()} + roles={mockRoles} + disableEdit={() => mockDisableEdit()} + /> + ); + expect(wrapper.find('[data-test="department"]').text()).toContain( + "WAUWATOSA" + ); + expect(wrapper.find('[data-test="note"]').text()).toContain("Note"); + expect(wrapper.find('[data-test="start-date"]').text()).toContain( + "01-02-2021" + ); + const endInput = wrapper.find('[data-test="end-date-input"]'); + const endInputValue = endInput.first().prop("value"); + expect(endInputValue).toEqual(""); + expect( + wrapper + .find('[data-test="submit-button"]') + .hostNodes() + .prop("disabled") + ).toBe(false); + }); +}); + +describe("A appUser wants to submit an edit to an employee's department assignments", () => { + it("disables submit and displays feedback when end date is invalid - end is before start", () => { + const mockState: IDepartmentAssignmentUpdateDto = { + endDate: moment("2021-01-01"), + role: "Teller", + employeeId: "11771", + assignmentId: 99, + slidingPayout: 100 + }; + + const wrapper = mount( + mockSubmit()} + updateFormState={() => mockUpdateFormState()} + roles={mockRoles} + disableEdit={() => mockDisableEdit()} + /> + ); + + const endInput = wrapper.find('[data-test="end-date-input"]'); + const endInputValue = endInput.first().prop("value"); + expect(endInputValue).toEqual("2021-01-01"); + expect( + wrapper + .find('[data-test="submit-button"]') + .hostNodes() + .prop("disabled") + ).toBe(true); + //Invalid and therefore displays feedback message, we trust reactstrap + expect(endInput.first().prop("invalid")).toBe(true); + }); + + it("calls submit with proper data when submit button is clicked", () => { + const mockState: IDepartmentAssignmentUpdateDto = { + endDate: moment("2021-01-03"), + role: "Teller", + employeeId: "11771", + assignmentId: 99, + slidingPayout: 100 + }; + + const wrapper = mount( + mockSubmit(form)} + updateFormState={() => mockUpdateFormState()} + roles={mockRoles} + disableEdit={() => mockDisableEdit()} + /> + ); + + expect( + wrapper + .find('[data-test="submit-button"]') + .hostNodes() + .prop("disabled") + ).toBe(false); + wrapper + .find('[data-test="submit-button"]') + .hostNodes() + .simulate("click"); + wrapper.update(); + expect(mockSubmit).toBeCalledTimes(1); + expect(mockDisableEdit).toBeCalledTimes(1); + expect(mockSubmit.mock.calls).toEqual([[mockState]]); + }); +}); + +describe("A appUser wants to cancel an edit to an employee's department assignments", () => { + it("does not submit and toggles edit to false", () => { + const mockState: IDepartmentAssignmentUpdateDto = { + endDate: moment("2021-01-03"), + role: "Teller", + employeeId: "11771", + assignmentId: 99, + slidingPayout: 100 + }; + + const wrapper = mount( + mockSubmit(form)} + updateFormState={() => mockUpdateFormState()} + roles={mockRoles} + disableEdit={() => mockDisableEdit()} + /> + ); + wrapper + .find('[data-test="clear-button"]') + .hostNodes() + .simulate("click"); + wrapper.update(); + expect(mockSubmit).toBeCalledTimes(0); + expect(mockDisableEdit).toBeCalledTimes(1); + }); +}); diff --git a/LCU.WebApps.Operations.IncentiveManager/src/containers/employee-page/department-assignments/department-assignment-table/DeptAssignmentEdit.tsx b/LCU.WebApps.Operations.IncentiveManager/src/containers/employee-page/department-assignments/department-assignment-table/DeptAssignmentEdit.tsx new file mode 100644 index 0000000..21cca45 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/containers/employee-page/department-assignments/department-assignment-table/DeptAssignmentEdit.tsx @@ -0,0 +1,196 @@ +import React from "react"; +import { Button, ButtonGroup, FormFeedback, Input } from "reactstrap"; +import { tryUpdateStateWithInput } from "../../../../shared/services/InputService"; +import moment, { Moment } from "moment"; +import HomeIcon from "../HomeIcon"; +import Translate from "./../../../../translate/Translate"; +import { + IDepartmentAssignment, + IDepartmentAssignmentUpdateDto +} from "../../../../shared/models/DepartmentAssignment"; +import { IAppRole } from "../../../../shared/models/Settings"; + +const newDepartmentAssignmentTranslate = + Translate.Get.containers.employeePage.deptAssignment.deptAssignmentTable + .newDeptAssignmentRow; + +export interface IDeptAssignmentTableRowEditProps { + departmentAssignment: IDepartmentAssignment; + formState: IDepartmentAssignmentUpdateDto; + getDepartmentNameFromId: (deptId: number) => string; + submitDeptAssignmentUpdate: ( + update: IDepartmentAssignmentUpdateDto + ) => void; + disableEdit: () => void; + updateFormState: (newState: IDepartmentAssignmentUpdateDto) => void; + roles: IAppRole[]; +} + +const DeptAssignmentTranslate = + Translate.Get.containers.employeePage.deptAssignment.deptAssignmentTable + .deptAssignmentTableEdit; + +const DeptAssignmentEdit: React.FC = ( + props +) => { + const castMomentToStringOrPlaceholder = ( + value: Moment | undefined, + fallBack: string + ): string => { + if (value === undefined || value === null || !moment(value).isValid()) { + return fallBack; + } + return moment(value).format("MM-DD-YYYY").toString(); + }; + + const isEndDateInvalid = () => { + if (props.formState.endDate && props.departmentAssignment.startDate) { + return moment(props.formState.endDate).isBefore( + props.departmentAssignment.startDate + ); + } + return false; + }; + + const submitForm = () => { + props.submitDeptAssignmentUpdate(props.formState); + props.disableEdit(); + }; + + const isConfirmButtonDisabled = () => { + return isEndDateInvalid(); + }; + + return ( + + + + + + {props.getDepartmentNameFromId( + props.departmentAssignment.departmentId + )} + + + {castMomentToStringOrPlaceholder( + props.departmentAssignment.startDate, + "-" + )} + + + { + props.updateFormState( + tryUpdateStateWithInput( + props.formState, + "endDate", + // FORCE to utc. Prevents a timezone conversion + // you don't normally have to do this. + moment.utc(event.target.value).endOf("month") + ) + ); + }} + /> + + {DeptAssignmentTranslate.validation.endDate} + + + + { + props.updateFormState( + tryUpdateStateWithInput( + props.formState, + "role", + event.target.value + ) + ); + }} + > + + {props.roles.map((role) => { + return ( + + ); + })} + + + + { + props.updateFormState( + tryUpdateStateWithInput( + props.formState, + "slidingPayout", + event.target.value + ) + ); + }} + /> + + {props.departmentAssignment.note} + + + + + + + + ); +}; + +export default DeptAssignmentEdit; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/containers/employee-page/department-assignments/department-assignment-table/DeptAssignmentNew.test.tsx b/LCU.WebApps.Operations.IncentiveManager/src/containers/employee-page/department-assignments/department-assignment-table/DeptAssignmentNew.test.tsx new file mode 100644 index 0000000..f63daff --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/containers/employee-page/department-assignments/department-assignment-table/DeptAssignmentNew.test.tsx @@ -0,0 +1,259 @@ +import { shallow } from "enzyme"; +import moment from "moment"; +import React from "react"; +import DeptAssignmentNew from "./DeptAssignmentNew"; +import { mockRoles } from "../../../../shared/testing/mocks/MockRoles"; +import { MultiSelectOption } from "../../../../shared/types/IMultiSelectOption"; + +const mockDepartments = [ + { + departmentId: 0, + id: 55, + departmentName: "WAUWATOSA", + businessLineCode: "OPS", + state: "Active" + }, + { + departmentId: 1, + id: 56, + departmentName: "HEADQUARTERS", + businessLineCode: "OPS", + state: "Active" + } +]; + +const departmentOptions = mockDepartments.map( + (department) => + ({ + key: department.id?.toString(), + label: department.departmentName, + value: department.id?.toString() + } as MultiSelectOption) +); + +const mockSubmit = jest.fn(); + +beforeEach(() => { + mockSubmit.mockClear(); +}); + +describe("User wants to add a new dept assignment", () => { + it("updates state when input changed", () => { + const wrapper = shallow( + mockSubmit(data)} + /> + ); + expect(wrapper.find('[data-test="dept-input"]').prop("value")).toEqual( + [] + ); + expect(wrapper.find('[data-test="note-input"]').prop("value")).toBe(""); + expect(wrapper.find('[data-test="start-input"]').prop("value")).toEqual( + "" + ); + expect(wrapper.find('[data-test="end-input"]').prop("value")).toEqual( + "" + ); + expect(wrapper.find('[data-test="end-input"]').prop("invalid")).toBe( + false + ); + wrapper + .find('[data-test="dept-input"]') + .simulate("change", { target: { value: [departmentOptions[0]] } }); + wrapper + .find('[data-test="note-input"]') + .simulate("change", { target: { value: "note" } }); + wrapper + .find('[data-test="start-input"]') + .simulate("change", { target: { value: moment("2021-01-01") } }); + wrapper + .find('[data-test="end-input"]') + .simulate("change", { target: { value: moment("2021-01-02") } }); + wrapper.update(); + expect(wrapper.find('[data-test="dept-input"]').prop("value")).toEqual({ + target: { value: [departmentOptions[0]] } + }); + expect(wrapper.find('[data-test="note-input"]').prop("value")).toBe( + "note" + ); + expect(wrapper.find('[data-test="start-input"]').prop("value")).toEqual( + "2021-01-01" + ); + expect(wrapper.find('[data-test="end-input"]').prop("value")).toEqual( + "2021-01-31" + ); + expect(wrapper.find('[data-test="end-input"]').prop("invalid")).toBe( + false + ); + }); + it("disables submit until required data is entered", () => { + const wrapper = shallow( + mockSubmit(data)} + /> + ); + expect( + wrapper.find('[data-test="submit-button"]').prop("disabled") + ).toBe(true); + wrapper + .find('[data-test="dept-input"]') + .simulate("change", { target: { value: [departmentOptions[0]] } }); + //Note not required + wrapper + .find('[data-test="start-input"]') + .simulate("change", { target: { value: moment("2021-01-01") } }); + //EndDate Not Required + wrapper.update(); + expect( + wrapper.find('[data-test="submit-button"]').prop("disabled") + ).toBe(false); + expect(wrapper.find('[data-test="end-input"]').prop("invalid")).toBe( + false + ); + }); + it("disables submit if endDate is before startDate", () => { + const wrapper = shallow( + mockSubmit(data)} + /> + ); + expect( + wrapper.find('[data-test="submit-button"]').prop("disabled") + ).toBe(true); + wrapper + .find('[data-test="dept-input"]') + .simulate("change", { target: { value: [departmentOptions[0]] } }); + wrapper + .find('[data-test="note-input"]') + .simulate("change", { target: { value: "note" } }); + wrapper + .find('[data-test="start-input"]') + .simulate("change", { target: { value: moment("2021-02-01") } }); + wrapper + .find('[data-test="end-input"]') + .simulate("change", { target: { value: moment("2021-01-02") } }); + wrapper.update(); + expect(wrapper.find('[data-test="end-input"]').prop("invalid")).toBe( + true + ); + expect( + wrapper.find('[data-test="submit-button"]').prop("disabled") + ).toBe(true); + }); + it("hides the clear button when there is nothing to clear", () => { + const wrapper = shallow( + mockSubmit(data)} + /> + ); + expect(wrapper.find('[data-test="clear-button"]').prop("hidden")).toBe( + true + ); + wrapper + .find('[data-test="dept-input"]') + .simulate("change", { target: { value: [departmentOptions[0]] } }); + wrapper + .find('[data-test="note-input"]') + .simulate("change", { target: { value: "note" } }); + expect(wrapper.find('[data-test="clear-button"]').prop("hidden")).toBe( + false + ); + }); +}); + +describe("User no longer wishes to add a new dept assignment", () => { + it("resets all inputs when the clear button is clicked", () => { + const wrapper = shallow( + mockSubmit(data)} + /> + ); + expect(wrapper.find('[data-test="clear-button"]').prop("hidden")).toBe( + true + ); + wrapper + .find('[data-test="dept-input"]') + .simulate("change", { target: { value: [departmentOptions[0]] } }); + wrapper + .find('[data-test="note-input"]') + .simulate("change", { target: { value: "note" } }); + expect(wrapper.find('[data-test="clear-button"]').prop("hidden")).toBe( + false + ); + wrapper.find('[data-test="clear-button"]').simulate("click"); + wrapper.update(); + expect(wrapper.find('[data-test="dept-input"]').prop("value")).toEqual( + [] + ); + expect(wrapper.find('[data-test="note-input"]').prop("value")).toBe(""); + expect(wrapper.find('[data-test="start-input"]').prop("value")).toEqual( + "" + ); + expect(wrapper.find('[data-test="end-input"]').prop("value")).toEqual( + "" + ); + expect(wrapper.find('[data-test="end-input"]').prop("invalid")).toBe( + false + ); + }); +}); + +describe("User submits a new dept assignment", () => { + it("passes the proper data on submit", () => { + const wrapper = shallow( + mockSubmit(data)} + /> + ); + wrapper + .find('[data-test="dept-input"]') + .simulate("change", { target: { value: [departmentOptions[0]] } }); + wrapper + .find('[data-test="role-input"]') + .simulate("change", { target: { value: "Admin" } }); + wrapper + .find('[data-test="note-input"]') + .simulate("change", { target: { value: "note" } }); + wrapper + .find('[data-test="sliding-payout-input"]') + .simulate("change", { target: { value: "65" } }); + wrapper + .find('[data-test="start-input"]') + .simulate("change", { target: { value: "2021-01-01" } }); + wrapper + .find('[data-test="end-input"]') + .simulate("change", { target: { value: "2021-01-02" } }); + wrapper.update(); + expect( + wrapper.find('[data-test="submit-button"]').prop("disabled") + ).toBe(false); + wrapper.find('[data-test="submit-button"]').simulate("click"); + wrapper.update(); + expect(wrapper.find('[data-test="dept-input"]').prop("value")).toEqual( + [] + ); + expect(wrapper.find('[data-test="note-input"]').prop("value")).toBe(""); + expect(wrapper.find('[data-test="start-input"]').prop("value")).toEqual( + "" + ); + expect(wrapper.find('[data-test="end-input"]').prop("value")).toEqual( + "" + ); + expect(wrapper.find('[data-test="end-input"]').prop("invalid")).toBe( + false + ); + //start and end date should be cast as UTC! + }); +}); diff --git a/LCU.WebApps.Operations.IncentiveManager/src/containers/employee-page/department-assignments/department-assignment-table/DeptAssignmentNew.tsx b/LCU.WebApps.Operations.IncentiveManager/src/containers/employee-page/department-assignments/department-assignment-table/DeptAssignmentNew.tsx new file mode 100644 index 0000000..e8d1c20 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/containers/employee-page/department-assignments/department-assignment-table/DeptAssignmentNew.tsx @@ -0,0 +1,264 @@ +import React, { useState } from "react"; +import Translate from "../../../../translate/Translate"; +import { IDepartment } from "../../../../shared/models/Department"; +import { Button, ButtonGroup, FormFeedback, Input } from "reactstrap"; +import { tryUpdateStateWithInput } from "../../../../shared/services/InputService"; +import moment from "moment"; +import { IDepartmentAssignmentDto } from "../../../../shared/models/DepartmentAssignment"; +import { IAppRole } from "../../../../shared/models/Settings"; +import MultiSelect from "react-multi-select-component"; +import { MultiSelectOption } from "../../../../shared/types/IMultiSelectOption"; + +export interface INewDeptAssignmentRowProps { + departments: IDepartment[]; + roles: IAppRole[]; + submitNewDepartmentAssignment: ( + newDepartmentData: Partial + ) => void; +} + +const newDepartmentAssignmentTranslate = + Translate.Get.containers.employeePage.deptAssignment.deptAssignmentTable + .newDeptAssignmentRow; + +const DeptAssignmentNew: React.FC = (props) => { + const emptyFormState = { + departmentId: [], + startDate: undefined, + endDate: undefined, + note: undefined, + role: undefined, + slidingPayout: 100 + }; + + const isSubmitDisabled = (): boolean => { + return ( + selectedNewDepartmentIds?.length === 0 || + formState.startDate === undefined || + !formState.startDate.isValid() || + isEndDateInvalid() + ); + }; + + const submitForm = () => { + props.submitNewDepartmentAssignment(formState); + clearFormState(); + }; + + const clearFormState = () => { + setFormState(emptyFormState); + setSelectedNewDepartmentIds([]); + }; + + const shouldHideClear = () => { + return ( + selectedNewDepartmentIds?.length === 0 && + formState.startDate === undefined && + formState.endDate === undefined && + formState.note === undefined + ); + }; + + const isEndDateInvalid = () => { + if (formState.endDate && formState.startDate) { + return moment(formState.endDate).isBefore(formState.startDate); + } + return false; + }; + + const [formState, setFormState] = useState< + Partial + >(emptyFormState); + + const departmentOptions = props.departments + .filter((x) => x.state === "Active") + .map( + (department) => + ({ + key: department.id?.toString(), + label: department.departmentName, + value: department.id?.toString() + } as MultiSelectOption) + ); + + const [selectedNewDepartmentIds, setSelectedNewDepartmentIds] = useState< + MultiSelectOption[] + >([]); + + const handleSelected = (options: MultiSelectOption[]) => { + formState.departmentId = Array.from( + options + ).map((e: MultiSelectOption) => parseInt(e.value, 10)); + setSelectedNewDepartmentIds(options); + }; + + return ( + + + + + + + { + setFormState( + tryUpdateStateWithInput( + formState, + "startDate", + // FORCE to utc. Prevents a timezone conversion + // you don't normally have to do this. + moment.utc(event.target.value).startOf("month") + ) + ); + }} + /> + + + { + setFormState( + tryUpdateStateWithInput( + formState, + "endDate", + // FORCE to utc. Prevents a timezone conversion + // you don't normally have to do this. + moment.utc(event.target.value).endOf("month") + ) + ); + }} + /> + + { + newDepartmentAssignmentTranslate.endDate.validation + .invalid + } + + + + { + setFormState( + tryUpdateStateWithInput( + formState, + "role", + event.target.value + ) + ); + }} + > + + {props.roles.map((role) => { + return ( + + ); + })} + + + + { + setFormState( + tryUpdateStateWithInput( + formState, + "slidingPayout", + event.target.value + ) + ); + }} + /> + + + { + setFormState( + tryUpdateStateWithInput( + formState, + "note", + event.target.value + ) + ); + }} + /> + + + + + + + + + ); +}; + +export default DeptAssignmentNew; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/containers/employee-page/department-assignments/department-assignment-table/DeptAssignmentRow.test.tsx b/LCU.WebApps.Operations.IncentiveManager/src/containers/employee-page/department-assignments/department-assignment-table/DeptAssignmentRow.test.tsx new file mode 100644 index 0000000..cfb21c0 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/containers/employee-page/department-assignments/department-assignment-table/DeptAssignmentRow.test.tsx @@ -0,0 +1,245 @@ +/* eslint-disable @typescript-eslint/ban-ts-comment */ +import { mount } from "enzyme"; +import moment from "moment"; +import React from "react"; +import * as reactRedux from "react-redux"; +import { IProfileState } from "../../../../reducers/ProfileReducer"; +import AsyncState from "../../../../shared/enumerations/AsyncState"; +import PermissionCode from "../../../../shared/enumerations/PermissionCode"; +import { UserType } from "../../../../shared/models/Auditable"; +import Translate from "../../../../translate/Translate"; +import DeptAssignmentRow from "./DeptAssignmentRow"; +import { IDepartmentAssignment } from "../../../../shared/models/DepartmentAssignment"; +import { mockRoles } from "../../../../shared/testing/mocks/MockRoles"; + +export interface IFakePermissionProps { + requiredPermissions: string[]; + showWarning?: boolean; + children: JSX.Element; +} + +export const FakePermissionGuard: React.FC = (props) => { + return props.children; +}; + +jest.mock("../../../shared/PermissionGuard", () => FakePermissionGuard); + +const departments = [ + { + departmentId: 5, + id: 55, + departmentName: "WAUWATOSA", + businessLineCode: "OPS", + state: "Active" + }, + { + departmentId: 1, + id: 56, + departmentName: "HEADQUARTERS", + businessLineCode: "OPS", + state: "Active" + } +]; + +const mockDeptAssignment: IDepartmentAssignment = { + id: 5, + employeeId: "5", + departmentId: 55, + startDate: moment("2021-01-02"), + endDate: moment("2021-01-03"), + note: "Note", + isHome: false, + createdByType: UserType.Associate, + createdBy: "me", + created: moment("2021-01-04"), + lastUpdated: moment("2021-01-04"), + lastUpdatedByType: UserType.Associate, + lastUpdatedBy: "me", + role: { + name: "Admin", + description: "admin", + permissions: [ + { + code: PermissionCode.ASSIGN_ASSOCIATES_TO_DEPARTMENT, + description: "ASSIGN_ASSOCIATES_TO_DEPARTMENT" + } + ], + hasCommissions: false + }, + slidingPayout: 100 +}; + +// setup appUser to get past permission guard +const mockProfileState: IProfileState = { + profile: { + data: { + adEmail: "test@test.com", + personId: 11, + employeeId: "11771", + homeDepartment: "HOME", + baseRole: "QA", + fullName: "John Tester", + role: { + name: "Admin", + description: "admin", + permissions: [ + { + code: PermissionCode.ASSIGN_ASSOCIATES_TO_DEPARTMENT, + description: "ASSIGN_ASSOCIATES_TO_DEPARTMENT" + } + ], + hasCommissions: false + }, + additionalPermissions: [], + departments: [], + assignments: [] + }, + state: AsyncState.Success + }, + searchResults: { + state: AsyncState.Success, + data: [] + } +}; + +const useSelectorMock = jest.spyOn(reactRedux, "useSelector"); + +beforeEach(() => { + useSelectorMock.mockClear(); +}); + +describe("An Associate Logs in and views the DeptAssignmentTable", () => { + it("Should display expected data", () => { + useSelectorMock.mockReturnValue(mockProfileState); + const wrapper = mount( + jest.fn()} + /> + ); + expect(wrapper.find('[data-test="department"]').text()).toContain( + "WAUWATOSA" + ); + expect(wrapper.find('[data-test="note"]').text()).toContain("Note"); + expect(wrapper.find('[data-test="start-date"]').text()).toContain( + "01-02-2021" + ); + expect(wrapper.find('[data-test="end-date"]').text()).toContain( + "01-03-2021" + ); + }); + + it("handles missing deparment", () => { + useSelectorMock.mockReturnValue(mockProfileState); + const wrapper = mount( + jest.fn()} + /> + ); + expect(wrapper.find('[data-test="department"]').text()).toContain("-"); + expect(wrapper.find('[data-test="note"]').text()).toContain("Note"); + expect(wrapper.find('[data-test="start-date"]').text()).toContain( + "01-02-2021" + ); + expect(wrapper.find('[data-test="end-date"]').text()).toContain( + "01-03-2021" + ); + }); + + it("defaults missing start/end date", () => { + useSelectorMock.mockReturnValue(mockProfileState); + + const wrapper = mount( + jest.fn()} + /> + ); + expect(wrapper.find('[data-test="department"]').text()).toContain( + "WAUWATOSA" + ); + expect(wrapper.find('[data-test="note"]').text()).toContain("Note"); + expect(wrapper.find('[data-test="start-date"]').text()).toContain("-"); + expect(wrapper.find('[data-test="end-date"]').text()).toContain( + Translate.Get.containers.employeePage.deptAssignment + .deptAssignmentTable.deptAssignmentTableRow.noEndDate + ); + }); +}); + +describe("An Associate Logs in and wants to update the end date of a DeptAssignmentTable", () => { + it("Toggling Edit Displays Edit Row", () => { + useSelectorMock.mockReturnValue(mockProfileState); + const wrapper = mount( + jest.fn()} + /> + ); + + expect(wrapper.find('[data-test="edit-row"]').length).toBe(0); + wrapper.find("Button").simulate("click"); + wrapper.update(); + expect(wrapper.find('[data-test="edit-row"]').length).toBe(1); + expect( + wrapper.find('[data-test="edit-row"]').prop("formState") + ).toEqual({ + assignmentId: 5, + employeeId: "5", + endDate: undefined, + role: "Admin", + slidingPayout: 100 + }); + }); + + it("Toggling Edit for a row with Data sets formState to include the existing end date", () => { + useSelectorMock.mockReturnValue(mockProfileState); + + const wrapper = mount( + jest.fn()} + /> + ); + + expect(wrapper.find('[data-test="edit-row"]').length).toBe(0); + wrapper.find("Button").simulate("click"); + wrapper.update(); + expect(wrapper.find('[data-test="edit-row"]').length).toBe(1); + expect( + wrapper.find('[data-test="edit-row"]').prop("formState") + ).toEqual({ + assignmentId: 5, + employeeId: "5", + endDate: moment("2021-02-01"), + role: "Admin", + slidingPayout: 100 + }); + }); +}); diff --git a/LCU.WebApps.Operations.IncentiveManager/src/containers/employee-page/department-assignments/department-assignment-table/DeptAssignmentRow.tsx b/LCU.WebApps.Operations.IncentiveManager/src/containers/employee-page/department-assignments/department-assignment-table/DeptAssignmentRow.tsx new file mode 100644 index 0000000..75a1d26 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/containers/employee-page/department-assignments/department-assignment-table/DeptAssignmentRow.tsx @@ -0,0 +1,143 @@ +import React, { useState } from "react"; +import { IDepartment } from "../../../../shared/models/Department"; +import moment, { Moment } from "moment"; +import DeptAssignmentEdit from "./DeptAssignmentEdit"; +import { Button } from "reactstrap"; +import HomeIcon from "../HomeIcon"; +import Translate from "./../../../../translate/Translate"; +import PermissionGuard from "../../../shared/PermissionGuard"; +import PermissionCode from "../../../../shared/enumerations/PermissionCode"; +import { + IDepartmentAssignment, + IDepartmentAssignmentUpdateDto +} from "../../../../shared/models/DepartmentAssignment"; +import { IAppRole } from "../../../../shared/models/Settings"; + +export interface IDeptAssignmentTableRowProps { + departments: IDepartment[]; + departmentAssignment: IDepartmentAssignment; + submitDeptAssignmentUpdate: ( + update: IDepartmentAssignmentUpdateDto + ) => void; + roles: IAppRole[]; +} + +const DeptAssignmentTranslate = + Translate.Get.containers.employeePage.deptAssignment.deptAssignmentTable + .deptAssignmentTableRow; + +const DeptAssignmentRow: React.FC = (props) => { + const createFormStateFromAssignment = ( + departmentAssignment: IDepartmentAssignment + ): IDepartmentAssignmentUpdateDto => { + const dto = { + assignmentId: departmentAssignment.id!, + employeeId: departmentAssignment.employeeId, + endDate: departmentAssignment.endDate + ? moment(departmentAssignment.endDate) + : undefined, + role: departmentAssignment.role?.name || "MISSING", + slidingPayout: departmentAssignment.slidingPayout + }; + return dto; + }; + + const initialFormState = createFormStateFromAssignment( + props.departmentAssignment + ); + const [isEditing, setEdit] = useState(false); + const [formState, setFormState] = useState( + initialFormState + ); + + const enableEdit = () => { + setFormState(createFormStateFromAssignment(props.departmentAssignment)); + setEdit(true); + }; + + const getDepartmentNameFromId = (id: number): string => { + return ( + props.departments.find((dept) => dept.id === id)?.departmentName ?? + "-" + ); + }; + + const castMomentToStringOrPlaceholder = ( + value: Moment | undefined, + fallBack: string + ): string => { + if (value === undefined || value === null || !moment(value).isValid()) { + return fallBack; + } + return moment(value).format("MM-DD-YYYY").toString(); + }; + + if (!isEditing) { + return ( + + + + + + {getDepartmentNameFromId( + props.departmentAssignment.departmentId + )} + + + + {castMomentToStringOrPlaceholder( + props.departmentAssignment.startDate, + "-" + )} + + + {castMomentToStringOrPlaceholder( + props.departmentAssignment.endDate, + DeptAssignmentTranslate.noEndDate + )} + + + {props.departmentAssignment.role + ? props.departmentAssignment.role.name + : "Missing!"} + + + {props.departmentAssignment.slidingPayout}% + + {props.departmentAssignment.note} + + + + + + + ); + } + + return ( + + getDepartmentNameFromId(deptId) + } + submitDeptAssignmentUpdate={props.submitDeptAssignmentUpdate} + disableEdit={() => setEdit(false)} + updateFormState={(newState) => setFormState(newState)} + data-test="edit-row" + /> + ); +}; + +export default DeptAssignmentRow; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/containers/employee-page/department-assignments/department-assignment-table/DeptAssignmentTable.test.tsx b/LCU.WebApps.Operations.IncentiveManager/src/containers/employee-page/department-assignments/department-assignment-table/DeptAssignmentTable.test.tsx new file mode 100644 index 0000000..877d6fe --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/containers/employee-page/department-assignments/department-assignment-table/DeptAssignmentTable.test.tsx @@ -0,0 +1,77 @@ +import { mount } from "enzyme"; +import React from "react"; +import DeptAssignmentTable from "./DeptAssignmentTable"; +import AsyncState from "./../../../../shared/enumerations/AsyncState"; +import * as reactRedux from "react-redux"; +import { IDepartmentState } from "../../../../reducers/DepartmentReducer"; +import { mockDeptAssignments } from "../../../../shared/testing/mocks/MockDepartmentAssignments"; +import { mockProfileState } from "../../../../shared/testing/mocks/MockProfileState"; +import { getMockAppSettings } from "../../../../shared/testing/mocks/MockAppSettings"; + +export interface IFakePermissionProps { + requiredPermissions: string[]; + showWarning?: boolean; + children: JSX.Element; +} + +export const FakePermissionGuard: React.FC = (props) => { + return props.children; +}; + +jest.mock("../../../shared/PermissionGuard", () => FakePermissionGuard); + +const useDispatchMock = jest.spyOn(reactRedux, "useDispatch"); +const useSelectorMock = jest.spyOn(reactRedux, "useSelector"); +const useContextMock = jest.spyOn(React, "useContext"); + +beforeEach(() => { + useDispatchMock.mockClear(); + useSelectorMock.mockClear(); + useContextMock.mockClear(); +}); + +const mockDepartmentState: IDepartmentState = { + departments: { + data: [ + { + departmentId: 0, + id: 55, + departmentName: "WAUWATOSA", + businessLineCode: "OPS", + state: "Active" + }, + { + departmentId: 1, + id: 56, + departmentName: "HEADQUARTERS", + businessLineCode: "OPS", + state: "Active" + } + ], + state: AsyncState.Success + }, + submitState: { state: AsyncState.Inactive } +}; + +describe("Associate views Emoloyees Department Assignments", () => { + it("Displays all available dept assignment table row", () => { + useDispatchMock.mockReturnValue(jest.fn()); + useSelectorMock.mockReturnValue({ + ...mockDepartmentState, + ...mockProfileState + }); + useContextMock.mockReturnValue(getMockAppSettings()); + + const wrapper = mount( + jest.fn()} + submitNewDeptAssignment={() => jest.fn()} + /> + ); + + expect(wrapper.find("DeptAssignmentRow").length).toBe(4); + expect(wrapper.find("DeptAssignmentNew").length).toBe(1); + }); +}); diff --git a/LCU.WebApps.Operations.IncentiveManager/src/containers/employee-page/department-assignments/department-assignment-table/DeptAssignmentTable.tsx b/LCU.WebApps.Operations.IncentiveManager/src/containers/employee-page/department-assignments/department-assignment-table/DeptAssignmentTable.tsx new file mode 100644 index 0000000..02b98cc --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/containers/employee-page/department-assignments/department-assignment-table/DeptAssignmentTable.tsx @@ -0,0 +1,133 @@ +import React from "react"; +import Translate from "../../../../translate/Translate"; +import DeptAssignmentRow from "./DeptAssignmentRow"; +import IAsyncStateContainer from "../../../../shared/types/IAsyncStateContainer"; +import DeptAssignmentAlertBar from "../DeptAssignmentAlertBar"; +import PermissionGuard from "../../../shared/PermissionGuard"; +import PermissionCode from "../../../../shared/enumerations/PermissionCode"; +import { + IDepartmentAssignment, + IDepartmentAssignmentDto, + IDepartmentAssignmentUpdateDto +} from "../../../../shared/models/DepartmentAssignment"; +import DeptAssignmentNew from "./DeptAssignmentNew"; +import { SettingsContext } from "../../../../shared/providers/SettingsProvider"; +import SettingsService from "../../../../shared/services/SettingsService"; +import { useSelector } from "react-redux"; +import { IAppState } from "../../../../store/Store"; +import { IProfileState } from "../../../../reducers/ProfileReducer"; +import ProfileService from "../../../../shared/services/ProfileService"; + +export interface IDeptAssignmentTableProps { + assignments: IDepartmentAssignment[]; + submitStatus: IAsyncStateContainer; + submitNewDeptAssignment: ( + deptAssignment: Partial + ) => void; + submitDeptAssignmentUpdate: ( + deptUpdate: IDepartmentAssignmentUpdateDto + ) => void; +} + +const departmentAssignmentTranslate = + Translate.Get.containers.employeePage.deptAssignment.deptAssignmentTable; + +const DeptAssignmentTable: React.FC = (props) => { + const settings = React.useContext(SettingsContext); + const profileState = useSelector( + (state) => state.profileState + ); + const profile = profileState.profile; + + const maxRank = profile.data + ? ProfileService.findHighestRank(profile.data) + : 1; + const assignableRoles = SettingsService.getAssignableRoles( + settings?.appRoles, + maxRank + ); + return ( +
+ +
+ + + + + + + + + + + + + + + + + ) => { + props.submitNewDeptAssignment( + deptAssignment + ); + }} + /> + + {props.assignments.map((assignment) => { + return ( + + ); + })} + +
+ {departmentAssignmentTranslate.headers.isHome} + + { + departmentAssignmentTranslate.headers + .departmentId + } + + { + departmentAssignmentTranslate.headers + .startDate + } + +   + {departmentAssignmentTranslate.headers.endDate} + + {departmentAssignmentTranslate.headers.role} + + {departmentAssignmentTranslate.headers.payout} + + {departmentAssignmentTranslate.headers.note} + + +
+
+
+ ); +}; +export default DeptAssignmentTable; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/containers/eoe-list/EndOfEmploymentPage.tsx b/LCU.WebApps.Operations.IncentiveManager/src/containers/eoe-list/EndOfEmploymentPage.tsx new file mode 100644 index 0000000..134010b --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/containers/eoe-list/EndOfEmploymentPage.tsx @@ -0,0 +1,140 @@ +import React, { useState } from "react"; +import moment, { Moment } from "moment"; +import Translate from "../../translate/Translate"; +import { Alert, Input } from "reactstrap"; +import PayoutService from "../../shared/services/PayoutService"; +import AsyncState from "../../shared/enumerations/AsyncState"; +import IAsyncStateContainer from "../../shared/types/IAsyncStateContainer"; +import AsyncGuard from "../shared/AsyncGuard"; +import PayoutEntryTable from "../../components/payouts/PayoutEntryTable"; +import ExportCsvService from "../../shared/services/ExportCsvService"; +import { IEmployeeSummary } from "../../shared/models/Employee"; +import EndOfEmploymentPayoutView from "../../components/payouts/EndOfEmploymentPayoutView"; +import TabView from "../../components/tab-view/TabView"; +import ProfileDao from "../../shared/dao/ProfileDao"; + +const EndOfEmploymentPage: React.FC = () => { + const [date, setDate] = useState(moment()); + const [payouts, setPayouts] = useState< + IAsyncStateContainer + >({ + state: AsyncState.Inactive + }); + + const submitForm = () => { + fetchData().then((employeeSummaries) => { + setPayouts(employeeSummaries); + }); + }; + + const fetchData = async () => { + return await ProfileDao.GetEndOfEmploymentList( + date.year(), + date.month() + 1 // moment months are 0 index. + ); + }; + + const exportCsv = () => { + if (payouts.data && payouts.data.length > 0) { + const converted = PayoutService.CreateEndOfEmploymentPayouts( + payouts.data + ); + ExportCsvService.exportDayforcePayoutCSV(converted); + } + }; + + return ( +
+
+
+ + { + Translate.Get.containers.endOfEmploymentPage + .instructions + } + +
+
+
+
+ { + setDate( + moment(event.target.value).startOf("month") + ); + }} + /> +
+
+ + + {payouts.data && payouts.data.length > 0 ? ( + + ) : ( + <> + )} + +
+
+
+
+ + <> + + ) + }, + { + header: "Details", + element: ( + + ) + } + ]} + /> + + +
+
+
+ ); +}; + +export default EndOfEmploymentPage; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/containers/goals/GoalEditCell.test.tsx b/LCU.WebApps.Operations.IncentiveManager/src/containers/goals/GoalEditCell.test.tsx new file mode 100644 index 0000000..ca5b297 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/containers/goals/GoalEditCell.test.tsx @@ -0,0 +1,25 @@ +import { shallow } from "enzyme"; +import React from "react"; +import GoalEditCell from "./GoalEditCell"; +import { IGoalEditInput } from "../../shared/models/Goal"; +import { Product2 } from "../../shared/enumerations/Product"; + +describe("A cell for editing goals", () => { + it("Displays a valid goal edit cell", () => { + const goal: IGoalEditInput = { + value: 1, + year: 2021, + type: Product2.Vehicle, + departmentId: 99, + personId: "programming@landmarkcu.com", + month: 1 + }; + const onAction = () => {}; + let wrapper = shallow( + + ); + expect(wrapper.find('[data-test="goal-edit-cell"]').length).toBe(1); + expect(wrapper.find('[data-test="valid-feedback"]').length).toBe(1); + expect(wrapper.find('[data-test="invalid-feedback"]').length).toBe(1); + }); +}); diff --git a/LCU.WebApps.Operations.IncentiveManager/src/containers/goals/GoalEditCell.tsx b/LCU.WebApps.Operations.IncentiveManager/src/containers/goals/GoalEditCell.tsx new file mode 100644 index 0000000..b61b539 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/containers/goals/GoalEditCell.tsx @@ -0,0 +1,48 @@ +import React, { useState } from "react"; +import { Product2 } from "../../shared/enumerations/Product"; +import { IGoalEditInput } from "../../shared/models/Goal"; +import GoalService from "../../shared/services/GoalService"; + +interface GoalEditorRowProps { + goal: IGoalEditInput; + tabIndex: number; + requestAction: (type: Product2, value: number) => void; +} + +const GoalEditCell: React.FC = ( + props: GoalEditorRowProps +) => { + const goal = props.goal; + const originalValue = goal.value + ""; + const [newValue, setNewValue] = useState(goal.value + ""); + + const pushChange = (value: string) => { + const filtered = value.replace(/\D/g, "").trim(); + props.requestAction(goal.type, parseInt(filtered, 10)); + setNewValue(filtered); + }; + + return ( +
+ pushChange(event.target.value)} + tabIndex={props.tabIndex} + /> +
+ Unsaved: {originalValue} {"->"} {newValue} +
+
+ Range: {goal.type.minValue} to {goal.type.maxValue} +
+
+ ); +}; + +export default GoalEditCell; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/containers/goals/department/AnnualProductTotal.tsx b/LCU.WebApps.Operations.IncentiveManager/src/containers/goals/department/AnnualProductTotal.tsx new file mode 100644 index 0000000..66d0dc4 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/containers/goals/department/AnnualProductTotal.tsx @@ -0,0 +1,39 @@ +import React from "react"; +import { + IDepartmentGoals, + IGoalChangeRequest +} from "../../../shared/models/Goal"; +import { Product2 } from "../../../shared/enumerations/Product"; +import GoalService from "../../../shared/services/GoalService"; + +interface GoalTotalProps { + departmentGoals: IDepartmentGoals; + pendingChanges: IGoalChangeRequest[]; + product: Product2; +} + +const AnnualProductTotal: React.FC = ( + props: GoalTotalProps +) => { + const total = GoalService.sumDepartmentGoalsForProduct( + props.departmentGoals, + props.product + ); + const totalPending = GoalService.sumPendingDepartmentGoalsForProduct( + props.departmentGoals, + props.pendingChanges, + props.product + ); + + return ( + + {GoalService.formatMonthlyGoal(total, props.product)} + {totalPending !== total + ? " -> " + + GoalService.formatMonthlyGoal(totalPending, props.product) + : ""} + + ); +}; + +export default AnnualProductTotal; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/containers/goals/department/DepartmentGoalsControls.test.tsx b/LCU.WebApps.Operations.IncentiveManager/src/containers/goals/department/DepartmentGoalsControls.test.tsx new file mode 100644 index 0000000..ac764aa --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/containers/goals/department/DepartmentGoalsControls.test.tsx @@ -0,0 +1,37 @@ +import { shallow } from "enzyme"; +import React from "react"; +import DepartmentGoalsControls from "./DepartmentGoalsControls"; +import { IDepartmentGoalsInput } from "../../../shared/models/Goal"; +import { IDepartment } from "../../../shared/models/Department"; + +describe("A component for controlling the department goals view", () => { + it("Displays a valid department goal controls", () => { + const input: IDepartmentGoalsInput = { + year: 2021, + departmentId: "99" + }; + const onChange = () => {}; + const onSave = () => {}; + const departments: IDepartment[] = [ + { + state: "Active", + departmentName: "Test Dept", + id: 99, + businessLineCode: "TEST", + departmentId: 56 + } + ]; + let wrapper = shallow( + + ); + expect( + wrapper.find('[data-test="department-goals-controls"]').length + ).toBe(1); + }); +}); diff --git a/LCU.WebApps.Operations.IncentiveManager/src/containers/goals/department/DepartmentGoalsControls.tsx b/LCU.WebApps.Operations.IncentiveManager/src/containers/goals/department/DepartmentGoalsControls.tsx new file mode 100644 index 0000000..13d7f6a --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/containers/goals/department/DepartmentGoalsControls.tsx @@ -0,0 +1,125 @@ +import React, { useEffect, useState } from "react"; +import { + IDepartmentGoals, + IDepartmentGoalsInput, + IGoalChangeRequest +} from "../../../shared/models/Goal"; +import { IDepartment } from "../../../shared/models/Department"; +import Translate from "../../../translate/Translate"; +import XLSX from "xlsx"; +import GoalsDisplay from "../../../components/goal/GoalsDisplay"; +import GoalService from "../../../shared/services/GoalService"; +import { Product2 } from "../../../shared/enumerations/Product"; +import { FormGroup } from "reactstrap"; + +export interface IProps { + inputForm: IDepartmentGoalsInput; + departments: IDepartment[]; + onChange: (newInput: IDepartmentGoalsInput) => void; + onSave: () => void; + onExport: () => void; + onImport: (file: File) => void; + display?: string; + allowSave: boolean; +} + +const DepartmentGoalsControls: React.FC = (input: IProps) => { + const [departmentId, setDepartmentId] = useState( + input.inputForm.departmentId + ); + const [year, setYear] = useState(input.inputForm.year); + + useEffect(() => { + input.onChange({ + year: year, + departmentId: departmentId + }); + }, [departmentId, year]); + + const years = [year - 2, year - 1, year, year + 1, year + 2]; + + function fileChange(e: React.ChangeEvent) { + const fileDom = e.target; + if (fileDom && fileDom.files && fileDom.files.length) { + input.onImport(fileDom.files[0]); + } + } + + return ( + + ); +}; + +export default DepartmentGoalsControls; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/containers/goals/department/DepartmentGoalsDetail.test.tsx b/LCU.WebApps.Operations.IncentiveManager/src/containers/goals/department/DepartmentGoalsDetail.test.tsx new file mode 100644 index 0000000..2415d06 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/containers/goals/department/DepartmentGoalsDetail.test.tsx @@ -0,0 +1,133 @@ +import { mount } from "enzyme"; +import React from "react"; +import * as reactRedux from "react-redux"; +import AsyncState from "../../../shared/enumerations/AsyncState"; +import GoalTable from "../../../components/goal/GoalTable"; +import DepartmentGoalsDetail, { + IDepartmentGoalsDetailProps +} from "./DepartmentGoalsDetail"; + +describe("DepartmentGoalsDetail", () => { + const useSelectorMock = jest.spyOn(reactRedux, "useSelector"); + const useDispatchMock = jest.spyOn(reactRedux, "useDispatch"); + + beforeEach(() => { + useSelectorMock.mockClear(); + useDispatchMock.mockClear(); + }); + + it("should display waiting while pending", () => { + const dummyDispatch = jest.fn(); + useDispatchMock.mockReturnValue(dummyDispatch); + useSelectorMock.mockReturnValue(testGoalsDataLoading); + + const detailInput: IDepartmentGoalsDetailProps = { + input: { + departmentId: "1", + year: 2021 + } + }; + + const wrapper = mount( + + ); + expect(wrapper.find("div").html().toString()).toBe( + "
Please Wait....
" + ); + }); + + it("should display data when successful", () => { + const dummyDispatch = jest.fn(); + useDispatchMock.mockReturnValue(dummyDispatch); + useSelectorMock.mockReturnValue(testGoalsData); + + const detailInput: IDepartmentGoalsDetailProps = { + input: { + departmentId: "1", + year: 2021 + } + }; + + const wrapper = mount( + + ); + expect(wrapper.find("GoalTable").length).toBe(1); + }); +}); + +// Simulating IGoalState +const testGoalsDataLoading = { + userGoals: { + state: AsyncState.Loading, + data: [] + } +}; + +// Simulating IGoalState +const testGoalsData = { + userGoals: { + state: AsyncState.Success, + data: [ + { + id: 1077, + departmentId: 103, + personId: null, + startDate: "2021-08-01T00:00:00", + endDate: "2021-08-31T23:59:59", + product: "NewChecking", + value: 1, + lastUpdatedBy: "George Frick", + lastUpdated: "2021-08-02T13:29:22.5617363", + lastUpdatedByType: "Associate", + createdBy: "George Frick", + created: "2021-08-02T13:29:22.5616949", + createdByType: "Associate" + }, + { + id: 1078, + departmentId: 103, + personId: null, + startDate: "2021-08-01T00:00:00", + endDate: "2021-08-31T23:59:59", + product: "NewChecking", + value: 1, + lastUpdatedBy: "George Frick", + lastUpdated: "2021-08-02T13:29:23.5158345", + lastUpdatedByType: "Associate", + createdBy: "George Frick", + created: "2021-08-02T13:29:23.5157728", + createdByType: "Associate" + }, + { + id: 1079, + departmentId: 103, + personId: null, + startDate: "2021-08-01T00:00:00", + endDate: "2021-08-31T23:59:59", + product: "NewChecking", + value: 1, + lastUpdatedBy: "George Frick", + lastUpdated: "2021-08-02T13:29:23.5228421", + lastUpdatedByType: "Associate", + createdBy: "George Frick", + created: "2021-08-02T13:29:23.5226656", + createdByType: "Associate" + }, + { + id: 1080, + departmentId: 103, + personId: null, + startDate: "2021-08-01T00:00:00", + endDate: "2021-08-31T23:59:59", + product: "NewChecking", + value: 1, + lastUpdatedBy: "George Frick", + lastUpdated: "2021-08-02T13:29:23.5330519", + lastUpdatedByType: "Associate", + createdBy: "George Frick", + created: "2021-08-02T13:29:23.5329406", + createdByType: "Associate" + } + ] + } +}; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/containers/goals/department/DepartmentGoalsDetail.tsx b/LCU.WebApps.Operations.IncentiveManager/src/containers/goals/department/DepartmentGoalsDetail.tsx new file mode 100644 index 0000000..4285a5c --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/containers/goals/department/DepartmentGoalsDetail.tsx @@ -0,0 +1,44 @@ +import React, { useEffect } from "react"; +import { queryGoals } from "../../../actions/GoalActions"; +import { IAppState } from "../../../store/Store"; +import { useDispatch, useSelector } from "react-redux"; +import { IGoalState } from "../../../reducers/GoalReducer"; +import AsyncState from "../../../shared/enumerations/AsyncState"; +import GoalTable from "../../../components/goal/GoalTable"; +import { IDepartmentGoalsInput } from "../../../shared/models/Goal"; +import { IMonthRange } from "../../../shared/types/IMonthRange"; + +export interface IDepartmentGoalsDetailProps { + input: IDepartmentGoalsInput; +} + +const DepartmentGoalsDetail: React.FC = ( + props +) => { + const dispatch = useDispatch(); + + const goalState = useSelector( + (state) => state.goalState + ); + const departmentGoals = goalState.userGoals; + + useEffect(() => { + const range: IMonthRange = { + fromMonth: 1, + fromYear: props.input.year, + toMonth: 12, + toYear: props.input.year + }; + const includePersonnel = false; + dispatch(queryGoals(range, includePersonnel, props.input.departmentId)); + }, []); + + return departmentGoals.state == AsyncState.Success && + departmentGoals.data ? ( + + ) : ( +
Please Wait....
+ ); +}; + +export default DepartmentGoalsDetail; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/containers/goals/department/DepartmentGoalsEditor.test.tsx b/LCU.WebApps.Operations.IncentiveManager/src/containers/goals/department/DepartmentGoalsEditor.test.tsx new file mode 100644 index 0000000..a9334be --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/containers/goals/department/DepartmentGoalsEditor.test.tsx @@ -0,0 +1,73 @@ +import { shallow } from "enzyme"; +import React from "react"; +import DepartmentGoalsEditor, { + IDepartmentGoalsEditorProps +} from "./DepartmentGoalsEditor"; +import * as reactRedux from "react-redux"; +import GoalService from "../../../shared/services/GoalService"; + +describe("DepartmentGoalsEditor", () => { + const useSelectorMock = jest.spyOn(reactRedux, "useSelector"); + const useDispatchMock = jest.spyOn(reactRedux, "useDispatch"); + const exportXlsSpy = jest.spyOn( + GoalService, + "exportAllGoalsForYearByProduct" + ); + + beforeEach(() => { + useSelectorMock.mockClear(); + useDispatchMock.mockClear(); + }); + + it("should call the export service with product goals and a monthly totals", () => { + useSelectorMock.mockImplementation(() => ({ + departmentGoals: { + data: { + departmentId: 1, + year: 2021, + monthlyGoals: [ + { + productGoals: { + CreditCard: 3, + GAPInsurance: 8, + LIC: 9, + Mortgage: 11, + NewChecking: 12, + Vehicle: 13 + } + }, + { + productGoals: { + CreditCard: 3, + GAPInsurance: 8, + LIC: 9, + Mortgage: 11, + NewChecking: 12, + Vehicle: 13 + } + } + ] + } + } + })); + const wrapper = renderDepartmentGoalsEditor(); + wrapper.find("DepartmentGoalsControls").simulate("export"); + expect(exportXlsSpy).toHaveBeenCalledTimes(1); + }); +}); + +function renderDepartmentGoalsEditor(args = {}) { + const defaultProps: IDepartmentGoalsEditorProps = { + departments: [ + { + businessLineCode: "BCHOPS", + departmentName: "TestBranch", + id: 1, + state: "Active" + } + ] + } as IDepartmentGoalsEditorProps; + + const props = { ...defaultProps, ...args }; + return shallow(); +} diff --git a/LCU.WebApps.Operations.IncentiveManager/src/containers/goals/department/DepartmentGoalsEditor.tsx b/LCU.WebApps.Operations.IncentiveManager/src/containers/goals/department/DepartmentGoalsEditor.tsx new file mode 100644 index 0000000..8ae5d83 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/containers/goals/department/DepartmentGoalsEditor.tsx @@ -0,0 +1,238 @@ +import React, { useEffect, useState } from "react"; +import { getDepartmentGoals } from "../../../actions/GoalActions"; +import { IAppState } from "../../../store/Store"; +import { useDispatch, useSelector } from "react-redux"; +import moment from "moment"; +import { IGoalState } from "../../../reducers/GoalReducer"; +import AsyncState from "../../../shared/enumerations/AsyncState"; +import DepartmentGoalsTable from "./DepartmentGoalsTable"; +import { + IDepartmentGoals, + IDepartmentGoalsInput, + IGoalChangeRequest +} from "../../../shared/models/Goal"; +import DepartmentGoalsControls from "./DepartmentGoalsControls"; +import { Product2 } from "../../../shared/enumerations/Product"; +import GoalService from "../../../shared/services/GoalService"; +import { IDepartment } from "../../../shared/models/Department"; +import TabView from "../../../components/tab-view/TabView"; +import DepartmentGoalsDetail from "./DepartmentGoalsDetail"; +import Translate from "../../../translate/Translate"; +import XLSX from "xlsx"; +import { Alert } from "reactstrap"; + +const DepartmentGoalsTranslate = Translate.Get.containers.goals.department; + +export interface IDepartmentGoalsEditorProps { + departments: IDepartment[]; +} + +enum ImportState { + NONE, + LOADING, + PROCESSING, + SUBMITTING +} + +const DepartmentGoalsEditor: React.FC = ( + props +) => { + const dispatch = useDispatch(); + const [input, setInput] = useState({ + departmentId: props.departments[0].id + "", + year: moment().year() + }); + const [importState, setImportState] = useState( + ImportState.NONE + ); + const [lastSave, setLastSave] = useState(""); + + const [pendingChanges, setPendingChanges] = useState( + [] + ); + + const goalState = useSelector( + (state) => state.goalState + ); + const departmentGoals = goalState.departmentGoals; + + useEffect(() => { + setPendingChanges([]); + dispatch(getDepartmentGoals(input)); + }, [input, lastSave]); + + const queueChanges = (label: string, type: Product2, value: number) => { + const index = pendingChanges.findIndex( + (element: IGoalChangeRequest) => + element.product == type && + element.startDate.month() == GoalService.getMonthIndex(label) + ); + const change = GoalService.convertToGoalChanges( + label, + type, + value, + parseInt(input.departmentId, 10), + input.year + ); + const changes = pendingChanges.slice(); + if (index >= 0) { + if (isNaN(value)) { + changes.splice(index, 1); // remove it. + } else { + changes.splice(index, 1, change); // replace + } + } else { + if (isNaN(value)) { + return; + } + changes.push(change); // add + } + setPendingChanges(changes); + }; + + const isValidChanges = (): boolean => { + if (pendingChanges && pendingChanges.length > 0) { + return GoalService.isValidDepartmentChanges(pendingChanges); + } + return false; + }; + + const saveChanges = () => { + if (isValidChanges()) { + GoalService.savePendingChanges(pendingChanges).then(() => { + setLastSave("Changes Saved " + moment().format("LTS")); + setImportState(ImportState.NONE); + }); + } + }; + + const importCsv = (file: File) => { + setImportState(ImportState.LOADING); + const reader = new FileReader(); + let existingData: IDepartmentGoals[] = []; + + reader.onerror = function () { + setImportState(ImportState.NONE); + }; + reader.onload = (e) => { + setImportState(ImportState.PROCESSING); + const wb = XLSX.read(e.target!.result, { type: "array" }); + /* Get first worksheet */ + let allChanges: IGoalChangeRequest[] = []; + wb.SheetNames.forEach((sheetName, sheetIdx) => { + const ws = wb.Sheets[sheetName]; + const data = XLSX.utils.sheet_to_json(ws, { header: 1 }); + allChanges.push( + ...GoalService.ImportGoalSheet( + sheetName, + input.year, + data, + existingData + ) + ); + }); + if (allChanges.length) { + setPendingChanges(allChanges); + setImportState(ImportState.SUBMITTING); + } else { + setImportState(ImportState.NONE); + } + }; + GoalService.getAllDepartmentGoals(input.year).then( + (allDepartmentsAllGoals) => { + if (allDepartmentsAllGoals.data) { + existingData = allDepartmentsAllGoals.data; + reader.readAsArrayBuffer(file); + } + } + ); + }; + + const exportCsv = () => { + const year = input.year; + const departments = props.departments; + GoalService.exportAllGoalsForYearByProduct(year, departments); + }; + + const getControls = () => { + switch (importState) { + default: + case ImportState.NONE: + return ( + setInput(change)} + onSave={saveChanges} + onExport={exportCsv} + display={lastSave} + departments={props.departments} + allowSave={isValidChanges()} + onImport={importCsv} + /> + ); + case ImportState.LOADING: + return ( + +

+ {DepartmentGoalsTranslate.states.loading} +

+
+ ); + case ImportState.PROCESSING: + return ( + +

+ {DepartmentGoalsTranslate.states.processing} +

+
+ ); + case ImportState.SUBMITTING: + return ( + +

+ Processed {pendingChanges.length} changes.{" "} + +

+
+ ); + } + }; + + return ( +
+ {getControls()} + {departmentGoals.state == AsyncState.Success && + departmentGoals.data ? ( + + ) + }, + { + header: DepartmentGoalsTranslate.tabs.detail, + element: + } + ]} + /> + ) : ( +
Please Wait....
+ )} +
+ ); +}; + +export default DepartmentGoalsEditor; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/containers/goals/department/DepartmentGoalsPage.tsx b/LCU.WebApps.Operations.IncentiveManager/src/containers/goals/department/DepartmentGoalsPage.tsx new file mode 100644 index 0000000..1eef45a --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/containers/goals/department/DepartmentGoalsPage.tsx @@ -0,0 +1,35 @@ +import React, { useEffect, useState } from "react"; +import DepartmentGoalsEditor from "./DepartmentGoalsEditor"; +import DepartmentService from "../../../shared/services/DepartmentService"; +import { IDepartment } from "../../../shared/models/Department"; +import { Alert } from "reactstrap"; + +const DepartmentGoalsPage: React.FC = () => { + const [departments, setDepartments] = useState(null); + + useEffect(() => { + DepartmentService.getAllDepartments(true) + .then((response) => { + setDepartments(response.data || []); + }) + .catch(() => { + setDepartments([]); + }); + }, []); + + if (departments && departments.length === 0) { + return ( + + No departments found for your role + + ); + } + + return departments && departments.length > 0 ? ( + + ) : ( +
Please Wait....
+ ); +}; + +export default DepartmentGoalsPage; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/containers/goals/department/DepartmentGoalsTable.test.tsx b/LCU.WebApps.Operations.IncentiveManager/src/containers/goals/department/DepartmentGoalsTable.test.tsx new file mode 100644 index 0000000..d5283d7 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/containers/goals/department/DepartmentGoalsTable.test.tsx @@ -0,0 +1,140 @@ +import { shallow } from "enzyme"; +import React from "react"; +import { + IDepartmentGoals, + IGoalChangeRequest, + IPersonnelGoalsInput +} from "../../../shared/models/Goal"; +import { Product2 } from "../../../shared/enumerations/Product"; +import DepartmentGoalsTable from "./DepartmentGoalsTable"; + +describe("A Table for editing department goals", () => { + it("Displays a valid department goal edit table", () => { + const departmentGoals: IDepartmentGoals = { + departmentId: 99, + year: 2021, + monthlyGoals: [ + { productGoals: {} }, + { productGoals: {} }, + { productGoals: {} }, + { productGoals: {} }, + { productGoals: {} }, + { productGoals: {} }, + { productGoals: {} }, + { productGoals: {} }, + { productGoals: {} }, + { productGoals: {} }, + { productGoals: {} }, + { productGoals: {} } + ] + }; + const pendingChanges: IGoalChangeRequest[] = []; + const input: IPersonnelGoalsInput = { + year: 2021, + departmentId: "99", + month: 2 + }; + const onChange = () => {}; + let wrapper = shallow( + + ); + expect(wrapper.find("[id='department-goals-table']").length).toBe(1); + }); + + describe("Products with department goals", () => { + let departmentGoals: IDepartmentGoals; + let productWithDepartmentGoals: Product2[]; + + beforeAll(() => { + departmentGoals = { + departmentId: 99, + year: 2021, + monthlyGoals: [ + { productGoals: {} }, + { productGoals: {} }, + { productGoals: {} }, + { productGoals: {} }, + { productGoals: {} }, + { productGoals: {} }, + { productGoals: {} }, + { productGoals: {} }, + { productGoals: {} }, + { productGoals: {} }, + { productGoals: {} }, + { productGoals: {} } + ] + }; + + productWithDepartmentGoals = Product2.Products.filter( + (product) => product.hasDepartmentGoals + ); + }); + + it("Displays titles in the table header for products that have department goals", () => { + const wrapper = shallow( + {}} + pendingChanges={[]} + /> + ); + const productTitles = productWithDepartmentGoals.map( + (product) => product.title + ); + const tableHeaders = wrapper.find("thead > tr > th").slice(1); + + expect(tableHeaders.length).toEqual( + productWithDepartmentGoals.length + ); + tableHeaders.forEach((th) => + expect(productTitles).toContain(th.text()) + ); + }); + + it("Displays only products with department goals in each GoalEditCell", () => { + const wrapper = shallow( + {}} + pendingChanges={[]} + /> + ); + const tableRows = wrapper.find("tbody > tr"); + + tableRows.forEach((tr) => { + const goalEditCells = tr.find("GoalEditCell"); + expect(goalEditCells.length).toBe( + productWithDepartmentGoals.length + ); + goalEditCells.forEach((cell) => { + const goal: { type: Product2 } = cell.prop("goal"); + expect(goal.type.hasDepartmentGoals).toBe(true); + }); + }); + }); + + it("Displays only products with department goals in each AnnualProductTotal cell ", () => { + const wrapper = shallow( + {}} + pendingChanges={[]} + /> + ); + const productsInFooter: Product2[] = wrapper + .find("tfoot AnnualProductTotal") + .map((cell) => cell.prop("product")); + + expect(productsInFooter.length).toEqual( + productWithDepartmentGoals.length + ); + productsInFooter.forEach((product) => + expect(product.hasDepartmentGoals).toBe(true) + ); + }); + }); +}); diff --git a/LCU.WebApps.Operations.IncentiveManager/src/containers/goals/department/DepartmentGoalsTable.tsx b/LCU.WebApps.Operations.IncentiveManager/src/containers/goals/department/DepartmentGoalsTable.tsx new file mode 100644 index 0000000..783190d --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/containers/goals/department/DepartmentGoalsTable.tsx @@ -0,0 +1,91 @@ +import React from "react"; +import { + IDepartmentGoals, + IGoalChangeRequest +} from "../../../shared/models/Goal"; +import { Product2 } from "../../../shared/enumerations/Product"; +import GoalEditCell from "../GoalEditCell"; +import GoalService from "../../../shared/services/GoalService"; +import AnnualProductTotal from "./AnnualProductTotal"; + +export interface IProps { + departmentGoals: IDepartmentGoals; + pendingChanges: IGoalChangeRequest[]; + onChange: (label: string, type: Product2, value: number) => void; +} + +const DepartmentGoalsTable: React.FC = (props: IProps) => { + const onAction = (label: string, type: Product2, value: number) => { + props.onChange(label, type, value); + }; + + const productsWithDepartmentGoals = Product2.Products.filter( + (product) => product.hasDepartmentGoals + ); + + return ( + + + + + {productsWithDepartmentGoals.map((product) => ( + + ))} + + + + {GoalService.Months.map((month, index: number) => ( + + + {productsWithDepartmentGoals.map((product, index2) => ( + + ))} + + ))} + + + + + {productsWithDepartmentGoals.map((product) => ( + + ))} + + +
Month + {product.title} +
{month} + onAction(month, type, value)} + /> +
Totals + +
+ ); +}; + +export default DepartmentGoalsTable; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/containers/goals/personnel/MonthlyProductTotal.test.tsx b/LCU.WebApps.Operations.IncentiveManager/src/containers/goals/personnel/MonthlyProductTotal.test.tsx new file mode 100644 index 0000000..1c33f83 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/containers/goals/personnel/MonthlyProductTotal.test.tsx @@ -0,0 +1,150 @@ +import { mount } from "enzyme"; +import React from "react"; +import MonthlyProductTotal from "./MonthlyProductTotal"; +import { Product2 } from "../../../shared/enumerations/Product"; +import { IEmployee } from "../../../shared/models/Employee"; +import moment from "moment"; +import { UserType } from "../../../shared/models/Auditable"; + +describe("A component for monthly product totals", () => { + const mockUser: IEmployee = { + role: { + name: "baseRole", + rank: 1, + permissions: [], + description: "fake role", + hasCommissions: true + }, + departments: [ + { + departmentId: 55, + employeeId: "11771", + isHome: true, + note: "fake home assignment", + startDate: moment(), + endDate: moment(), + created: moment(), + createdBy: "nobody", + createdByType: UserType.System, + lastUpdated: moment(), + id: 5, + lastUpdatedBy: "nobody", + lastUpdatedByType: UserType.System + } + ], + baseRole: "none", + homeDepartment: "none", + personId: 555, + adEmail: "test@test.com", + employeeId: "11771", + fullName: "Test McTester", + additionalPermissions: [] + }; + + it("Displays a valid total when everything is -zero-", () => { + const data = { + personnelGoals: { + departmentId: 5, + year: 2020, + month: 1, + personnel: [mockUser], // IEmployee[]; + goals: { + "11771": { + productGoals: { + NewChecking: 0, + CreditCard: 0, + LIC: 0, + Mortgage: 0, + Vehicle: 0 + } + } + } // PerPersonGoalType; + }, // IPersonnelGoals; + pendingChanges: [], // IGoalChangeRequest[]; + product: Product2.Vehicle, + departmentTotal: 0 + }; + const wrapper = mount( + + ); + expect(wrapper.html()).toBe('0 (0%)'); + }); + + it("Displays a valid total when everything is 100%", () => { + const data = { + personnelGoals: { + departmentId: 5, + year: 2020, + month: 1, + personnel: [mockUser], // IEmployee[]; + goals: { + "11771": { + productGoals: { + NewChecking: 5, + CreditCard: 5, + LIC: 0, + Mortgage: 0, + Vehicle: 25 + } + } + } // PerPersonGoalType; + }, // IPersonnelGoals; + pendingChanges: [], // IGoalChangeRequest[]; + product: Product2.Vehicle, + departmentTotal: 25 + }; + const wrapper = mount( + + ); + expect(wrapper.html()).toBe( + '25 (100%)' + ); + }); + + it("Displays a valid total when everything is 50%", () => { + const data = { + personnelGoals: { + departmentId: 5, + year: 2020, + month: 1, + personnel: [mockUser], // IEmployee[]; + goals: { + "11771": { + productGoals: { + NewChecking: 3, + CreditCard: 510, + GAPInsurance: 610, + LIC: 9, + Mortgage: 4, + Vehicle: 10 + } + } + } // PerPersonGoalType; + }, // IPersonnelGoals; + pendingChanges: [], // IGoalChangeRequest[]; + product: Product2.Vehicle, + departmentTotal: 20 + }; + const wrapper = mount( + + ); + expect(wrapper.html()).toBe( + '10 (50%)' + ); + }); +}); diff --git a/LCU.WebApps.Operations.IncentiveManager/src/containers/goals/personnel/MonthlyProductTotal.tsx b/LCU.WebApps.Operations.IncentiveManager/src/containers/goals/personnel/MonthlyProductTotal.tsx new file mode 100644 index 0000000..d6dd232 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/containers/goals/personnel/MonthlyProductTotal.tsx @@ -0,0 +1,54 @@ +import React from "react"; +import { + IGoalChangeRequest, + IPersonnelGoals +} from "../../../shared/models/Goal"; +import { Product2 } from "../../../shared/enumerations/Product"; +import GoalService from "../../../shared/services/GoalService"; + +interface GoalTotalProps { + personnelGoals: IPersonnelGoals; + pendingChanges: IGoalChangeRequest[]; + product: Product2; + departmentTotal: number; +} + +const MonthlyProductTotal: React.FC = ( + props: GoalTotalProps +) => { + const total = GoalService.sumPersonnelGoalsForProduct( + props.personnelGoals, + props.product + ); + const totalPending = GoalService.sumPendingPersonnelGoalsForProduct( + props.personnelGoals, + props.pendingChanges, + props.product + ); + + let percentage = Math.floor( + ((totalPending !== total ? totalPending : total) / + props.departmentTotal) * + 100 + ); + if (isNaN(percentage)) { + percentage = 0; + } + const percentStr = " (" + percentage + "%)"; + let outputStr = + GoalService.formatMonthlyGoal(total, props.product) + percentStr; + let className = "text-normal"; + + if (totalPending !== total) { + outputStr = + GoalService.formatMonthlyGoal(total, props.product) + + " -> " + + GoalService.formatMonthlyGoal(totalPending, props.product) + + percentStr; + className = percentage < 100 ? "text-danger" : "text-success"; + } + + return {outputStr}; +}; + +export default MonthlyProductTotal; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/containers/goals/personnel/PersonnelGoalsControls.test.tsx b/LCU.WebApps.Operations.IncentiveManager/src/containers/goals/personnel/PersonnelGoalsControls.test.tsx new file mode 100644 index 0000000..c1ae152 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/containers/goals/personnel/PersonnelGoalsControls.test.tsx @@ -0,0 +1,129 @@ +/* eslint-disable @typescript-eslint/no-empty-function */ +import { mount, shallow } from "enzyme"; +import React from "react"; +import { IPersonnelGoalsInput } from "../../../shared/models/Goal"; +import PersonnelGoalsControls from "./PersonnelGoalsControls"; +import { IDepartment } from "../../../shared/models/Department"; +import { act } from "react-dom/test-utils"; +import AsyncState from "../../../shared/enumerations/AsyncState"; +import { IGoalDispersalReceiptItem } from "../../../shared/models/GoalDispersal"; + +describe("A component for controlling personnel goals view", () => { + it("Displays a valid personnel goals controls", () => { + const input: IPersonnelGoalsInput = { + year: 2021, + departmentId: "99", + month: 2, + isShowAllPersonnelChecked: false + }; + const onChange = () => {}; + const onSave = () => {}; + const onExport = () => {}; + const departments: IDepartment[] = [ + { + state: "Active", + departmentName: "Test Dept", + id: 99, + businessLineCode: "TEST", + departmentId: 99 + } + ]; + const receiptContainer = { + state: AsyncState.Success, + data: { + departmentId: 99, + goalDispersalErrors: [] + } + }; + const wrapper = shallow( + {}} + /> + ); + expect( + wrapper.find("[data-test='personnel-goals-controls']").length + ).toBe(1); + }); + + it("Calls onChange show all personnel is changed", async () => { + const input: IPersonnelGoalsInput = { + year: 2021, + departmentId: "99", + month: 2, + isShowAllPersonnelChecked: false + }; + const stubOnChange = jest.fn(); + const onSave = () => {}; + const onExport = () => {}; + const departments: IDepartment[] = [ + { + state: "Active", + departmentName: "Test Dept", + id: 99, + businessLineCode: "TEST", + departmentId: 99 + } + ]; + const receiptContainer = { + state: AsyncState.Success, + data: { + departmentId: 99, + goalDispersalErrors: [] + } + }; + const wrapper = mount( + {}} + /> + ); + + wrapper.update(); + + stubOnChange.mockClear(); + expect(stubOnChange).not.toHaveBeenCalled(); + + await act(async () => { + wrapper + .find("#toggle-all-personnel input") + .simulate("change", { target: { checked: true } }); + }); + + wrapper.update(); + expect(stubOnChange).toHaveBeenCalledTimes(1); + expect(stubOnChange).toHaveBeenLastCalledWith({ + year: 2021, + departmentId: "99", + month: 2, + isShowAllPersonnelChecked: true + }); + + await act(async () => { + wrapper + .find("#toggle-all-personnel input") + .simulate("change", { target: { checked: false } }); + }); + + wrapper.update(); + expect(stubOnChange).toHaveBeenCalledTimes(2); + expect(stubOnChange).toHaveBeenLastCalledWith({ + year: 2021, + departmentId: "99", + month: 2, + isShowAllPersonnelChecked: false + }); + }); +}); diff --git a/LCU.WebApps.Operations.IncentiveManager/src/containers/goals/personnel/PersonnelGoalsControls.tsx b/LCU.WebApps.Operations.IncentiveManager/src/containers/goals/personnel/PersonnelGoalsControls.tsx new file mode 100644 index 0000000..3261de9 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/containers/goals/personnel/PersonnelGoalsControls.tsx @@ -0,0 +1,203 @@ +import React, { useEffect, useState } from "react"; +import { IPersonnelGoalsInput } from "../../../shared/models/Goal"; +import GoalService from "../../../shared/services/GoalService"; +import { IDepartment } from "../../../shared/models/Department"; +import Translate from "../../../translate/Translate"; +import { CustomInput } from "reactstrap"; +import { Button } from "reactstrap"; +import DispersalForm from "../../../components/goal/dispersal/DispersalForm"; +import { + AllowedGoalDispersalStrategy, + ICustomWeightedDispersalRequest, + IGoalDispersalReceipt, + IGoalDispersalRequest +} from "../../../shared/models/GoalDispersal"; +import IAsyncStateContainer from "../../../shared/types/IAsyncStateContainer"; +import moment from "moment"; +import { ICustomWeightedDispersalFormData } from "./../../../shared/models/GoalDispersal"; + +export interface IProps { + inputForm: IPersonnelGoalsInput; + departments: IDepartment[]; + onChange: (newInput: IPersonnelGoalsInput) => void; + onExport: () => void; + onSave: () => void; + submitDispersalViaExtrapolation: (request: IGoalDispersalRequest) => void; + submitCustomWeightedDispersal: ( + request: ICustomWeightedDispersalRequest + ) => void; + dispersalReceiptContainer: IAsyncStateContainer; + display?: string; + allowSave: boolean; +} + +const PersonnelGoalsControls: React.FC = (input: IProps) => { + const [departmentId, setDepartmentId] = useState( + input.inputForm.departmentId + ); + const [year, setYear] = useState(input.inputForm.year); + const [month, setMonth] = useState(input.inputForm.month); + const [isShowAllPersonnelChecked, setIsShowAllPersonnelChecked] = useState( + input.inputForm.isShowAllPersonnelChecked + ); + const [isDispersalFormOpen, toggleDispersalForm] = useState(false); + + useEffect(() => { + input.onChange({ + year: year, + departmentId: departmentId, + month: month, + isShowAllPersonnelChecked: isShowAllPersonnelChecked + }); + toggleDispersalForm(false); + }, [departmentId, year, month, isShowAllPersonnelChecked]); + + const years = [year - 2, year - 1, year, year + 1, year + 2]; + + //Expand if add more strategies, expand if requires additional arguments + const handleStatelessDispersal = ( + strategy: AllowedGoalDispersalStrategy + ) => { + switch (strategy) { + default: + input.submitDispersalViaExtrapolation({ + departmentId: departmentId, + month: month, + year: year + }); + } + }; + + const handleCustomWeightedDispersal = ( + formData: ICustomWeightedDispersalFormData + ) => { + input.submitCustomWeightedDispersal({ + departmentId: departmentId, + month: month, + year: year, + percentageByRole: formData.weightsByRole, + dispersalProduct: formData.dispersalProduct + }); + }; + + return ( +
+ +
+ { + handleStatelessDispersal(strategy); + }} + submitState={input.dispersalReceiptContainer.state} + confirmWeightedDispersal={(formData) => { + handleCustomWeightedDispersal(formData); + }} + /> +
+
+ ); +}; + +export default PersonnelGoalsControls; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/containers/goals/personnel/PersonnelGoalsDetail.test.tsx b/LCU.WebApps.Operations.IncentiveManager/src/containers/goals/personnel/PersonnelGoalsDetail.test.tsx new file mode 100644 index 0000000..d1beea8 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/containers/goals/personnel/PersonnelGoalsDetail.test.tsx @@ -0,0 +1,137 @@ +import { mount } from "enzyme"; +import React from "react"; +import * as reactRedux from "react-redux"; +import AsyncState from "../../../shared/enumerations/AsyncState"; +import PersonnelGoalsDetail, { + IPersonnelGoalsDetailProps +} from "./PersonnelGoalsDetail"; +import GoalTable from "../../../components/goal/GoalTable"; + +describe("PersonnelGoalsDetail", () => { + const useSelectorMock = jest.spyOn(reactRedux, "useSelector"); + const useDispatchMock = jest.spyOn(reactRedux, "useDispatch"); + + beforeEach(() => { + useSelectorMock.mockClear(); + useDispatchMock.mockClear(); + }); + + it("should display waiting while pending", () => { + const dummyDispatch = jest.fn(); + useDispatchMock.mockReturnValue(dummyDispatch); + useSelectorMock.mockReturnValue(testGoalsDataLoading); + + const detailInput: IPersonnelGoalsDetailProps = { + input: { + departmentId: "1", + year: 2021, + month: 6, + isShowAllPersonnelChecked: true + } + }; + + const wrapper = mount( + + ); + expect(wrapper.find("div").html().toString()).toBe( + "
Please Wait....
" + ); + }); + + it("should display data when successful", () => { + const dummyDispatch = jest.fn(); + useDispatchMock.mockReturnValue(dummyDispatch); + useSelectorMock.mockReturnValue(testGoalsData); + + const detailInput: IPersonnelGoalsDetailProps = { + input: { + departmentId: "1", + year: 2021, + month: 6, + isShowAllPersonnelChecked: true + } + }; + + const wrapper = mount( + + ); + expect(wrapper.find("GoalTable").length).toBe(1); + }); +}); + +// Simulating IGoalState +const testGoalsDataLoading = { + userGoals: { + state: AsyncState.Loading, + data: [] + } +}; + +// Simulating IGoalState +const testGoalsData = { + userGoals: { + state: AsyncState.Success, + data: [ + { + id: 1077, + departmentId: 103, + personId: "11412", + startDate: "2021-08-01T00:00:00", + endDate: "2021-08-31T23:59:59", + product: "NewChecking", + value: 1, + lastUpdatedBy: "George Frick", + lastUpdated: "2021-08-02T13:29:22.5617363", + lastUpdatedByType: "Associate", + createdBy: "George Frick", + created: "2021-08-02T13:29:22.5616949", + createdByType: "Associate" + }, + { + id: 1078, + departmentId: 103, + personId: "C00001", + startDate: "2021-08-01T00:00:00", + endDate: "2021-08-31T23:59:59", + product: "NewChecking", + value: 1, + lastUpdatedBy: "George Frick", + lastUpdated: "2021-08-02T13:29:23.5158345", + lastUpdatedByType: "Associate", + createdBy: "George Frick", + created: "2021-08-02T13:29:23.5157728", + createdByType: "Associate" + }, + { + id: 1079, + departmentId: 103, + personId: "11928", + startDate: "2021-08-01T00:00:00", + endDate: "2021-08-31T23:59:59", + product: "NewChecking", + value: 1, + lastUpdatedBy: "George Frick", + lastUpdated: "2021-08-02T13:29:23.5228421", + lastUpdatedByType: "Associate", + createdBy: "George Frick", + created: "2021-08-02T13:29:23.5226656", + createdByType: "Associate" + }, + { + id: 1080, + departmentId: 103, + personId: "11685", + startDate: "2021-08-01T00:00:00", + endDate: "2021-08-31T23:59:59", + product: "NewChecking", + value: 1, + lastUpdatedBy: "George Frick", + lastUpdated: "2021-08-02T13:29:23.5330519", + lastUpdatedByType: "Associate", + createdBy: "George Frick", + created: "2021-08-02T13:29:23.5329406", + createdByType: "Associate" + } + ] + } +}; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/containers/goals/personnel/PersonnelGoalsDetail.tsx b/LCU.WebApps.Operations.IncentiveManager/src/containers/goals/personnel/PersonnelGoalsDetail.tsx new file mode 100644 index 0000000..744b521 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/containers/goals/personnel/PersonnelGoalsDetail.tsx @@ -0,0 +1,42 @@ +import React, { useEffect } from "react"; +import { queryGoals } from "../../../actions/GoalActions"; +import { IAppState } from "../../../store/Store"; +import { useDispatch, useSelector } from "react-redux"; +import { IGoalState } from "../../../reducers/GoalReducer"; +import AsyncState from "../../../shared/enumerations/AsyncState"; +import GoalTable from "../../../components/goal/GoalTable"; +import { IPersonnelGoalsInput } from "../../../shared/models/Goal"; +import { IMonthRange } from "../../../shared/types/IMonthRange"; + +export interface IPersonnelGoalsDetailProps { + input: IPersonnelGoalsInput; +} + +const PersonnelGoalsDetail: React.FC = (props) => { + const dispatch = useDispatch(); + + const goalState = useSelector( + (state) => state.goalState + ); + const departmentGoals = goalState.userGoals; + + useEffect(() => { + const range: IMonthRange = { + fromMonth: props.input.month, + fromYear: props.input.year, + toMonth: props.input.month, + toYear: props.input.year + }; + const includePersonnel = true; + dispatch(queryGoals(range, includePersonnel, props.input.departmentId)); + }, []); + + return departmentGoals.state == AsyncState.Success && + departmentGoals.data ? ( + + ) : ( +
Please Wait....
+ ); +}; + +export default PersonnelGoalsDetail; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/containers/goals/personnel/PersonnelGoalsEditor.test.tsx b/LCU.WebApps.Operations.IncentiveManager/src/containers/goals/personnel/PersonnelGoalsEditor.test.tsx new file mode 100644 index 0000000..e6c7224 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/containers/goals/personnel/PersonnelGoalsEditor.test.tsx @@ -0,0 +1,176 @@ +import { shallow } from "enzyme"; +import React from "react"; +import PersonnelGoalsEditor, { + IGoalsEditorProps +} from "./PersonnelGoalsEditor"; +import ExportCsvService from "../../../shared/services/ExportCsvService"; +import * as reactRedux from "react-redux"; +import PermissionCode from "../../../shared/enumerations/PermissionCode"; + +describe("PersonnelGoalsEditor", () => { + const useSelectorMock = jest.spyOn(reactRedux, "useSelector"); + const useDispatchMock = jest.spyOn(reactRedux, "useDispatch"); + const exportCsvSpy = jest.spyOn( + ExportCsvService, + "exportMonthlyPersonnelGoalsAsCSV" + ); + + beforeEach(() => { + useSelectorMock.mockClear(); + useDispatchMock.mockClear(); + }); + + it("should call the export service with personal goals and totals", () => { + useSelectorMock.mockImplementation(() => ({ + departmentGoals: testdepartmentGoalsData(), + personnelGoals: testPersonnelGoalsData() + })); + + const wrapper = renderPersonnelGoalsEditor(); + wrapper.find("PersonnelGoalsControls").simulate("export"); + + expect(exportCsvSpy).toHaveBeenCalledWith([ + { + goals: { + CreditCard: 3, + GAPInsurance: 8, + LIC: 9, + Mortgage: 11, + NewChecking: 12, + Vehicle: 13 + }, + rowHeader: "Department Goal" + }, + { + goals: { + NewChecking: 0, + CreditCard: 0, + GAPInsurance: 0, + LIC: 0, + Mortgage: 0, + Vehicle: 0 + }, + rowHeader: "One, Test E" + }, + { + goals: { + CreditCard: 0, + GAPInsurance: 0, + LIC: 0, + Mortgage: 0, + NewChecking: 0, + Vehicle: 0 + }, + rowHeader: "Three, Test" + }, + { + goals: { + CreditCard: 0, + GAPInsurance: 0, + LIC: 0, + Mortgage: 0, + NewChecking: 0, + Vehicle: 0 + }, + rowHeader: "Totals" + } + ]); + expect(exportCsvSpy).toHaveBeenCalledTimes(1); + }); +}); + +function renderPersonnelGoalsEditor(args = {}) { + const defaultProps: IGoalsEditorProps = { + departments: [ + { + businessLineCode: "BCHOPS", + departmentName: "TestBranch", + id: 1, + state: "Active" + } + ] + } as IGoalsEditorProps; + + const props = { ...defaultProps, ...args }; + return shallow(); +} + +const testdepartmentGoalsData = () => ({ + data: { + departmentId: 1, + year: 2021, + monthlyGoals: [ + { + productGoals: { + ATMCard: 1, + CheckCard: 2, + CreditCard: 3, + CreditCardBalanceTransfer: 4, + DirectDeposit: 7, + GAPInsurance: 8, + LIC: 9, + MechanicalBreakdownProtection: 10, + Mortgage: 11, + NewChecking: 12, + Vehicle: 13 + } + } + ] + } +}); + +const testPersonnelGoalsData = () => ({ + data: { + departmentId: 1, + year: 2020, + month: 1, + personnel: [ + { + employeeId: "1", + fullName: "One, Test E", + role: { + permissions: [{ code: PermissionCode.HAVE_SALES_GOALS }] + } + }, + { + employeeId: "3", + fullName: "Three, Test", + role: { + permissions: [{ code: PermissionCode.HAVE_SALES_GOALS }] + } + } + ], + goals: { + "1": { + productGoals: { + ATMCard: 0, + CreditCardBalanceTransfer: 0, + CheckCard: 0, + NewChecking: 0, + CreditCard: 0, + DirectDeposit: 0, + GAPInsurance: 0, + LIC: 0, + Mortgage: 0, + MechanicalBreakdownProtection: 0, + Vehicle: 0 + } + }, + "3": { + productGoals: { + ATMCard: 0, + CreditCardBalanceTransfer: 0, + CheckCard: 0, + NewChecking: 0, + CreditCard: 0, + DirectDeposit: 0, + GAPInsurance: 0, + LIC: 0, + Mortgage: 0, + MechanicalBreakdownProtection: 0, + Vehicle: 0 + } + } + } + } +}); diff --git a/LCU.WebApps.Operations.IncentiveManager/src/containers/goals/personnel/PersonnelGoalsEditor.tsx b/LCU.WebApps.Operations.IncentiveManager/src/containers/goals/personnel/PersonnelGoalsEditor.tsx new file mode 100644 index 0000000..e159a43 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/containers/goals/personnel/PersonnelGoalsEditor.tsx @@ -0,0 +1,284 @@ +import React, { useEffect, useState } from "react"; +import { + getDepartmentGoals, + getPersonnelGoals +} from "../../../actions/GoalActions"; +import { IAppState } from "../../../store/Store"; +import { useDispatch, useSelector } from "react-redux"; +import moment from "moment"; +import { IGoalState } from "../../../reducers/GoalReducer"; +import AsyncState from "../../../shared/enumerations/AsyncState"; +import PersonnelGoalsTable from "./PersonnelGoalsTable"; +import { Product2 } from "../../../shared/enumerations/Product"; +import { + IGoalChangeRequest, + IPersonnelGoalsInput +} from "../../../shared/models/Goal"; +import PersonnelGoalsControls from "./PersonnelGoalsControls"; +import GoalService from "../../../shared/services/GoalService"; +import { IDepartment } from "../../../shared/models/Department"; +import ExportCsvService, { + MonthlyPersonnelGoalsCsvData +} from "../../../shared/services/ExportCsvService"; +import TabView from "../../../components/tab-view/TabView"; +import PersonnelGoalsDetail from "./PersonnelGoalsDetail"; +import Translate from "../../../translate/Translate"; +import { + ICustomWeightedDispersalRequest, + IGoalDispersalReceipt, + IGoalDispersalRequest +} from "../../../shared/models/GoalDispersal"; +import IAsyncStateContainer from "../../../shared/types/IAsyncStateContainer"; +import GoalDispersalService from "./../../../shared/services/GoalDispersalService"; +import DispersalFormAlertBar from "../../../components/goal/dispersal/DispersalFormAlertBar"; + +export interface IGoalsEditorProps { + departments: IDepartment[]; +} + +const PersonalGoalsTranslate = Translate.Get.containers.goals.personnel; + +const PersonnelGoalsEditor: React.FC = (props) => { + const dispatch = useDispatch(); + const [input, setInput] = useState({ + departmentId: props.departments[0].id + "", + year: moment().year(), + month: moment().month() + 1, + isShowAllPersonnelChecked: false + }); + const [lastSave, setLastSave] = useState(""); + const [pendingChanges, setPendingChanges] = useState( + [] + ); + const [dispersalReceipt, setDispersalReceipt] = useState< + IAsyncStateContainer + >({ state: AsyncState.Inactive }); + + useEffect(() => { + setPendingChanges([]); + setDispersalReceipt({ state: AsyncState.Inactive }); + dispatch(getPersonnelGoals(input)); + dispatch(getDepartmentGoals(input)); + }, [input, lastSave]); + + const goalState = useSelector( + (state) => state.goalState + ); + const personnelGoals = goalState.personnelGoals; + const filteredPersonnelGoals = personnelGoals.data + ? GoalService.filterPersonnelGoalsByShowAll( + input.isShowAllPersonnelChecked, + personnelGoals.data + ) + : personnelGoals.data; + const departmentGoals = goalState.departmentGoals; + + const queueChanges = (person: string, type: Product2, value: number) => { + const index = pendingChanges.findIndex( + (element: IGoalChangeRequest) => + element.product === type && + element.startDate.year() === input.year && + element.startDate.month() === input.month - 1 && + element.personId === person + ); + const change = convertToGoalChanges(person, type, value); + const changes = pendingChanges.slice(); + if (index >= 0) { + if (isNaN(value)) { + changes.splice(index, 1); // remove it. + } else { + changes.splice(index, 1, change); // replace + } + } else { + if (isNaN(value)) { + return; + } + changes.push(change); // add + } + setPendingChanges(changes); + }; + + // todo, move to service. + const convertToGoalChanges = ( + personId: string, + type: Product2, + value: number + ): IGoalChangeRequest => { + let startDate = moment().utc().set("year", input.year); + startDate = startDate.set("month", input.month - 1); + startDate = startDate.clone().startOf("month"); + const endDate = startDate.clone(); + endDate.add(1, "month"); + endDate.subtract(1, "second"); + + return { + departmentId: parseInt(input.departmentId, 10), + personId: personId, + product: type, + value: value, + startDate: startDate, + endDate: endDate + }; + }; + + const isValidChanges = (): boolean => { + if (pendingChanges && pendingChanges.length > 0) { + return GoalService.isValidPersonnelChanges( + pendingChanges, + filteredPersonnelGoals, + departmentGoals.data + ); + } + return false; + }; + + const saveChanges = () => { + if (isValidChanges()) { + GoalService.savePendingChanges(pendingChanges).then(() => { + setLastSave("Changes Saved " + moment().format("LTS")); + }); + } + }; + + const submitDispersalViaExtrapolation = async ( + request: IGoalDispersalRequest + ) => { + setDispersalReceipt({ state: AsyncState.Loading }); + const res = await GoalDispersalService.DisperseGoalsViaExtrapolation( + request + ); + setDispersalReceipt(res); + if (res.state === AsyncState.Success) { + dispatch(getPersonnelGoals(input)); + } + }; + + const submitcustomWeighteDispersal = async ( + request: ICustomWeightedDispersalRequest + ) => { + setDispersalReceipt({ state: AsyncState.Loading }); + const res = await GoalDispersalService.DisperseGoalsByCustomWeights( + request + ); + setDispersalReceipt(res); + if (res.state === AsyncState.Success) { + dispatch(getPersonnelGoals(input)); + } + }; + + const exportCsv = () => { + if (filteredPersonnelGoals && departmentGoals.data) { + const personnelGoalsData = filteredPersonnelGoals; + const departmentGoalsData = departmentGoals.data; + + const productsWithPersonnelGoals = Product2.Products.filter( + (product) => product.hasPersonnelGoals + ); + + const deptGoals: MonthlyPersonnelGoalsCsvData = { + rowHeader: "Department Goal", + goals: productsWithPersonnelGoals.reduce( + (perProductGoals, product) => ({ + ...perProductGoals, + [product.code]: + departmentGoalsData.monthlyGoals[ + personnelGoalsData.month - 1 + ].productGoals[product.code] + }), + {} + ) + }; + + const goals: MonthlyPersonnelGoalsCsvData[] = personnelGoalsData.personnel.map( + (person) => { + return { + rowHeader: person.fullName, + goals: productsWithPersonnelGoals.reduce( + (perProductGoals, product) => ({ + ...perProductGoals, + [product.code]: + personnelGoalsData.goals[person.employeeId] + .productGoals[product.code] + }), + {} + ) + }; + } + ); + + const goalTotals: MonthlyPersonnelGoalsCsvData = { + rowHeader: "Totals", + goals: productsWithPersonnelGoals.reduce( + (perProductGoals, product) => ({ + ...perProductGoals, + [product.code]: GoalService.sumPersonnelGoalsForProduct( + personnelGoalsData, + product + ) + }), + {} + ) + }; + + ExportCsvService.exportMonthlyPersonnelGoalsAsCSV([ + deptGoals, + ...goals, + goalTotals + ]); + } + }; + + return ( +
+ + setInput(change)} + onSave={saveChanges} + onExport={exportCsv} + display={lastSave} + departments={props.departments} + allowSave={isValidChanges()} + dispersalReceiptContainer={dispersalReceipt} + submitDispersalViaExtrapolation={(request) => + submitDispersalViaExtrapolation(request) + } + submitCustomWeightedDispersal={(request) => + submitcustomWeighteDispersal(request) + } + /> + {personnelGoals.state == AsyncState.Success && + personnelGoals.data && + filteredPersonnelGoals && + departmentGoals.state == AsyncState.Success && + departmentGoals.data ? ( + + ) + }, + { + header: PersonalGoalsTranslate.tabs.detail, + element: + } + ]} + /> + ) : ( +
Please Wait....
+ )} +
+ ); +}; + +export default PersonnelGoalsEditor; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/containers/goals/personnel/PersonnelGoalsPage.tsx b/LCU.WebApps.Operations.IncentiveManager/src/containers/goals/personnel/PersonnelGoalsPage.tsx new file mode 100644 index 0000000..4dbb540 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/containers/goals/personnel/PersonnelGoalsPage.tsx @@ -0,0 +1,35 @@ +import React, { useEffect, useState } from "react"; +import PersonnelGoalsEditor from "./PersonnelGoalsEditor"; +import { IDepartment } from "../../../shared/models/Department"; +import DepartmentService from "../../../shared/services/DepartmentService"; +import { Alert } from "reactstrap"; + +const PersonnelGoalsPage: React.FC = () => { + const [departments, setDepartments] = useState(null); + + useEffect(() => { + DepartmentService.getAllDepartments(undefined, true) + .then((response) => { + setDepartments(response.data || []); + }) + .catch(() => { + setDepartments([]); + }); + }, []); + + if (departments && departments.length === 0) { + return ( + + No departments found for your role + + ); + } + + return departments && departments.length > 0 ? ( + + ) : ( +
Please Wait....
+ ); +}; + +export default PersonnelGoalsPage; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/containers/goals/personnel/PersonnelGoalsTable.test.tsx b/LCU.WebApps.Operations.IncentiveManager/src/containers/goals/personnel/PersonnelGoalsTable.test.tsx new file mode 100644 index 0000000..7aa1a07 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/containers/goals/personnel/PersonnelGoalsTable.test.tsx @@ -0,0 +1,170 @@ +import { shallow } from "enzyme"; +import React from "react"; +import Product2 from "../../../shared/enumerations/Product"; +import { + IDepartmentGoals, + IGoalChangeRequest, + IPersonnelGoals +} from "../../../shared/models/Goal"; +import PersonnelGoalsTable, { IProps } from "./PersonnelGoalsTable"; +import { IEmployee } from "../../../shared/models/Employee"; + +const person: IEmployee = { + employeeId: "11771", + adEmail: "programming@landmarkcu.com", + personId: 0, + fullName: "Test McTester", + homeDepartment: "QA", + baseRole: "QA", + departments: [], + role: { + name: "Role1", + description: "NONE", + permissions: [{ code: "VIEW_DASHBOARD", description: "" }], + hasCommissions: true + }, + additionalPermissions: [], + assignments: [] +}; + +describe("A table for editing personnel goals", () => { + it("Displays a valid personnel goals edit table", () => { + const personnelGoals: IPersonnelGoals = { + year: 2021, + personnel: [person], + departmentId: 99, + month: 1, + goals: { + "11771": { + productGoals: {} + } + } + }; + const departmentGoals: IDepartmentGoals = { + departmentId: 99, + year: 2021, + monthlyGoals: [ + { productGoals: {} }, + { productGoals: {} }, + { productGoals: {} } + ] + }; + const pendingChanges: IGoalChangeRequest[] = []; + // eslint-disable-next-line @typescript-eslint/no-empty-function + const onChange = () => {}; + let wrapper = shallow( + + ); + expect(wrapper.find("[id='personnel-goals-table']").length).toBe(1); + }); + + describe("Products with personnel goals", () => { + it("Displays titles in the table header for products that have personnel goals", () => { + const wrapper = renderPersonnelGoalsTable(); + const expectedProductNames = productWithPersonnelGoals().map( + (product) => product.title + ); + const headerCols = wrapper.find("thead > tr > th").slice(1); + + expect(headerCols.length).toEqual( + productWithPersonnelGoals().length + ); + headerCols.forEach((col) => + expect(expectedProductNames).toContain(col.text()) + ); + }); + + it("Match titles in the department goal row to products that have personnel goals ", () => { + const wrapper = renderPersonnelGoalsTable(); + const headerCols = wrapper + .find("thead > tr.alert-info > td") + .slice(1); + const expectedProductCodes = productWithPersonnelGoals().map( + (product) => product.code + ); + + expect(headerCols.length).toEqual( + productWithPersonnelGoals().length + ); + + headerCols.forEach((col, index) => + expect( + col + .prop("title") + ?.startsWith(`total_${expectedProductCodes[index]}`) + ).toBe(true) + ); + }); + + it("Displays only products with personnel goals in each GoalEditCell", () => { + const wrapper = renderPersonnelGoalsTable(); + const goalEditCells = wrapper.find("tbody GoalEditCell"); + + expect(goalEditCells.length).toEqual( + productWithPersonnelGoals().length + ); + + goalEditCells.forEach((cell) => { + const goal: { type: Product2 } = cell.prop("goal"); + expect(goal.type.hasPersonnelGoals).toBe(true); + }); + }); + + it("Displays only product with personnel goals in each MonthlyProductTotal cell", () => { + const wrapper = renderPersonnelGoalsTable(); + const productsInFooter: Product2[] = wrapper + .find("tfoot MonthlyProductTotal") + .map((cell) => cell.prop("product")); + + expect(productsInFooter.length).toEqual( + productWithPersonnelGoals().length + ); + productsInFooter.forEach((product) => + expect(product.hasPersonnelGoals).toBe(true) + ); + }); + }); +}); + +function renderPersonnelGoalsTable(args = {}) { + const personnelGoals: IPersonnelGoals = { + year: 2021, + personnel: [person], + departmentId: 99, + month: 1, + goals: { + "11771": { + productGoals: {} + } + } + }; + const departmentGoals: IDepartmentGoals = { + departmentId: 99, + year: 2021, + monthlyGoals: [ + { productGoals: {} }, + { productGoals: {} }, + { productGoals: {} } + ] + }; + + const defaultProps: IProps = { + personnelGoals, + departmentGoals, + // eslint-disable-next-line @typescript-eslint/no-empty-function + onChange: () => {}, + pendingChanges: [] + }; + + const props = { ...defaultProps, ...args }; + return shallow(); +} + +function productWithPersonnelGoals() { + return Product2.Products.filter((product) => product.hasPersonnelGoals); +} diff --git a/LCU.WebApps.Operations.IncentiveManager/src/containers/goals/personnel/PersonnelGoalsTable.tsx b/LCU.WebApps.Operations.IncentiveManager/src/containers/goals/personnel/PersonnelGoalsTable.tsx new file mode 100644 index 0000000..b7531d8 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/containers/goals/personnel/PersonnelGoalsTable.tsx @@ -0,0 +1,152 @@ +import React from "react"; +import { + IDepartmentGoals, + IGoalChangeRequest, + IPersonnelGoals +} from "../../../shared/models/Goal"; +import { Product2 } from "../../../shared/enumerations/Product"; +import GoalEditCell from "../GoalEditCell"; +import MonthlyProductTotal from "./MonthlyProductTotal"; +import GoalService from "../../../shared/services/GoalService"; +import UserDisplay from "../../shared/UserDisplay"; + +export interface IProps { + personnelGoals: IPersonnelGoals; + departmentGoals: IDepartmentGoals; + pendingChanges: IGoalChangeRequest[]; + onChange: (label: string, type: Product2, value: number) => void; +} + +const PersonnelGoalsTable: React.FC = (props: IProps) => { + const onAction = (label: string, type: Product2, value: number) => { + props.onChange(label, type, value); + }; + + return ( + + + + + {GoalService.productsWithPersonnelGoals().map((product) => ( + + ))} + + + + {GoalService.productsWithPersonnelGoals().map((product) => ( + + ))} + + + + {GoalService.productsWithPersonnelGoals().map((product) => ( + + ))} + + + + {props.personnelGoals.personnel.map((person, index: number) => ( + + + {GoalService.productsWithPersonnelGoals().map( + (product, index2) => ( + + ) + )} + + ))} + + + + + {GoalService.productsWithPersonnelGoals().map((product) => ( + + ))} + + +
Personnel + {product.title} +
Department Goal + {GoalService.formatMonthlyGoal( + GoalService.getMonthlyGoal( + props.departmentGoals, + props.personnelGoals.month, + product.code + ), + product + )} +
Totals + +
+ + + + onAction( + person.employeeId, + type, + value + ) + } + /> +
Totals + +
+ ); +}; + +export default PersonnelGoalsTable; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/containers/organization-view/BusinessLineTable.tsx b/LCU.WebApps.Operations.IncentiveManager/src/containers/organization-view/BusinessLineTable.tsx new file mode 100644 index 0000000..1b6b866 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/containers/organization-view/BusinessLineTable.tsx @@ -0,0 +1,37 @@ +import React from "react"; +import { AutoTable } from "../../components/auto-table/AutoTable"; +import Translate from "./../../translate/Translate"; +import { IBusinessLine } from "../../shared/models/Settings"; +export interface BusinessLineTableProps { + businessLines: IBusinessLine[]; +} + +const BusinessLineTable: React.FC = (props) => { + const headers = [ + Translate.Get.containers.businessLineTable.headers.code, + Translate.Get.containers.businessLineTable.headers.name, + Translate.Get.containers.businessLineTable.headers.orgCode + ]; + + const cells = [ + (x: IBusinessLine) => x.code, + (x: IBusinessLine) => x.name, + (x: IBusinessLine) => x.orgCode + ]; + + return ( +
+
+
{Translate.Get.containers.businessLineTable.title}
+ +
+
+ ); +}; + +export default BusinessLineTable; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/containers/organization-view/OrganizationTable.tsx b/LCU.WebApps.Operations.IncentiveManager/src/containers/organization-view/OrganizationTable.tsx new file mode 100644 index 0000000..5a225f0 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/containers/organization-view/OrganizationTable.tsx @@ -0,0 +1,32 @@ +import React from "react"; +import { AutoTable } from "../../components/auto-table/AutoTable"; +import Translate from "./../../translate/Translate"; + +export interface OrganizationTableProps { + organizations: string[][]; +} + +const OrganizationTable: React.FC = (props) => { + const headers = [ + Translate.Get.containers.organizationTable.headers.orgCode, + Translate.Get.containers.organizationTable.headers.orgName + ]; + + const cells = [(x: any[]) => x[0], (x: any[]) => x[1]]; + + return ( +
+
+
{Translate.Get.containers.organizationTable.title}
+ +
+
+ ); +}; + +export default OrganizationTable; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/containers/organization-view/OrganizationView.scss b/LCU.WebApps.Operations.IncentiveManager/src/containers/organization-view/OrganizationView.scss new file mode 100644 index 0000000..e69de29 diff --git a/LCU.WebApps.Operations.IncentiveManager/src/containers/organization-view/OrganizationView.tsx b/LCU.WebApps.Operations.IncentiveManager/src/containers/organization-view/OrganizationView.tsx new file mode 100644 index 0000000..7cd1a4a --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/containers/organization-view/OrganizationView.tsx @@ -0,0 +1,122 @@ +import React, { useEffect, useState } from "react"; +import { useSelector } from "react-redux"; +import { IAppState } from "../../store/Store"; +import { RouteComponentProps } from "react-router-dom"; +import { flattenRecord } from "../../shared/services/SettingsService"; +import Translate from "./../../translate/Translate"; +import OrganizationTable from "./OrganizationTable"; +import BusinessLineTable from "./BusinessLineTable"; +import { IDepartmentState } from "./../../reducers/DepartmentReducer"; +import { + getAllDepartments, + updateExistingDepartment, + submitNewDepartment +} from "../../actions/DepartmentActions"; +import { getAllUserAssignments } from "../../actions/UserAssignmentActions"; +import DepartmentTable from "../../components/department-table/DepartmentTable"; +import { useDispatch } from "react-redux"; +import RegionTable from "../../components/region/RegionTable"; +import { IUserAssignmentState } from "../../reducers/UserAssignmentReducer"; +import UserAssignmentService from "../../shared/services/UserAssignmentService"; +import { IProfileState } from "../../reducers/ProfileReducer"; +import { profileSearch } from "../../actions/ProfileActions"; +import { IUserAssignmentDto } from "../../shared/models/UserAssignment"; +import AsyncState from "../../shared/enumerations/AsyncState"; +import IAsyncStateContainer from "../../shared/types/IAsyncStateContainer"; +import { SettingsContext } from "../../shared/providers/SettingsProvider"; + +type OrganizationViewProps = RouteComponentProps; + +const OrganizationView: React.FC = () => { + const dispatch = useDispatch(); + const settings = React.useContext(SettingsContext); + + // In this case we still load departments instead of using settings because + // the appUser may make modifications. It might be smarter to have the appUser + // reload. They'll almost never 'create' a department. + const departmentState = useSelector( + (state) => state.departmentState + ); + + const userAssignmentState = useSelector( + (state) => state.userAssignmentState + ); + + const profileState = useSelector( + (state) => state.profileState + ); + + const [organizations, setOrganizations] = useState([]); + + useEffect(() => { + dispatch(getAllDepartments()); + dispatch(getAllUserAssignments()); + dispatch(profileSearch(null, "RegionalManager")); + }, []); + + // TODO, Handle department creation => Department refresh + useEffect(() => { + dispatch(getAllDepartments()); + }, [departmentState.submitState.data]); + + useEffect(() => { + setOrganizations(flattenRecord(settings?.organizationSet)); + }, [settings]); + + const [ + submitRegionalManagerUpdateState, + setSubmitRegionalManagerUpdateState + ] = useState>({ state: AsyncState.Inactive }); + + const regionalManagerAssignments = UserAssignmentService.filterUserAssignmentsByType( + userAssignmentState.userAssignments.data ?? [], + "RegionalManager" + ); + + const updateRegionalManager = async ( + employeeId: string, + regionCode: string + ) => { + setSubmitRegionalManagerUpdateState({ state: AsyncState.Loading }); + const assignment: IUserAssignmentDto = { + employeeId: employeeId, + key: regionCode + }; + const result = await UserAssignmentService.setRegionalManager( + assignment + ); + setSubmitRegionalManagerUpdateState(result); + dispatch(getAllUserAssignments()); + }; + + return ( +
+

{Translate.Get.containers.organizationView.pageTitle}

+ + + + { + dispatch(updateExistingDepartment(id, updatedData)); + }} + submitNewDepartment={(newDepartment) => { + dispatch(submitNewDepartment(newDepartment)); + }} + submitState={departmentState.submitState} + /> +
+ ); +}; + +export default OrganizationView; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/containers/payout-profile/EligiblePayouts.tsx b/LCU.WebApps.Operations.IncentiveManager/src/containers/payout-profile/EligiblePayouts.tsx new file mode 100644 index 0000000..2bada84 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/containers/payout-profile/EligiblePayouts.tsx @@ -0,0 +1,45 @@ +import React from "react"; +import { IEmployee } from "../../shared/models/Employee"; +import DepartmentDisplay from "../shared/DepartmentDisplay"; +import PayoutProfileForRole from "./PayoutProfileForRole"; +import DepartmentService from "../../shared/services/DepartmentService"; +import CommissionsForRole from "../commissions-page/CommissionsForRole"; + +export interface IPayoutProfileProps { + profile: IEmployee; +} + +const EligiblePayouts: React.FC = (props) => { + const profile = props.profile; + const activeDepartments = profile.departments.filter((userDept) => + DepartmentService.isActiveAssignment(userDept) + ); + + return activeDepartments.length > 0 ? ( + + {activeDepartments.map((department, idx) => { + return ( +
+
+ + : You get payouts & commissions for{" "} + {department.role?.name || "Missing"} +
+ {/**/} + + +
+
+ ); + })} +
+ ) : ( + No eligible payouts. + ); +}; + +export default EligiblePayouts; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/containers/payout-profile/PayoutProfileForRole.tsx b/LCU.WebApps.Operations.IncentiveManager/src/containers/payout-profile/PayoutProfileForRole.tsx new file mode 100644 index 0000000..9559558 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/containers/payout-profile/PayoutProfileForRole.tsx @@ -0,0 +1,40 @@ +import React from "react"; +import PayoutProfileCard from "../../components/payouts/PayoutProfileCard"; +import { IAppRole, IPayoutProfile } from "../../shared/models/Settings"; +import { SettingsContext } from "../../shared/providers/SettingsProvider"; +import { CardDeck } from "reactstrap"; +import "./PayoutProfilePage.scss"; + +export interface IPayoutProfileProps { + role: IAppRole | undefined; +} + +const PayoutProfileForRole: React.FC = (props) => { + const settings = React.useContext(SettingsContext); + const payoutProfiles = settings?.payoutProfiles; + let profiles: IPayoutProfile[] = []; + + if (payoutProfiles) { + profiles = payoutProfiles.filter((payoutProfile) => + payoutProfile.roles.find((x) => x.name == props.role?.name) + ); + } + + return profiles.length > 0 && props.role ? ( + + {profiles?.map((payoutProfile, idx) => { + return ( + + ); + })} + + ) : ( + No eligible goal based payouts. + ); +}; + +export default PayoutProfileForRole; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/containers/payout-profile/PayoutProfilePage.scss b/LCU.WebApps.Operations.IncentiveManager/src/containers/payout-profile/PayoutProfilePage.scss new file mode 100644 index 0000000..e78e19d --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/containers/payout-profile/PayoutProfilePage.scss @@ -0,0 +1,10 @@ +.FittedCard { + width: 30%; + height: auto; +} + +.FlexCenter { + display: flex; + align-items: baseline; + justify-content: center; +} diff --git a/LCU.WebApps.Operations.IncentiveManager/src/containers/payout-profile/PayoutProfilePage.tsx b/LCU.WebApps.Operations.IncentiveManager/src/containers/payout-profile/PayoutProfilePage.tsx new file mode 100644 index 0000000..4afb5fe --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/containers/payout-profile/PayoutProfilePage.tsx @@ -0,0 +1,37 @@ +import React from "react"; +import Translate from "./../../translate/Translate"; +import PayoutProfileCard from "../../components/payouts/PayoutProfileCard"; +import { SettingsContext } from "../../shared/providers/SettingsProvider"; +import { CardDeck } from "reactstrap"; +import "./PayoutProfilePage.scss"; + +const PayoutProfilePage: React.FC = () => { + const settings = React.useContext(SettingsContext); + + const payoutProfiles = settings?.payoutProfiles; + + return payoutProfiles ? ( +
+

{Translate.Get.containers.payoutProfileView.pageTitle}

+ + {payoutProfiles + ?.sort((a, b) => a.payouts.size) + .map((payoutProfile, idx) => { + return ( +
+ +
+ ); + })} +
+
+ ) : ( +

Could not load payout profiles.

+ ); +}; + +export default PayoutProfilePage; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/containers/payouts/ExportPayoutPage.test.tsx b/LCU.WebApps.Operations.IncentiveManager/src/containers/payouts/ExportPayoutPage.test.tsx new file mode 100644 index 0000000..a1bed0f --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/containers/payouts/ExportPayoutPage.test.tsx @@ -0,0 +1,17 @@ +import React from "react"; +import { shallow } from "enzyme"; +import ExportPayoutPage from "./ExportPayoutPage"; + +describe("A Component For exporting payouts", () => { + it("Displays the export options", () => { + const wrapper = shallow(); + const tableData = wrapper.find("[data-test='export-payout-page']"); + expect(tableData.length).toBe(1); + + const buttons = wrapper.find("button"); + expect(buttons.length).toBe(2); + + const selects = wrapper.find("select"); + expect(selects.length).toBe(2); + }); +}); diff --git a/LCU.WebApps.Operations.IncentiveManager/src/containers/payouts/ExportPayoutPage.tsx b/LCU.WebApps.Operations.IncentiveManager/src/containers/payouts/ExportPayoutPage.tsx new file mode 100644 index 0000000..d5100c2 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/containers/payouts/ExportPayoutPage.tsx @@ -0,0 +1,152 @@ +import React, { useState } from "react"; +import moment from "moment"; +import GoalService from "../../shared/services/GoalService"; +import Translate from "../../translate/Translate"; +import { Alert } from "reactstrap"; +import PayoutService from "../../shared/services/PayoutService"; +import { IAccountingSummary, IPayoutEntry } from "../../shared/models/Payout"; +import AsyncState from "../../shared/enumerations/AsyncState"; +import IAsyncStateContainer from "../../shared/types/IAsyncStateContainer"; +import AsyncGuard from "../shared/AsyncGuard"; +import PayoutEntryTable from "../../components/payouts/PayoutEntryTable"; +import ExportCsvService from "../../shared/services/ExportCsvService"; + +const ExportPayoutPage: React.FC = () => { + const [month, setMonth] = useState( + moment().add(1, "months").month() + ); + const [year, setYear] = useState(moment().year()); + const [payouts, setPayouts] = useState< + IAsyncStateContainer + >({ + state: AsyncState.Inactive + }); + const [accountingReport, setAccountingReport] = useState< + IAsyncStateContainer + >({ + state: AsyncState.Inactive + }); + + const years = [moment().year() - 2, moment().year() - 1, moment().year()]; + + const submitForm = async () => { + await fetchPayoutData(); + }; + + const submitAccountingForm = async () => { + setAccountingReport({ + state: AsyncState.Loading + }); + const date = moment({ year: year, month: month - 1, day: 1 }); + const response = await PayoutService.GetAccountingReport(date); + if (response.data) { + setAccountingReport(response); + ExportCsvService.exportAccountingReportCSV(response.data); + } else { + setAccountingReport({ + state: AsyncState.NotFound + }); + } + }; + + const fetchPayoutData = async () => { + setPayouts({ + state: AsyncState.Loading + }); + const response = await PayoutService.GetExportPayouts(year, month); + setPayouts(response); + setAccountingReport({ + state: AsyncState.Inactive + }); + }; + + const exportCsv = () => { + if (payouts.data && payouts.data.length > 0) { + ExportCsvService.exportDayforcePayoutCSV(payouts.data); + } + }; + + return ( +
+ + {Translate.Get.containers.exportPayoutPage.instructions} + + + + + + + <> + {payouts.data && payouts.data.length > 0 ? ( + + ) : ( + <> + )} + + +
+ + + Accounting Report Exported + + + + + +
+ ); +}; + +export default ExportPayoutPage; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/containers/payouts/PayoutPage.test.tsx b/LCU.WebApps.Operations.IncentiveManager/src/containers/payouts/PayoutPage.test.tsx new file mode 100644 index 0000000..a2889ff --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/containers/payouts/PayoutPage.test.tsx @@ -0,0 +1,24 @@ +import React from "react"; +import { shallow } from "enzyme"; +import AsyncState from "../../shared/enumerations/AsyncState"; +import PayoutPage from "./PayoutPage"; +import PayoutService from "../../shared/services/PayoutService"; +import IAsyncStateContainer from "../../shared/types/IAsyncStateContainer"; +import { IPayoutLineItem } from "../../shared/models/Payout"; + +describe("A Component For displaying an employee payout", () => { + const payoutServiceMock = jest.spyOn(PayoutService, "getEmployeePayouts"); + + beforeEach(() => { + payoutServiceMock.mockClear(); + }); + + it("Displays loading wait", () => { + payoutServiceMock.mockResolvedValue({ + state: AsyncState.Loading + } as IAsyncStateContainer); + + const wrapper = shallow(); + expect(wrapper.html()).toBe("Loading..."); + }); +}); diff --git a/LCU.WebApps.Operations.IncentiveManager/src/containers/payouts/PayoutPage.tsx b/LCU.WebApps.Operations.IncentiveManager/src/containers/payouts/PayoutPage.tsx new file mode 100644 index 0000000..2bce9e1 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/containers/payouts/PayoutPage.tsx @@ -0,0 +1,86 @@ +import React, { useContext, useEffect, useState } from "react"; +import PayoutView from "../../components/payouts/PayoutView"; +import AsyncState from "../../shared/enumerations/AsyncState"; +import moment, { Moment } from "moment"; +import { CustomInput, Form, Input } from "reactstrap"; +import PayoutService from "../../shared/services/PayoutService"; +import { IPayoutLineItem } from "../../shared/models/Payout"; +import IAsyncStateContainer from "../../shared/types/IAsyncStateContainer"; +import { SettingsContext } from "../../shared/providers/SettingsProvider"; + +export interface IEmployeeParam { + employeeId: string; +} + +const PayoutPage: React.FC = (props) => { + const employeeId = props.employeeId; + const [fromDate, setFromDate] = useState(moment().startOf("month")); + const [showDetails, setShowDetails] = useState(false); + const [lineItems, setLineItems] = useState< + IAsyncStateContainer + >({ + state: AsyncState.Loading + }); + const settings = React.useContext(SettingsContext); + + useEffect(() => { + setLineItems({ + state: AsyncState.Loading + }); + const fetchData = async () => { + return await PayoutService.getEmployeePayouts({ + employeeId: employeeId, + year: fromDate.year(), + month: fromDate.month() + 1 + }); + }; + fetchData().then((lineItems) => { + setLineItems(lineItems); + }); + }, [employeeId, fromDate]); + + return lineItems.state === AsyncState.Success && lineItems.data ? ( + <> +
+ Next Incentive Payout Date:{" "} + {moment(settings?.nextBonusPeriod).format("MM/DD/yyyy")} +
+
+
+ Payout details for + { + setFromDate( + moment(event.target.value).startOf("month") + ); + }} + /> + { + setShowDetails(event.target.checked); + }} + label="Show Details" + className="ml-4 mr-1" + /> + +
+ + + ) : ( + <>Loading... + ); +}; + +export default PayoutPage; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/containers/profile/ProfilePage.test.tsx b/LCU.WebApps.Operations.IncentiveManager/src/containers/profile/ProfilePage.test.tsx new file mode 100644 index 0000000..3024d8f --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/containers/profile/ProfilePage.test.tsx @@ -0,0 +1,39 @@ +import { shallow } from "enzyme"; +import React from "react"; +import ProfilePage from "./ProfilePage"; +import { IEmployee } from "../../shared/models/Employee"; +import ProfileView from "../../components/profile/ProfileView"; + +jest.mock("react-redux"); + +describe("A component for displaying appUser profile", () => { + it("Displays a valid profile", () => { + const profile: IEmployee = { + adEmail: "test@test.com", + personId: 11, + employeeId: "11771", + homeDepartment: "HOME", + baseRole: "QA", + fullName: "John Tester", + additionalPermissions: [], + role: { + name: "Role1", + description: "NONE", + permissions: [{ code: "VIEW_DASHBOARD", description: "" }], + hasCommissions: true + }, + departments: [], + assignments: [] + }; + + // eslint-disable-next-line @typescript-eslint/no-empty-function + const refreshProfile = () => {}; + + let wrapper = shallow( + + ); + + const found = wrapper.find(ProfileView); + expect(found.length).toBe(1); + }); +}); diff --git a/LCU.WebApps.Operations.IncentiveManager/src/containers/profile/ProfilePage.tsx b/LCU.WebApps.Operations.IncentiveManager/src/containers/profile/ProfilePage.tsx new file mode 100644 index 0000000..ada4c62 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/containers/profile/ProfilePage.tsx @@ -0,0 +1,208 @@ +import React, { useEffect, useState } from "react"; +import ProfileView from "../../components/profile/ProfileView"; +import { Alert, Col, ListGroup, ListGroupItem, Row } from "reactstrap"; +import EmployeePage from "../employee-page/EmployeePage"; +import PermissionsPage from "../../components/permission/PermissionsPage"; +import PayoutPage from "../payouts/PayoutPage"; +import { + IEmployee, + IEmployeeDetailsUpdate +} from "../../shared/models/Employee"; +import { SettingsContext } from "../../shared/providers/SettingsProvider"; +import PermissionGuard from "../shared/PermissionGuard"; +import EmployeeStatusGuard from "../shared/EmployeeStatusGuard"; +import moment from "moment"; +import EligiblePayouts from "../payout-profile/EligiblePayouts"; +import EmployeeDetailsPage from "../employee-page/EmployeeDetailsPage"; +import IAsyncStateContainer from "../../shared/types/IAsyncStateContainer"; + +interface IProfileParams { + profile: IEmployee; + employeeDetailsUpdateResult: string; + submitStatus: IAsyncStateContainer; + refreshProfile: () => void; + submitEmployeeDetailsUpdate: (update: IEmployeeDetailsUpdate) => void; +} + +const ProfilePage: React.FC = (props) => { + const [activeSheet, setActiveSheet] = useState("profile"); + const settings = React.useContext(SettingsContext); + const profile = props.profile; + const name = profile.fullName || "Unknown"; + + return ( + + + + { + setActiveSheet("profile"); + }} + action + > + Profile + + { + setActiveSheet("eligibility"); + }} + action + > + Payout Eligibility + + { + setActiveSheet("payouts"); + }} + action + > + Payouts + + + + { + setActiveSheet("departments"); + }} + action + > + Manage Departments + + + + + + { + setActiveSheet("permissions"); + }} + action + > + Manage Permissions + + + + + + { + setActiveSheet("employeeDetails"); + }} + action + > + Manage Employee Details + + + + +
+ + +

{name}

+ {profile.terminated ? ( + + Employee Not Active As Of{" "} + {moment(profile.terminated).format("MM/DD/YYYY")} + + ) : ( + "" + )} + {(() => { + switch (activeSheet) { + case "departments": + return ( + + + + ); + case "permissions": + return ( + + { + props.refreshProfile(); + }} + employeeId={profile.employeeId} + /> + + ); + case "employeeDetails": + return ( + + { + props.refreshProfile(); + }} + submitEmployeeDetailsUpdate={ + props.submitEmployeeDetailsUpdate + } + /> + + ); + case "payouts": + return ( + + ); + case "eligibility": + return ; + case "profile": + default: + return ; + } + })()} + +
+ ); +}; + +export default ProfilePage; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/containers/profile/ProfileResultsTable.tsx b/LCU.WebApps.Operations.IncentiveManager/src/containers/profile/ProfileResultsTable.tsx new file mode 100644 index 0000000..3861709 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/containers/profile/ProfileResultsTable.tsx @@ -0,0 +1,44 @@ +import React from "react"; +import { AutoTable } from "../../components/auto-table/AutoTable"; +import Translate from "./../../translate/Translate"; +import { useHistory } from "react-router-dom"; +import { IEmployeeSummary } from "../../shared/models/Employee"; + +export interface IProfileTableProps { + profiles: IEmployeeSummary[]; +} + +const ProfileResultsTable: React.FC = (props) => { + const history = useHistory(); + + const headers = [ + Translate.Get.containers.profileResultsTable.headers.name, + Translate.Get.containers.profileResultsTable.headers.id, + Translate.Get.containers.profileResultsTable.headers.email + ]; + + const cells = [ + (x: IEmployeeSummary) => x.fullName, + (x: IEmployeeSummary) => x.employeeId, + (x: IEmployeeSummary) => x.adEmail + ]; + + return ( +
+
+
{Translate.Get.containers.profileResultsTable.title}
+ { + history.push("/profile/" + selection.employeeId); + }} + /> +
+
+ ); +}; + +export default ProfileResultsTable; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/containers/profile/ProfileSearchPage.test.tsx b/LCU.WebApps.Operations.IncentiveManager/src/containers/profile/ProfileSearchPage.test.tsx new file mode 100644 index 0000000..fc93af2 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/containers/profile/ProfileSearchPage.test.tsx @@ -0,0 +1,57 @@ +import { mount } from "enzyme"; +import React from "react"; +import AsyncState from "../../shared/enumerations/AsyncState"; +import * as reactRedux from "react-redux"; +import { IProfileState } from "../../reducers/ProfileReducer"; +import ProfileSearchPage from "./ProfileSearchPage"; + +describe("A component for finding employee profiles", () => { + const useSelectorMock = jest.spyOn(reactRedux, "useSelector"); + const useDispatchMock = jest.spyOn(reactRedux, "useDispatch"); + + beforeEach(() => { + useSelectorMock.mockClear(); + useDispatchMock.mockClear(); + }); + + it("Displays a blank search form", () => { + const profile: IProfileState = { + profile: { + data: { + adEmail: "test@test.com", + personId: 11, + employeeId: "11771", + homeDepartment: "HOME", + baseRole: "QA", + fullName: "John Tester", + role: { + name: "Role1", + description: "NONE", + permissions: [ + { code: "VIEW_DASHBOARD", description: "" } + ], + hasCommissions: true + }, + additionalPermissions: [], + departments: [], + assignments: [] + }, + state: AsyncState.Success + }, + searchResults: { + state: AsyncState.Success, + data: [] + } + }; + + const dummyDispatch = jest.fn(); + useDispatchMock.mockReturnValue(dummyDispatch); + useSelectorMock.mockReturnValue(profile); + let wrapper = mount(); + + const found = wrapper.find("[data-test='profile-search-page']"); + expect(found.length).toBe(1); + + expect(dummyDispatch).not.toHaveBeenCalled(); + }); +}); diff --git a/LCU.WebApps.Operations.IncentiveManager/src/containers/profile/ProfileSearchPage.tsx b/LCU.WebApps.Operations.IncentiveManager/src/containers/profile/ProfileSearchPage.tsx new file mode 100644 index 0000000..aae1ab3 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/containers/profile/ProfileSearchPage.tsx @@ -0,0 +1,60 @@ +import React, { useCallback, useEffect, useState } from "react"; +import { useDispatch, useSelector } from "react-redux"; +import { profileSearch } from "../../actions/ProfileActions"; +import { IAppState } from "../../store/Store"; +import { IProfileState } from "../../reducers/ProfileReducer"; +import AsyncState from "../../shared/enumerations/AsyncState"; +import Translate from "../../translate/Translate"; +import { FormGroup, Input, Label } from "reactstrap"; +import { debounce } from "debounce"; +import ProfileResultsTable from "./ProfileResultsTable"; + +const profileTranslate = Translate.Get.components.profileSearch; + +const ProfileSearchPage: React.FC = () => { + const [searchQuery, setSearchQuery] = useState(""); + const dispatch = useDispatch(); + + useEffect(() => { + if (searchQuery && searchQuery.length > 1) { + search(searchQuery); + } + }, [searchQuery]); + + const search = useCallback( + debounce((query: string) => { + dispatch(profileSearch(query)); + }, 500), + [] + ); + + const profileState = useSelector( + (state) => state.profileState + ); + const profileResults = profileState.searchResults; + + return ( +
+ + + { + setSearchQuery(event.target.value); + }} + type="text" + placeholder={profileTranslate.placeholder} + /> + + {profileResults.state === AsyncState.Success ? ( + + ) : ( +

{searchQuery ? profileTranslate.loading : ""}

+ )} +
+ ); +}; + +export default ProfileSearchPage; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/containers/profile/ProfileSelector.tsx b/LCU.WebApps.Operations.IncentiveManager/src/containers/profile/ProfileSelector.tsx new file mode 100644 index 0000000..94ebec6 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/containers/profile/ProfileSelector.tsx @@ -0,0 +1,102 @@ +import React, { useEffect, useState } from "react"; +import AsyncState from "../../shared/enumerations/AsyncState"; +import Translate from "../../translate/Translate"; +import { useParams } from "react-router-dom"; +import ProfilePage from "./ProfilePage"; +import IAsyncStateContainer from "../../shared/types/IAsyncStateContainer"; +import { + IEmployee, + IEmployeeDetailsUpdate, + IEmployeeDetailsUpdateResult +} from "../../shared/models/Employee"; +import ProfileDao from "../../shared/dao/ProfileDao"; +import { Alert } from "reactstrap"; + +interface IRouteMatchParams { + employeeId?: string; +} + +const profileEditTranslate = Translate.Get.components.profileEdit; + +const ProfileSelector: React.FC = () => { + const [profile, setProfile] = useState>({ + state: AsyncState.Inactive + }); + const [updateFeedback, setUpdateFeedback] = useState(""); + const [submitStatus, setUpdatedSubmitStatus] = useState< + IAsyncStateContainer + >({ + state: AsyncState.Inactive + }); + + const { employeeId } = useParams(); + + const refreshUserProfile = () => { + ProfileDao.getProfile(employeeId).then( + (result) => { + setProfile(result); + }, + (errorResult) => { + setProfile(errorResult); + } + ); + }; + + const submitCurrentEmployeeDetailsUpdate = async ( + update: IEmployeeDetailsUpdate + ) => { + setUpdateFeedback(profileEditTranslate.loadingDetailUpdate); + submitStatus.state = AsyncState.Loading; + if (update) { + ProfileDao.submitEmployeeDetailsUpdate(update).then((result) => { + if (result.data) { + setSubmitStatus(result.data); + setUpdateFeedback(result.data.updateMessage); + refreshUserProfile(); + } + }); + } + }; + + const setSubmitStatus = async (update: IEmployeeDetailsUpdateResult) => { + if (update.updatedSuccessfully) { + submitStatus.state = AsyncState.Success; + } else { + submitStatus.state = AsyncState.Failure; + } + }; + + useEffect(() => { + refreshUserProfile(); + }, []); + + if (profile.state === AsyncState.Success && profile.data) + return ( + refreshUserProfile()} + submitEmployeeDetailsUpdate={( + employeeDetailsUpdate: IEmployeeDetailsUpdate + ) => submitCurrentEmployeeDetailsUpdate(employeeDetailsUpdate)} + /> + ); + + if (profile.state === AsyncState.Failure && profile.data) + return ( + refreshUserProfile()} + submitEmployeeDetailsUpdate={( + employeeDetailsUpdate: IEmployeeDetailsUpdate + ) => submitCurrentEmployeeDetailsUpdate(employeeDetailsUpdate)} + /> + ); + + return

{profileEditTranslate.loading}

; +}; + +export default ProfileSelector; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/containers/profile/readme.md b/LCU.WebApps.Operations.IncentiveManager/src/containers/profile/readme.md new file mode 100644 index 0000000..d6742ea --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/containers/profile/readme.md @@ -0,0 +1,17 @@ +## ProfileSearchPage + +This component is used to search for a profile. + +## ProfileResultsTable + +This component is used to display employee search results. + +## ProfileSelector + +Responsible for choosing between the current appUser's +profile and the optional route parameter if they were +using the search or a deep link, then show that profile. + +## ProfilePage + +The actual profile management page. diff --git a/LCU.WebApps.Operations.IncentiveManager/src/containers/report-page/ReportPage.tsx b/LCU.WebApps.Operations.IncentiveManager/src/containers/report-page/ReportPage.tsx new file mode 100644 index 0000000..ff015b4 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/containers/report-page/ReportPage.tsx @@ -0,0 +1,37 @@ +import React from "react"; +import { SettingsContext } from "../../shared/providers/SettingsProvider"; +import Translate from "../../translate/Translate"; +import { Button, Card, CardDeck } from "reactstrap"; + +const ReportPage: React.FC = () => { + const settings = React.useContext(SettingsContext); + const reports = settings?.authorizedReports; + + return reports ? ( +
+

{Translate.Get.containers.reportPage.title}

+ +
+ {reports.map((reportSettings) => { + return ( + + + + ); + })} +
+
+
+ ) : ( +

No Authorized Reports Found

+ ); +}; + +export default ReportPage; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/containers/role-management/RoleMangement.tsx b/LCU.WebApps.Operations.IncentiveManager/src/containers/role-management/RoleMangement.tsx new file mode 100644 index 0000000..4b8afe4 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/containers/role-management/RoleMangement.tsx @@ -0,0 +1,22 @@ +import React from "react"; +import { RouteComponentProps } from "react-router-dom"; +import Translate from "./../../translate/Translate"; +import RoleTable from "./RoleTable"; +import { SettingsContext } from "../../shared/providers/SettingsProvider"; + +type RoleManagementProps = RouteComponentProps; + +const RoleManagement: React.FC = () => { + const settings = React.useContext(SettingsContext); + return ( +
+

{Translate.Get.containers.roleManagement.pageTitle}

+ +
+ ); +}; + +export default RoleManagement; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/containers/role-management/RoleTable.tsx b/LCU.WebApps.Operations.IncentiveManager/src/containers/role-management/RoleTable.tsx new file mode 100644 index 0000000..62f7322 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/containers/role-management/RoleTable.tsx @@ -0,0 +1,66 @@ +import React from "react"; +import { AutoTable } from "../../components/auto-table/AutoTable"; +import Translate from "./../../translate/Translate"; +import { IAppRole, IPermission } from "../../shared/models/Settings"; +import PermissionDisplay from "../../components/permission/PermissionDisplay"; + +export interface RoleTableProps { + appRoles: IAppRole[]; + permissionSet: IPermission[]; +} + +const RoleTable: React.FC = (props) => { + const headers = [ + Translate.Get.containers.roleManagement.roleTable.headers.name, + Translate.Get.containers.roleManagement.roleTable.headers.description, + Translate.Get.containers.roleManagement.roleTable.headers.permissions + ]; + + const cells = [ + (x: IAppRole) => {x.name}, + (x: IAppRole) => x.description, + (x: IAppRole) => ( + + ) + ]; + /** + * Sorts by Params Count, then Sorts by Alpha + * Param Count Desc (More Params = Higher on List) + * Alpha Desc (A on top Z on bottom) + */ + const CompareByPermsThenAlpha = (a: IAppRole, b: IAppRole) => { + if ( + a.permissions.length - b.permissions.length > 0 || + a.name < b.name + ) { + return -1; + } else if ( + a.permissions.length - b.permissions.length < 0 || + a.name > b.name + ) { + return 1; + } else return 0; + }; + + return ( +
+
+
+ {Translate.Get.containers.roleManagement.roleTable.title} +
+
+ + CompareByPermsThenAlpha(a, b) + )} + /> +
+
+
+ ); +}; + +export default RoleTable; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/containers/role-mapping-page/RoleMappingPage.tsx b/LCU.WebApps.Operations.IncentiveManager/src/containers/role-mapping-page/RoleMappingPage.tsx new file mode 100644 index 0000000..c451254 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/containers/role-mapping-page/RoleMappingPage.tsx @@ -0,0 +1,45 @@ +import React, { useEffect } from "react"; +import { useDispatch, useSelector } from "react-redux"; +import { + getAllRoleMappings, + submitRoleMappingUpdate +} from "../../actions/RoleMappingActions"; +import { IRoleMappingState } from "../../reducers/RoleMappingReducer"; +import { IAppState } from "../../store/Store"; +import RoleMappingTable from "../../components/role-mapping/RoleMappingTable"; +import { IRoleMappingChange } from "../../shared/models/RoleMapping"; + +const RoleMappingPage: React.FC = () => { + const dispatch = useDispatch(); + const roleMappingState = useSelector( + (state) => state.roleMappingState + ); + + const roleMappings = roleMappingState.roleMappings.data ?? []; + + const submitCurrentRoleMappingUpdate = async ( + update: IRoleMappingChange + ) => { + if (roleMappingState.roleMappings.data) { + await dispatch(submitRoleMappingUpdate(update)); + await dispatch(getAllRoleMappings()); + } + }; + + useEffect(() => { + dispatch(getAllRoleMappings()); + }, []); + + return ( +
+ submitCurrentRoleMappingUpdate(roleMappingUpdate)} + /> +
+ ); +}; + +export default RoleMappingPage; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/containers/shared/AsyncGuard.test.tsx b/LCU.WebApps.Operations.IncentiveManager/src/containers/shared/AsyncGuard.test.tsx new file mode 100644 index 0000000..2fcbdbe --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/containers/shared/AsyncGuard.test.tsx @@ -0,0 +1,48 @@ +import { mount } from "enzyme"; +import React from "react"; +import AsyncState from "../../shared/enumerations/AsyncState"; +import AsyncGuard from "./AsyncGuard"; + +describe("A component for displaying async containers", () => { + it("Displays an async status: Loading", () => { + const container = { + data: [], + state: AsyncState.Loading + }; + let wrapper = mount( + + <>!!ERROR!! + + ); + + expect(wrapper.html()).toBe("Loading..."); + }); + + it("Displays an async custom status: Error", () => { + const container = { + data: [], + state: AsyncState.Failure + }; + let wrapper = mount( + OH NO}> + <>!!ERROR!! + + ); + + expect(wrapper.html()).toBe("

OH NO

"); + }); + + it("Displays an async child on Success", () => { + const container = { + data: [], + state: AsyncState.Success + }; + let wrapper = mount( + + <>YES + + ); + + expect(wrapper.html()).toBe("YES"); + }); +}); diff --git a/LCU.WebApps.Operations.IncentiveManager/src/containers/shared/AsyncGuard.tsx b/LCU.WebApps.Operations.IncentiveManager/src/containers/shared/AsyncGuard.tsx new file mode 100644 index 0000000..47b657d --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/containers/shared/AsyncGuard.tsx @@ -0,0 +1,35 @@ +import React from "react"; +import AsyncState from "../../shared/enumerations/AsyncState"; +import IAsyncStateContainer from "../../shared/types/IAsyncStateContainer"; + +export interface IAsyncProps { + container: IAsyncStateContainer; + children: JSX.Element; + error?: JSX.Element; + loading?: JSX.Element; + notFound?: JSX.Element; + inactive?: JSX.Element; +} + +const AsyncGuard: React.FC = (props) => { + switch (props.container.state) { + case AsyncState.Inactive: + default: + return props.inactive ? props.inactive : <>; + case AsyncState.Loading: + return props.loading ? props.loading : <>Loading...; + case AsyncState.Invalid: + case AsyncState.Failure: + return props.error ? props.error : <>Data failed to load; + case AsyncState.NotFound: + return props.notFound ? props.notFound : <>Data was not found; + case AsyncState.Success: + return props.container.data ? ( + props.children + ) : ( + <>No Data Returned + ); + } +}; + +export default AsyncGuard; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/containers/shared/BusinessLineDisplay.test.tsx b/LCU.WebApps.Operations.IncentiveManager/src/containers/shared/BusinessLineDisplay.test.tsx new file mode 100644 index 0000000..4127bb8 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/containers/shared/BusinessLineDisplay.test.tsx @@ -0,0 +1,25 @@ +import { mount } from "enzyme"; +import React from "react"; +import BusinessLineDisplay from "./BusinessLineDisplay"; +import { getMockAppSettings } from "../../shared/testing/mocks/MockAppSettings"; + +describe("A component for displaying business line", () => { + const useSelectorMock = jest.spyOn(React, "useContext"); + + beforeEach(() => { + useSelectorMock.mockClear(); + }); + + it("Displays a valid business line name", () => { + useSelectorMock.mockReturnValue(getMockAppSettings()); + let wrapper = mount(); + + expect(wrapper.html()).toBe("Operations"); + }); + + it("Displays an invalid business line name for a code", () => { + useSelectorMock.mockReturnValue(getMockAppSettings()); + let wrapper = mount(); + expect(wrapper.html()).toBe("Missing"); + }); +}); diff --git a/LCU.WebApps.Operations.IncentiveManager/src/containers/shared/BusinessLineDisplay.tsx b/LCU.WebApps.Operations.IncentiveManager/src/containers/shared/BusinessLineDisplay.tsx new file mode 100644 index 0000000..52a5e01 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/containers/shared/BusinessLineDisplay.tsx @@ -0,0 +1,21 @@ +import React from "react"; +import { SettingsContext } from "../../shared/providers/SettingsProvider"; + +interface IBusinessLineDisplayProps { + businessLineCode: string; +} + +const BusinessLineDisplay: React.FC = (props) => { + const settings = React.useContext(SettingsContext); + const businessName = settings?.businessLines.find( + (x) => x.code === props.businessLineCode + ); + + return props.businessLineCode && businessName ? ( + <>{businessName.name} + ) : ( + <>Missing + ); +}; + +export default BusinessLineDisplay; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/containers/shared/DepartmentDisplay.test.tsx b/LCU.WebApps.Operations.IncentiveManager/src/containers/shared/DepartmentDisplay.test.tsx new file mode 100644 index 0000000..a0f8ef6 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/containers/shared/DepartmentDisplay.test.tsx @@ -0,0 +1,58 @@ +import { mount } from "enzyme"; +import React from "react"; +import DepartmentDisplay from "./DepartmentDisplay"; +import AsyncState from "../../shared/enumerations/AsyncState"; +import { IDepartmentState } from "../../reducers/DepartmentReducer"; +import { getMockAppSettings } from "../../shared/testing/mocks/MockAppSettings"; +import { getMockDepartments } from "../../shared/testing/mocks/MockDepartments"; + +describe("A component for displaying departments", () => { + const useContextMock = jest.spyOn(React, "useContext"); + + beforeEach(() => { + useContextMock.mockClear(); + }); + + it("Displays a valid department", () => { + const departments: IDepartmentState = { + departments: { + data: [ + { + id: 4, + departmentId: 5, + departmentName: "bad test", + state: "Active", + businessLineCode: "XY" + }, + { + id: 5, + departmentId: 6, + departmentName: "good test", + state: "Active", + businessLineCode: "XY" + } + ], + state: AsyncState.Success + }, + submitState: { + state: AsyncState.Success + } + }; + + const settings = getMockAppSettings(); + settings.departments = getMockDepartments(); + useContextMock.mockReturnValue(settings); + const wrapper = mount(); + const found = wrapper.find('[data-test="department-display-wrapper"]'); + expect(found.length).toBe(1); + expect(found.children().first().props().title).toBe("55"); + expect(found.children().first().text()).toBe("WAUWATOSA"); + }); + + it("Displays a invalid department", () => { + const wrapper = mount(); + const found = wrapper.find('[data-test="department-display-wrapper"]'); + expect(found.length).toBe(1); + expect(found.children().first().text()).toBe("Missing"); + }); +}); diff --git a/LCU.WebApps.Operations.IncentiveManager/src/containers/shared/DepartmentDisplay.tsx b/LCU.WebApps.Operations.IncentiveManager/src/containers/shared/DepartmentDisplay.tsx new file mode 100644 index 0000000..802de2a --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/containers/shared/DepartmentDisplay.tsx @@ -0,0 +1,47 @@ +import React from "react"; +import { SettingsContext } from "../../shared/providers/SettingsProvider"; +import Configuration from "../../shared/configuration/Configuration"; + +export enum DepartmentDisplayTargets { + Detail = "detail", + Dashboard = "dashboard" +} +interface IDepartmentDisplayProps { + departmentId: number; + target?: DepartmentDisplayTargets; + nameOverride?: string; +} + +const DepartmentDisplay: React.FC = (props) => { + const settings = React.useContext(SettingsContext); + const department = settings?.departments.find( + (x) => x.id === props.departmentId + ); + + let href = Configuration.PrefixAppUrlWithBasename( + Configuration.Get.appUrls.department + ).replace(":id", props.departmentId.toString()); + + if (props.target && props.target === DepartmentDisplayTargets.Dashboard) { + href = + Configuration.PrefixAppUrlWithBasename( + Configuration.Get.appUrls.dashboard + ) + + "?dashboard=department&departmentId=" + + props.departmentId; + } + + return ( + + {props.departmentId && department ? ( + + {props.nameOverride ?? department.departmentName} + + ) : ( + <>Missing + )} + + ); +}; + +export default DepartmentDisplay; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/containers/shared/EmployeeStatusGuard.tsx b/LCU.WebApps.Operations.IncentiveManager/src/containers/shared/EmployeeStatusGuard.tsx new file mode 100644 index 0000000..831ad9e --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/containers/shared/EmployeeStatusGuard.tsx @@ -0,0 +1,17 @@ +import React from "react"; +import { IEmployee } from "../../shared/models/Employee"; + +export interface IEmployeeStatusGuardProps { + employee: IEmployee; + children: JSX.Element; +} + +const EmployeeStatusGuard: React.FC = (props) => { + const shouldRenderChildren = () => { + return props.employee && !props.employee.terminated; + }; + + return shouldRenderChildren() ? props.children : <>; +}; + +export default EmployeeStatusGuard; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/containers/shared/PermissionGuard.test.tsx b/LCU.WebApps.Operations.IncentiveManager/src/containers/shared/PermissionGuard.test.tsx new file mode 100644 index 0000000..211c462 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/containers/shared/PermissionGuard.test.tsx @@ -0,0 +1,211 @@ +import { mount } from "enzyme"; +import React from "react"; +import AsyncState from "../../shared/enumerations/AsyncState"; +import * as reactRedux from "react-redux"; +import { IProfileState } from "../../reducers/ProfileReducer"; +import PermissionGuard from "./PermissionGuard"; +import moment from "moment"; +import { UserType } from "../../shared/models/Auditable"; +import PermissionCode from "../../shared/enumerations/PermissionCode"; + +describe("A component for requiring permissions", () => { + const useSelectorMock = jest.spyOn(reactRedux, "useSelector"); + const useDispatchMock = jest.spyOn(reactRedux, "useDispatch"); + + beforeEach(() => { + useSelectorMock.mockClear(); + useDispatchMock.mockClear(); + }); + + it("Displays children when role permission present", () => { + jest.mock("react-router-dom", () => ({ + ...jest.requireActual("react-router-dom"), + useParams: () => ({ + employeeId: "11771" + }) + })); + + const profile: IProfileState = { + profile: { + data: { + adEmail: "test@test.com", + personId: 11, + employeeId: "11771", + homeDepartment: "HOME", + baseRole: "QA", + fullName: "John Tester", + role: { + name: "Admin", + description: "admin", + permissions: [ + { + code: "VIEW_DASHBOARD", + description: "View Dashboard" + } + ], + hasCommissions: false + }, + additionalPermissions: [], + departments: [ + { + id: 1, + role: { + name: "Admin", + description: "admin", + permissions: [ + { + code: "VIEW_DASHBOARD", + description: "View Dashboard" + } + ], + hasCommissions: false + }, + departmentId: 114, + employeeId: "11771", + isHome: true, + note: "asdf", + slidingPayout: 100, + startDate: moment(), + lastUpdated: moment(), + createdByType: UserType.Associate, + createdBy: "asdf", + endDate: undefined, + lastUpdatedByType: UserType.Associate, + created: moment(), + lastUpdatedBy: "asdf" + } + ], + assignments: [] + }, + state: AsyncState.Success + }, + searchResults: { + state: AsyncState.Success, + data: [] + } + }; + + const dummyDispatch = jest.fn(); + useDispatchMock.mockReturnValue(dummyDispatch); + useSelectorMock.mockReturnValue(profile); + let wrapper = mount( + +

Authorized

+
+ ); + + expect(wrapper.html()).toBe("

Authorized

"); + }); + + it("Displays children when additional permission present", () => { + jest.mock("react-router-dom", () => ({ + ...jest.requireActual("react-router-dom"), + useParams: () => ({ + employeeId: "11771" + }) + })); + + const profile: IProfileState = { + profile: { + data: { + adEmail: "test@test.com", + personId: 11, + employeeId: "11771", + homeDepartment: "HOME", + baseRole: "QA", + fullName: "John Tester", + role: { + name: "Admin", + description: "admin", + permissions: [], + hasCommissions: false + }, + additionalPermissions: [ + { + id: 1, + created: moment().utc(), + createdByType: UserType.System, + createdBy: "Tester1", + permission: { + code: "VIEW_DASHBOARD", + description: "View Dashboard" + } + } + ], + departments: [], + assignments: [] + }, + state: AsyncState.Success + }, + searchResults: { + state: AsyncState.Success, + data: [] + } + }; + + const dummyDispatch = jest.fn(); + useDispatchMock.mockReturnValue(dummyDispatch); + useSelectorMock.mockReturnValue(profile); + const wrapper = mount( + +

Authorized

+
+ ); + + expect(wrapper.html()).toBe("

Authorized

"); + }); + + it("Displays nothing when permission not present", () => { + jest.mock("react-router-dom", () => ({ + ...jest.requireActual("react-router-dom"), + useParams: () => ({ + employeeId: "11771" + }) + })); + + const profile: IProfileState = { + profile: { + data: { + adEmail: "test@test.com", + personId: 11, + employeeId: "11771", + homeDepartment: "HOME", + baseRole: "QA", + fullName: "John Tester", + role: { + name: "Admin", + description: "admin", + permissions: [ + { + code: "VIEW_DASHBOARD", + description: "View Dashboard" + } + ], + hasCommissions: false + }, + additionalPermissions: [], + departments: [], + assignments: [] + }, + state: AsyncState.Success + }, + searchResults: { + state: AsyncState.Success, + data: [] + } + }; + + const dummyDispatch = jest.fn(); + useDispatchMock.mockReturnValue(dummyDispatch); + useSelectorMock.mockReturnValue(profile); + let wrapper = mount( + +

Authorized

+
+ ); + + expect(wrapper.html()).toBe(null); + }); +}); diff --git a/LCU.WebApps.Operations.IncentiveManager/src/containers/shared/PermissionGuard.tsx b/LCU.WebApps.Operations.IncentiveManager/src/containers/shared/PermissionGuard.tsx new file mode 100644 index 0000000..6cf295c --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/containers/shared/PermissionGuard.tsx @@ -0,0 +1,47 @@ +import React from "react"; +import { useSelector } from "react-redux"; +import { IAppState } from "../../store/Store"; +import { IProfileState } from "../../reducers/ProfileReducer"; +import AsyncState from "../../shared/enumerations/AsyncState"; +import { Alert } from "reactstrap"; +import ProfileService from "../../shared/services/ProfileService"; + +export interface IPermissionProps { + requiredPermissions: string[]; + showWarning?: boolean; + children: JSX.Element; +} + +const PermissionGuard: React.FC = (props) => { + const profileState = useSelector( + (state) => state.profileState + ); + const profile = profileState.profile; + + const shouldRenderChildren = () => { + const requiredPermissions = props.requiredPermissions; + if (!requiredPermissions || requiredPermissions.length < 1) { + return true; + } + + if (profile.state !== AsyncState.Success || !profile.data) { + return false; + } + const profileData = profile.data; + return ProfileService.hasAnyPermission( + profileData, + requiredPermissions + ); + }; + + if (shouldRenderChildren()) { + return props.children; + } + return props.showWarning ? ( + You do not have access. + ) : ( + <> + ); +}; + +export default PermissionGuard; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/containers/shared/ProductDisplay.test.tsx b/LCU.WebApps.Operations.IncentiveManager/src/containers/shared/ProductDisplay.test.tsx new file mode 100644 index 0000000..da763d1 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/containers/shared/ProductDisplay.test.tsx @@ -0,0 +1,25 @@ +import { mount } from "enzyme"; +import React from "react"; +import ProductDisplay from "./ProductDisplay"; +import { getMockAppSettings } from "../../shared/testing/mocks/MockAppSettings"; + +describe("A component for displaying products", () => { + const useSelectorMock = jest.spyOn(React, "useContext"); + + beforeEach(() => { + useSelectorMock.mockClear(); + }); + + it("Displays a valid product", () => { + useSelectorMock.mockReturnValue(getMockAppSettings()); + let wrapper = mount(); + + expect(wrapper.html()).toBe("Check Card"); + }); + + it("Displays an invalid product", () => { + useSelectorMock.mockReturnValue(getMockAppSettings()); + let wrapper = mount(); + expect(wrapper.html()).toBe("Missing"); + }); +}); diff --git a/LCU.WebApps.Operations.IncentiveManager/src/containers/shared/ProductDisplay.tsx b/LCU.WebApps.Operations.IncentiveManager/src/containers/shared/ProductDisplay.tsx new file mode 100644 index 0000000..57f8775 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/containers/shared/ProductDisplay.tsx @@ -0,0 +1,17 @@ +import React from "react"; +import { SettingsContext } from "../../shared/providers/SettingsProvider"; + +interface IDepartmentDisplayProps { + productCode: string; +} + +const ProductDisplay: React.FC = (props) => { + const settings = React.useContext(SettingsContext); + const product = settings?.productDetails.find( + (x) => x.product === props.productCode + ); + + return props.productCode && product ? <>{product.title} : <>Missing; +}; + +export default ProductDisplay; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/containers/shared/UserDisplay.css b/LCU.WebApps.Operations.IncentiveManager/src/containers/shared/UserDisplay.css new file mode 100644 index 0000000..af3c694 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/containers/shared/UserDisplay.css @@ -0,0 +1,7 @@ +.term-user { + text-decoration: line-through; + color: darkred; +} + +.active-user { +} diff --git a/LCU.WebApps.Operations.IncentiveManager/src/containers/shared/UserDisplay.tsx b/LCU.WebApps.Operations.IncentiveManager/src/containers/shared/UserDisplay.tsx new file mode 100644 index 0000000..f4a4d7d --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/containers/shared/UserDisplay.tsx @@ -0,0 +1,70 @@ +import React from "react"; +import { SettingsContext } from "../../shared/providers/SettingsProvider"; +import Configuration from "../../shared/configuration/Configuration"; +import { IUserSummary } from "../../shared/models/Settings"; +import "./UserDisplay.css"; +import moment from "moment"; + +export enum UserDisplayTargets { + Profile = "profile", + Dashboard = "dashboard" +} + +interface IUserDisplayProps { + employeeId: string; + target?: UserDisplayTargets; +} + +const UserDisplay: React.FC = (props) => { + const settings = React.useContext(SettingsContext); + const user = settings?.employees.find( + (x) => x.employeeId === props.employeeId + ); + + function determineDecoration(user: IUserSummary) { + return user.terminated ? "term-appUser" : "active-appUser"; + } + + let terminated = ""; + if (user && user.terminated) { + terminated = moment(user.terminated).format(" (MM/DD/YYYY)"); + } + + const toTitleCase = (str: string) => { + return str.replace(/\w\S*/g, function (txt) { + return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase(); + }); + }; + + let href = Configuration.PrefixAppUrlWithBasename( + Configuration.Get.appUrls.profile + ).replace(":employeeId?", props.employeeId); + + if (props.target && props.target === UserDisplayTargets.Dashboard) { + href = + Configuration.PrefixAppUrlWithBasename( + Configuration.Get.appUrls.dashboard + ) + + "?dashboard=employee&employeeId=" + + props.employeeId; + } + + return ( + + {props.employeeId && user ? ( + + {toTitleCase(user.fullName)} + {terminated} + + ) : ( + <>Missing + )} + + ); +}; + +export default UserDisplay; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/environments/environment.dev.ts b/LCU.WebApps.Operations.IncentiveManager/src/environments/environment.dev.ts new file mode 100644 index 0000000..164aea3 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/environments/environment.dev.ts @@ -0,0 +1,98 @@ +import { BUILD_STAMP, VERSION } from "./version"; + +const devEnvironment = { + name: "qa", + requiredRole: "QaAssociate", + statesToSave: ["savedState"], + version: VERSION, + buildTs: BUILD_STAMP, + production: false, + useLocalSettings: true, + enableTimeout: false, + collectionsAccountNoPrefix: "", + appUrlBaseName: "incentive-manager", + defaultPaginationLimit: 10, + appUrls: { + root: "/", + activity: "/activity", + activityForm: "/activity/new", + commissions: "/commissions", + dashboard: "/dashboard", + deadActivity: "/deadActivity", + department: "/department/:id", + departmentGoals: "/department-goals", + employeeMappings: "/employee-mappings", + eoeList: "/eoe-list", + exportPayout: "/export-payout", + payoutProfiles: "/payoutProfiles", + personnelGoals: "/personnel-goals", + profile: "/profile/:employeeId?", + profileSearch: "/profile-search", + organization: "/organization", + reports: "/reports", + roleMappings: "/role-mappings", + roles: "/roles", + testRoute: "/somethingstrange" + }, + baseUrls: { + incentiveApi: + "https://qa-incentive-manager-api.apps.azure.lcu-internal.com/api" + }, + urls: { + incentiveApi: { + deleteDeadActivity: "/deadactivity", + disperseGoalsByCustomWeights: + "/goal/personnel/customweighteddispersal", + disperseGoalsByExtrapolation: + "/goal/personnel/{departmentId}/disperse", + eoeList: "/employee/terminated", + exportAccountReport: "/payout/getAccounting", + exportPayouts: "/payout/export", + getActivity: "/activity", + getActivityDetailUpdates: "/activity/{activityId}/DetailChanges", + getActivityStateChanges: "/activity/{activityId}/StateChanges", + getAllActivities: "/activity", + getAllActivitiesPaged: "/activity/paged", + getAllCommissions: "/commission", + getAllDeadActivitiesPaged: "/deadactivity/paged", + getAllDepartments: "/department", + getAllGoals: "/goal", + getAllRoleMappings: "/Settings/GetAllRoleMappings", + getAllSettings: "/settings", + getAllUserAssignments: "/UserAssignment", + getBusinessLineProgress: "/activity/businessLine", + getDeadActivity: "/deadactivity", + getDepartmentDetails: "/department/{departmentId}/details", + getEmployeeByEmployeeId: "/employee/resolve?employeeId=", + getEmployeePayouts: "/payout/{employeeId}", + getMyActivities: "/activity/me", + getMyDepartmentActivities: "/activity/me/departments", + getMyDepartmentsGoals: "/goal/me/departments", + getMyGoals: "/goal/me", + getMyPayouts: "/payout/me", + getMyRegionProgress: "/activity/region", + getRequestedTransferAction: + "/activity/{activityId}/RequestedTransferAction", + getUsersForMapping: "/Employee/GetUsersForMapping", + postActivityStateChange: "/activity/ManualStateChange", + postActivityTransferRequest: "/activity/RequestTransfer", + postActivityTransferToOwnDepartmentRequest: + "/activity/RequestTransferToOwnDepartment", + postApproveTransfer: "/activity/ApproveTransfer", + postNewDepartmentAssignment: "/employee/{employeeId}/assigndept", + profile: "/employee/me", + profileUpdate: "/employee/UpdateEmployeeDetails", + putPermissionsUpdate: "/employee/{employeeId}/UpdatePerms", + runEmployeeUpdateSync: "/Employee/sync", + setCommissions: "/commission", + setRegionalManager: "/UserAssignment/SetRegionalManager", + submitActivityForm: "/activity", + updateDepartmentAssignment: + "/employee/{employeeId}/updatedept/{assignmentId}", + updateEmployeeMapping: "/Employee/UpdateEmployeeMapping", + updateRoleMapping: "/Settings/UpdateRoleMapping" + } + } +}; + +export default devEnvironment; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/environments/environment.local.ts b/LCU.WebApps.Operations.IncentiveManager/src/environments/environment.local.ts new file mode 100644 index 0000000..7369c76 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/environments/environment.local.ts @@ -0,0 +1,15 @@ +import devEnvironment from "./environment.dev"; + +const localEnvironment = { + ...devEnvironment, + name: "local", + requiredRole: "QaAssociate", + production: false, + useLocalSettings: true, + enableTimeout: false, + baseUrls: { + incentiveApi: "https://localhost:20001/api" + } +}; + +export default localEnvironment; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/environments/environment.prod.ts b/LCU.WebApps.Operations.IncentiveManager/src/environments/environment.prod.ts new file mode 100644 index 0000000..faaa11f --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/environments/environment.prod.ts @@ -0,0 +1,13 @@ +import uatEnvironment from "./environment.uat"; + +const prodEnvironment = { + ...uatEnvironment, + name: "prod", + requiredRole: "Associate", + baseUrls: { + incentiveApi: + "https://prd-incentive-manager-api.apps.azure.lcu-internal.com/api" + } +}; + +export default prodEnvironment; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/environments/environment.uat.ts b/LCU.WebApps.Operations.IncentiveManager/src/environments/environment.uat.ts new file mode 100644 index 0000000..00f04eb --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/environments/environment.uat.ts @@ -0,0 +1,16 @@ +import devEnvironment from "./environment.dev"; + +const uatEnvironment = { + ...devEnvironment, + name: "uat", + requiredRole: "QaAssociate", + production: true, + useLocalSettings: false, + enableTimeout: true, + baseUrls: { + incentiveApi: + "https://uat-incentive-manager-api.apps.azure.lcu-internal.com/api" + } +}; + +export default uatEnvironment; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/index.scss b/LCU.WebApps.Operations.IncentiveManager/src/index.scss new file mode 100644 index 0000000..fbcdea1 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/index.scss @@ -0,0 +1,120 @@ +body { + margin: 0; + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", + "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", + "Helvetica Neue", sans-serif; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + overflow-x: hidden; +} + +code { + font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New", + monospace; +} + +.table-hover { + cursor: pointer; +} + +.tablecenterCSS td { + text-align: center; +} + +.tablecenterCSS td.tdLabel { + text-align: left; +} + +.tablecenterheadCSS th { + text-align: center; +} + +.logo-main { + width: 140px; +} + +.title-main { + font-size: 1.33em; + margin-left: 1em; +} + +.nav-margin { + margin: 0 0.5em 0 0; +} + +.watermark-outer { + position: relative; + width: 0; + height: 0; +} + +.watermark-inner { + position: absolute; + left: 0; + bottom: -25px; + z-index: -1; +} + +$width-to-mobile: 767px; +.watermark-secondary { + max-width: 200px; + position: absolute; + right: 10%; + opacity: 1; + z-index: -1; +} + +@media screen and (max-width: $width-to-mobile) { + .watermark-primary { + display: none; + } + .watermark-secondary { + max-width: 75px; + position: absolute; + top: 4%; + left: 0; + right: 0; + margin: auto; + } +} + +.toast-container-wrapper { + .Toastify__toast-container--top-right { + top: 4em; + } + + .Toastify__toast { + border-radius: 0.25em; + } +} + +.react-datepicker-wrapper { + display: block !important; +} + +.start-page-parent { + overflow-x: auto; +} + +.main-panel { + padding: 75px 15px; + min-height: calc(101vh - 80px); +} + +.vertical-center-container { + position: relative; +} + +.vertical-center { + margin: 0; + position: absolute; + top: 50%; + -ms-transform: translateY(-50%); + transform: translateY(-50%); +} + +.multi-select { + display: block; + max-width: 150px; + text-overflow: ellipsis; +} diff --git a/LCU.WebApps.Operations.IncentiveManager/src/index.tsx b/LCU.WebApps.Operations.IncentiveManager/src/index.tsx new file mode 100644 index 0000000..9297ae6 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/index.tsx @@ -0,0 +1,68 @@ +//import "bootstrap/dist/css/bootstrap.css"; +import "./theme/theme.scss"; +import "font-awesome/css/font-awesome.css"; +import "react-app-polyfill/ie11"; +import "react-app-polyfill/stable"; +import "react-datepicker/dist/react-datepicker.css"; +import "react-toastify/dist/ReactToastify.css"; +import "./index.scss"; + +import * as React from "react"; + +import configureStore from "./store/Store"; +import { PublicClientApplication } from "@azure/msal-browser"; +import ReactDOM from "react-dom"; +import { Provider } from "react-redux"; +import App from "./App"; +import { ToastContainer } from "react-toastify"; +import { MsalProvider } from "@azure/msal-react"; +import LocalStorageService from "./shared/services/LocalStorageService"; +import Configuration from "./shared/configuration/Configuration"; +import { startInterceptor } from "./shared/security/interceptor"; +import { loginConfig, securityConfig, tokenConfig } from "./authConfig"; + +/** + * The docs tend to make the msalInstance a global and/or export it! + * We wrap it up here, reference it from the MsalProvider and use hooks. + */ +(function startup() { + const msalInstance = new PublicClientApplication(securityConfig); + // handle auth redirect/do all initial setup for msal + msalInstance + .handleRedirectPromise() + .then(() => { + const accounts = msalInstance.getAllAccounts(); + if (accounts.length > 0) { + msalInstance.setActiveAccount(accounts[0]); + } + const account = msalInstance.getActiveAccount(); + if (!account) { + msalInstance.loginRedirect(loginConfig); + } else { + main(msalInstance); + } + }) + .catch((err) => { + // eslint-disable-next-line no-console + console.error(err); + }); +})(); + +function main(msal: PublicClientApplication) { + const store = configureStore( + LocalStorageService.loadAppSavedState(Configuration.Get.statesToSave) + ); + startInterceptor(msal, tokenConfig).then(() => { + ReactDOM.render( + + + +
+ +
+
+
, + document.getElementById("root") + ); + }); +} diff --git a/LCU.WebApps.Operations.IncentiveManager/src/logo.svg b/LCU.WebApps.Operations.IncentiveManager/src/logo.svg new file mode 100644 index 0000000..9dfc1c0 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/logo.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/LCU.WebApps.Operations.IncentiveManager/src/react-app-env.d.ts b/LCU.WebApps.Operations.IncentiveManager/src/react-app-env.d.ts new file mode 100644 index 0000000..6431bc5 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/react-app-env.d.ts @@ -0,0 +1 @@ +/// diff --git a/LCU.WebApps.Operations.IncentiveManager/src/reducers/ActivityReducer.ts b/LCU.WebApps.Operations.IncentiveManager/src/reducers/ActivityReducer.ts new file mode 100644 index 0000000..63d1104 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/reducers/ActivityReducer.ts @@ -0,0 +1,70 @@ +import { Reducer } from "react"; +import { + ActivityActions, + ActivityActionTypes +} from "../actions/ActivityActions"; +import AsyncState from "../shared/enumerations/AsyncState"; +import { + IAuditableActivity, + IDepartmentProgress, + IEmployeeProgress +} from "../shared/models/Activity"; +import IAsyncStateContainer from "../shared/types/IAsyncStateContainer"; +import IPagedResponse from "./../shared/types/IPagedResponse"; +export interface IActivityState { + readonly filteredActivitiesPageCache: IPagedResponse[]; + readonly filteredActivities: IAsyncStateContainer< + IPagedResponse + >; + readonly currentUserProgress: IAsyncStateContainer; + readonly currentUserDepartmentProgress: IAsyncStateContainer< + IDepartmentProgress[] + >; + readonly activityStateFilter: string[]; +} + +const initialActivityState: IActivityState = { + filteredActivitiesPageCache: [], + filteredActivities: { + state: AsyncState.Inactive + }, + currentUserProgress: { + state: AsyncState.Inactive + }, + currentUserDepartmentProgress: { + state: AsyncState.Inactive + }, + activityStateFilter: [] +}; + +export const activityReducer: Reducer = ( + state = initialActivityState, + action +) => { + switch (action.type) { + case ActivityActionTypes.GET_ACTIVITIES: + case ActivityActionTypes.GET_NEXT_ACTIVITY_PAGE: + case ActivityActionTypes.GET_LAST_ACTIVITY_PAGE: + return { + ...state, + filteredActivities: action.activities + }; + case ActivityActionTypes.GET_MY_ACTIVITIES: + return { + ...state, + currentUserProgress: action.progress + }; + case ActivityActionTypes.GET_MY_DEPARTMENT_ACTIVITIES: + return { + ...state, + currentUserDepartmentProgress: action.departmentProgress + }; + case ActivityActionTypes.SET_ACTIVITY_STATES_FILTER: + return { + ...state, + activityStateFilter: action.allowedStates + }; + default: + return state; + } +}; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/reducers/CommissionReducer.ts b/LCU.WebApps.Operations.IncentiveManager/src/reducers/CommissionReducer.ts new file mode 100644 index 0000000..89033db --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/reducers/CommissionReducer.ts @@ -0,0 +1,33 @@ +import { Reducer } from "react"; +import { + CommissionActions, + CommissionActionTypes +} from "../actions/CommissionActions"; +import AsyncState from "../shared/enumerations/AsyncState"; +import { IAuditableCommission } from "../shared/models/Commission"; +import IAsyncStateContainer from "../shared/types/IAsyncStateContainer"; + +export interface ICommissionState { + readonly commissions: IAsyncStateContainer; +} + +const initialCommissionState: ICommissionState = { + commissions: { + state: AsyncState.Inactive + } +}; + +export const commissionReducer: Reducer = ( + state = initialCommissionState, + action +) => { + switch (action.type) { + case CommissionActionTypes.GET_ALL_COMMISSIONS: + return { + ...state, + commissions: action.commissions + }; + default: + return state; + } +}; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/reducers/DeadActivityReducer.ts b/LCU.WebApps.Operations.IncentiveManager/src/reducers/DeadActivityReducer.ts new file mode 100644 index 0000000..33a3c97 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/reducers/DeadActivityReducer.ts @@ -0,0 +1,44 @@ +import { Reducer } from "react"; +import { + DeadActivityActions, + DeadActivityActionTypes +} from "../actions/DeadActivityActions"; +import AsyncState from "../shared/enumerations/AsyncState"; +import { IAuditableDeadActivity } from "../shared/models/DeadActivity"; +import IAsyncStateContainer from "../shared/types/IAsyncStateContainer"; +import IPagedResponse from "./../shared/types/IPagedResponse"; +export interface IDeadActivityState { + readonly filteredDeadActivities: IAsyncStateContainer< + IPagedResponse + >; + readonly deadActivityStateFilter: string[]; +} + +const initialDeadActivityState: IDeadActivityState = { + filteredDeadActivities: { + state: AsyncState.Inactive + }, + deadActivityStateFilter: [] +}; + +export const deadActivityReducer: Reducer< + IDeadActivityState, + DeadActivityActions +> = (state = initialDeadActivityState, action) => { + switch (action.type) { + case DeadActivityActionTypes.GET_DEAD_ACTIVITIES: + case DeadActivityActionTypes.GET_NEXT_DEAD_ACTIVITY_PAGE: + case DeadActivityActionTypes.GET_LAST_DEAD_ACTIVITY_PAGE: + return { + ...state, + filteredDeadActivities: action.deadActivities + }; + case DeadActivityActionTypes.SET_DEAD_ACTIVTY_STATE_FILTER: + return { + ...state, + deadActivityStateFilter: action.allowedStates + }; + default: + return state; + } +}; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/reducers/DepartmentReducer.ts b/LCU.WebApps.Operations.IncentiveManager/src/reducers/DepartmentReducer.ts new file mode 100644 index 0000000..8780ee9 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/reducers/DepartmentReducer.ts @@ -0,0 +1,47 @@ +import AsyncState from "../shared/enumerations/AsyncState"; +import IAsyncStateContainer from "../shared/types/IAsyncStateContainer"; +import { IDepartment } from "./../shared/models/Department"; +import { Reducer } from "react"; +import { + DepartmentActions, + DepartmentActionTypes +} from "../actions/DepartmentActions"; + +export interface IDepartmentState { + readonly departments: IAsyncStateContainer; + readonly submitState: IAsyncStateContainer; +} + +const initialDepartmentState: IDepartmentState = { + departments: { + state: AsyncState.Inactive + }, + submitState: { + state: AsyncState.Inactive + } +}; + +export const departmentReducer: Reducer = ( + state = initialDepartmentState, + action +) => { + switch (action.type) { + case DepartmentActionTypes.GET_ALL_DEPARTMENTS: + return { + ...state, + departments: action.departments + }; + case DepartmentActionTypes.ADD_NEW_DEPARTMENT: + return { + ...state, + submitState: action.newDepartment + }; + case DepartmentActionTypes.UPDATE_EXISTING_DEPARTNEMTN: + return { + ...state, + submitState: action.updatedDepartment + }; + default: + return { ...state }; + } +}; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/reducers/EmployeeReducer.ts b/LCU.WebApps.Operations.IncentiveManager/src/reducers/EmployeeReducer.ts new file mode 100644 index 0000000..80e5856 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/reducers/EmployeeReducer.ts @@ -0,0 +1,48 @@ +import { Reducer } from "react"; +import { IEmployee } from "../shared/models/Employee"; +import IAsyncStateContainer from "../shared/types/IAsyncStateContainer"; +import AsyncState from "./../shared/enumerations/AsyncState"; +import { + EmployeeActions, + EmployeeActionTypes +} from "../actions/EmployeeActions"; +/* TODO: This is currently configured as a single 'get', + Will eventually need something like this: + searchValue: string, + searchResults: IAsyncStateContainer + selectedEmployee: IEmployee +*/ +export interface IEmployeeState { + readonly selectedEmployee?: IAsyncStateContainer; + readonly submitState: IAsyncStateContainer; +} + +const initialEmployeeState: IEmployeeState = { + selectedEmployee: { state: AsyncState.Inactive }, + submitState: { state: AsyncState.Inactive } +}; + +export const employeeReducer: Reducer = ( + state = initialEmployeeState, + action +) => { + switch (action.type) { + case EmployeeActionTypes.SELECT_EMPLOYEE: + return { + ...state, + selectedEmployee: action.selectedEmployee + }; + case EmployeeActionTypes.SUBMIT_DEPT_ASSIGNMENT: + return { + ...state, + submitState: action.submitState + }; + case EmployeeActionTypes.SUBMIT_END_DEPARTMENT_ASSIGNMENT: + return { + ...state, + submitState: action.submitState + }; + default: + return state; + } +}; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/reducers/GoalReducer.ts b/LCU.WebApps.Operations.IncentiveManager/src/reducers/GoalReducer.ts new file mode 100644 index 0000000..4788dcc --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/reducers/GoalReducer.ts @@ -0,0 +1,75 @@ +import { Reducer } from "react"; +import { GoalActions, GoalActionTypes } from "../actions/GoalActions"; +import AsyncState from "../shared/enumerations/AsyncState"; +import IAsyncStateContainer from "../shared/types/IAsyncStateContainer"; +import { + IDepartmentGoals, + IDepartmentGoalsForRange, + IEmployeeGoals, + IGoal, + IPersonnelGoals +} from "../shared/models/Goal"; + +export interface IGoalState { + readonly userGoals: IAsyncStateContainer; + readonly departmentGoals: IAsyncStateContainer; + readonly personnelGoals: IAsyncStateContainer; + readonly currentUserGoals: IAsyncStateContainer; + readonly currentUsersDepartmentsGoals: IAsyncStateContainer< + IDepartmentGoalsForRange[] + >; +} + +const initialGoalState: IGoalState = { + userGoals: { + state: AsyncState.Inactive + }, + departmentGoals: { + state: AsyncState.Inactive + }, + personnelGoals: { + state: AsyncState.Inactive + }, + currentUserGoals: { + state: AsyncState.Inactive + }, + currentUsersDepartmentsGoals: { + state: AsyncState.Inactive + } +}; + +export const goalReducer: Reducer = ( + state = initialGoalState, + action +) => { + switch (action.type) { + case GoalActionTypes.GET_ALL_GOALS: + return { + ...state, + userGoals: action.retrievedGoals + }; + case GoalActionTypes.GET_DEPT_GOALS: + return { + ...state, + departmentGoals: action.retrievedGoals + }; + case GoalActionTypes.GET_PERS_GOALS: + return { + ...state, + personnelGoals: action.retrievedGoals + }; + case GoalActionTypes.GET_MY_GOALS: + return { + ...state, + currentUserGoals: action.currentUserGoals + }; + case GoalActionTypes.GET_MY_DEPTS_GOALS: + return { + ...state, + currentUsersDepartmentsGoals: + action.currentUsersDepartmentsGoals + }; + default: + return state; + } +}; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/reducers/PayoutReducer.ts b/LCU.WebApps.Operations.IncentiveManager/src/reducers/PayoutReducer.ts new file mode 100644 index 0000000..b578100 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/reducers/PayoutReducer.ts @@ -0,0 +1,40 @@ +import { Reducer } from "react"; +import AsyncState from "../shared/enumerations/AsyncState"; +import IAsyncStateContainer from "../shared/types/IAsyncStateContainer"; +import { PayoutActions, PayoutActionTypes } from "../actions/PayoutActions"; +import { IPayoutLineItem } from "../shared/models/Payout"; + +export interface IPayoutState { + readonly employeePayouts: IAsyncStateContainer; + readonly currentUserPayouts: IAsyncStateContainer; +} + +const initialPayoutState: IPayoutState = { + employeePayouts: { + state: AsyncState.Inactive + }, + currentUserPayouts: { + state: AsyncState.Inactive + } +}; + +export const payoutReducer: Reducer = ( + state = initialPayoutState, + action +) => { + switch (action.type) { + case PayoutActionTypes.GET_MY_PAYOUTS: + return { + ...state, + currentUserPayouts: action.currentUserPayouts + }; + + case PayoutActionTypes.GET_EMP_PAYOUTS: + return { + ...state, + employeePayouts: action.retrievedPayouts + }; + default: + return state; + } +}; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/reducers/ProfileReducer.ts b/LCU.WebApps.Operations.IncentiveManager/src/reducers/ProfileReducer.ts new file mode 100644 index 0000000..1dc39bb --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/reducers/ProfileReducer.ts @@ -0,0 +1,39 @@ +import { Reducer } from "react"; +import AsyncState from "./../shared/enumerations/AsyncState"; +import IAsyncStateContainer from "./../shared/types/IAsyncStateContainer"; +import { ProfileActions, ProfileActionTypes } from "../actions/ProfileActions"; +import { IEmployee, IEmployeeSummary } from "../shared/models/Employee"; + +export interface IProfileState { + readonly profile: IAsyncStateContainer; + readonly searchResults: IAsyncStateContainer; +} + +const initialProfileState: IProfileState = { + profile: { + state: AsyncState.Inactive + }, + searchResults: { + state: AsyncState.Inactive + } +}; + +export const profileReducer: Reducer = ( + state = initialProfileState, + action +) => { + switch (action.type) { + case ProfileActionTypes.REFRESH_PROFILE: + return { + ...state, + profile: action.profile + }; + case ProfileActionTypes.SEARCH_PROFILE: + return { + ...state, + searchResults: action.searchResults + }; + default: + return state; + } +}; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/reducers/RoleMappingReducer.ts b/LCU.WebApps.Operations.IncentiveManager/src/reducers/RoleMappingReducer.ts new file mode 100644 index 0000000..e291189 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/reducers/RoleMappingReducer.ts @@ -0,0 +1,33 @@ +import { Reducer } from "react"; +import { + RoleMappingActions, + RoleMappingActionTypes +} from "../actions/RoleMappingActions"; +import AsyncState from "../shared/enumerations/AsyncState"; +import { IAuditableRoleMapping } from "../shared/models/RoleMapping"; +import IAsyncStateContainer from "../shared/types/IAsyncStateContainer"; + +export interface IRoleMappingState { + readonly roleMappings: IAsyncStateContainer; +} + +const initialRoleMappingState: IRoleMappingState = { + roleMappings: { + state: AsyncState.Loading + } +}; + +export const roleMappingReducer: Reducer< + IRoleMappingState, + RoleMappingActions +> = (state = initialRoleMappingState, action) => { + switch (action.type) { + case RoleMappingActionTypes.GET_ALL_ROLE_MAPPINGS: + return { + ...state, + roleMappings: action.roleMappings + }; + default: + return state; + } +}; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/reducers/UserAssignmentReducer.ts b/LCU.WebApps.Operations.IncentiveManager/src/reducers/UserAssignmentReducer.ts new file mode 100644 index 0000000..19074f6 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/reducers/UserAssignmentReducer.ts @@ -0,0 +1,33 @@ +import { Reducer } from "react"; +import AsyncState from "../shared/enumerations/AsyncState"; +import IAsyncStateContainer from "../shared/types/IAsyncStateContainer"; +import { IUserAssignment } from "./../shared/models/UserAssignment"; +import { + UserAssignmentActions, + UserAssignmentActionTypes +} from "../actions/UserAssignmentActions"; + +export interface IUserAssignmentState { + readonly userAssignments: IAsyncStateContainer; +} + +const initialUserAssignmentState: IUserAssignmentState = { + userAssignments: { + state: AsyncState.Inactive + } +}; + +export const userAssignmentReducer: Reducer< + IUserAssignmentState, + UserAssignmentActions +> = (state = initialUserAssignmentState, action) => { + switch (action.type) { + case UserAssignmentActionTypes.GET_ALL_USER_ASSIGNMENTS: + return { + ...state, + userAssignments: action.userAssignments + }; + default: + return { ...state }; + } +}; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/reportWebVitals.ts b/LCU.WebApps.Operations.IncentiveManager/src/reportWebVitals.ts new file mode 100644 index 0000000..49a2a16 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/reportWebVitals.ts @@ -0,0 +1,15 @@ +import { ReportHandler } from 'web-vitals'; + +const reportWebVitals = (onPerfEntry?: ReportHandler) => { + if (onPerfEntry && onPerfEntry instanceof Function) { + import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => { + getCLS(onPerfEntry); + getFID(onPerfEntry); + getFCP(onPerfEntry); + getLCP(onPerfEntry); + getTTFB(onPerfEntry); + }); + } +}; + +export default reportWebVitals; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/setupTests.ts b/LCU.WebApps.Operations.IncentiveManager/src/setupTests.ts new file mode 100644 index 0000000..845b145 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/setupTests.ts @@ -0,0 +1,12 @@ +// jest-dom adds custom jest matchers for asserting on DOM nodes. +// allows you to do things like: +// expect(element).toHaveTextContent(/react/i) +// learn more: https://github.com/testing-library/jest-dom +import "@testing-library/jest-dom"; +import Adapter from "enzyme-adapter-react-16"; +import Enzyme from "enzyme"; + +Enzyme.configure({ + adapter: new Adapter(), + disableLifecycleMethods: true + }); \ No newline at end of file diff --git a/LCU.WebApps.Operations.IncentiveManager/src/shared/Routes.ts b/LCU.WebApps.Operations.IncentiveManager/src/shared/Routes.ts new file mode 100644 index 0000000..6b79783 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/shared/Routes.ts @@ -0,0 +1,10 @@ +import Configuration from "./configuration/Configuration"; + +// Setup Parameterized URLs Here +// For consistency and to avoid conflict, have all paramterized URL functions +// be capitalized and all configuration urls be lowercase +const Routes = { + ...Configuration.Get.appUrls +}; + +export default Routes; \ No newline at end of file diff --git a/LCU.WebApps.Operations.IncentiveManager/src/shared/assets/LMCU-Blue-Logo-Only.png b/LCU.WebApps.Operations.IncentiveManager/src/shared/assets/LMCU-Blue-Logo-Only.png new file mode 100644 index 0000000000000000000000000000000000000000..cd2f98948dfc71888785b3ac85d76df3e466f9d0 GIT binary patch literal 16243 zcmeHubyVC-wr(TAAxLAvy&<@}H|`$1fyTWdI3WbL;2r{vCOE+%fuMolfdIiFNN^1r zcumfknKSpzyZ6mn@BKGGukK&f-e2wdwp4Z1B3esB5f|$b761UiRaTPMLA(zF07!%w zXoyek&s59-0CG2fJtHq2h!3s1r<<*VGnCfL&mBq&^>wfX0DQkyWCeifzlMEGXCFx3lOq0X&@}5zco886;JmKZC^9{4;t-qe!pT5y8X>hpPl@Wrc-(BQMmjI zk8|c)Op*a3jOSda<%*umee`fD)GSSjtx%j|E{TOA$Ogn>&I z{oNTx8%@~j2U6}oJ|7(hQ`{Z(t(=XxUwlD_wqDJ7ncs~(Im`VuRMoF0e^L&2ty!3Z z3VYxmyLY5tvfik+bDftS>AEzfq;iSvE!(wjh(8hO12>ua^zU7ZF4#nkyIJ_21z)+p zoYD$BIkDy3?x(W&G=r5!snLPU8vC*1W$(guV}O*p=j^)hlo)48{oRH2?di(1W9t!Z zI$^sVB9`q?55FmaZmo^eoOeo5V%6=%6w6Jo4X^ehue9GNXy|4>Cfa=}ousg` z38gUC(N%pSAM`z^bX2pC6*p%hQjVDtrZb&$498D9BPL)1xqC(rI<*?w*PlpU~!V{kg<3zu0 zr5tB@lw{duc{k2xSU`}79#_|GJC?iI2`bgDX^0)v?YOqlIPQfJX^LK^>S~Jpl5pCe zYP5KE`=X-7@axTR>d51RCu?P_>wAP6?#Zcqyc>lKYhd#H{P&)=<+0Tw8*bdPx-m~Z8_c;g*@)9Qze^I4+YTO`6MAB@+A;mnVGY-tN0yC%1k`XQC0SJi_mu-E%ZZm#8V zeiK0Y#YGb#-RyX?oCQsW-PWGox)+z2!X5})N}T*1K~C&%F4^5n1Q;GZ$8?70bre87j@-j6u+GpdPB%>iHY?!^+p+26VOUR zceWX+XFmy)+VSs)>4o(!Onhu0nPK?&epZ-}(y@>b6FFBe-r4}V^cAHfjaWaSafM5^o9D9JSE@Jq>Z2T zbMbiTx;=Vyy{JO`r%#Y8v0Y5J-mYKQ78;z&W&Y|BZ-ou<;e3f@>3F{hWbH_hMXBlz zwbN;&uF1f%$>it*wB~8_zT98{x3Y?QhmpDFd^;COQUTnG^E#as^O9{P>SL{ccGu3Y zkrmcABT1Jt49%{r4m&S)S)TH_9Cp#*9Ts)K@*vM z`Le#*%14cp6Zgs3>Z>OJLu}pS%%okGiww&0h_pZE@e@s;xKJ&%IAO^uXYTtv*0EzCo5YdC90vLs5zA4gCyeK<;r7+8jU&$t&8{=xxpj>S`@-?CI5_2 ze7_gpY|vO=GpO0@tPKTe!>j(Sw)&POASg(X}lGpnbN?KPL7+{^riC|ZYI#i+&*^lv{)Lbt`r!rOJZi6|*0D)DnmRL@h9 zISS)xT_24LbrK94ezoxp=_W1;#(w~BW^=+I1}B^p|s*A=LP z%k`Y(a*bL9i)k*bUvkhAZu|49)J#;=z#roNG$m&WD}nnOPH*A7 z5AGh!vXW)Arver$&Pcuo+y2y6_Cd`Chk0}IlMX*E(;bfEmY-A#t7x)dxx(Ilalrg3 zW7l9mU{#W#D&A%#ki`l4#+98o?)7CHbHOU+Q=?2^K8(ZR-8%YBjYkNzZ+P7_r-19* z7<1csrP+4c@!TXx*{dx>Z`Bs={GL}fie}m~8fH)4eUMK%M}l`=pe)MkXdcs)p>vm5 zi}z#J9LLzDzl$%8CfHNr<`-lKpWa@K-F=p zLb%U>`KnSqAtX%19T4=6j4Quch)Vef1s1MMq@{e3@?s!)t@HsoZ*M!&@F$K0{s+9W zH=&C&pmQ4q1wBp81%MLZC>WpU5R}mOSnprF`LBlNMAB} z7_a029Kv#lYm^Xz))_N6*TQ}1LUmgd_MLopKh_+eXBHXl)v10>T7>l0p)mOyE?mDH|`n(MPlpj`6I5 zkG<|DF^*>ndoNs@2;I%eUVym*Qh6rI8!IqgI>mMN4%VGZ&2K=$`2W>l5Y)BAY+KCNZM_Mutty6HBfV!^UHeyHyK$;@>5Rj42S_P^6=J zMja5RKTuK-6O|D8gHp20FH8?*UR=j9hP!P3`EmG*x!S4j+AT_Ajyi9>{ZwrwiEO5! zP9nkpDQ24}y*{hdDL!(!vE;opI=rnWx;jjvZ|_2n$z#ILR9}P~(SMTKb?Od1$FNHr ze;`_3fTdkr@mxnCu$eD27Byu~#=XEXglZ%!QTx@|pmk-aNO7IEiEjxRz7pXO(a|P< z15OVY&x|XsW}D^U8s)z2@}@f_(B@6-BHgJGNE5qGLs` zBtrUGodNA=%srDFWTI-O_I{k7{DgyjpGj``*rW%k!>|>dcinExoG}@H&6~;?$3Y-G zzo$E&ZOaaOz@8d2Y+xSc;sp^BX*TRk8hNdVZVO2q%@O$xM#%$9l7)vSt&a5I|^hjwf?!MZ56{s9vyEtVH!u1tpZer_8% z+>y`*xi~0MD|g)?%LTRT`-2B0tmMf<7M~HW=JCU&R|IJTQ|H z{9Y)Ct|x!f4}V&)_0tRbK#FB~psdNz^ufioMNrhfs{#eT^mVD0hNZgx|;HvC1W?w=pedhOgkCk?j&}7CL z)oZ;}l296wQSp3Fe10#)VY*IwFil+y{ZNteD(mPep5hmul#cKh>+lALT`>WBd?Cs^!dAB%4F~vW!z$pD;H+`9s{yL8h!cE*`a= z7DvS6A-Y%3*IUJRb`uwr(T?#aJfD9D4R15*F!{qK*!Xl}SHp_6?&3{uGVxEi$S-;5 z0=ar?4y_mDIr!k3HjY|SNbBl11B!B8oyw&X4PD90AKYV6J7Ac#kP-qxlaGmTuPKNa#8A8uc0Gs#d#T( zm8xhr@;zfE+2|2_SlsF3IyTJcxEiaG3H&-K3gS6EH%mb@Tlmq@)v*eWnEv;yuT55g z935oKIwrCxQ^npHIulmefoRcQdo}VRym};XR(ryztJHU)i&$W=MB8d5A0A(wF^4=( z{iBB@9+IIDDY^j@iavyLuyPodt%1A_EP5HnGEgTp8TyUIbZ; zO&a66-p+T`hDEM?kJ^z&rmL{+=VZWX)54&5Hu^%P^wjv#VwT;WZ~5j?#kpD=hZP}# zZ@M7v^QVqPZz#qwQKjbFC9(648scU${L=ktLk>&7ERM!>@@VmqAR7gvJ(ZeX&4Ov7 zxnaEeDN;K~hkZ)HFel(|I$S4ADrwUw$Gsjw#7%ovgZwt}X}h*%qk$P`ByWXT5+g>+ zM<$fM>2apVRXy2Of|12$=QO6=Pkuk zC?lYcx-PHsdAM!kLxLN7lLfu^EFNqMRm*6K`l7Pmw&X-Rj9EQWi(SITu&WICILHy3 z+4_^;`bpJ%uetNCE^^XA*aiSIHUh6DdvHo_T#&_p`6|HwL$-{GbiGQ@WpTkK?C^E> zGZVCpCe%A?@**y%WuB-sSaO@-9gH41*kMcmt*nWtA6I0=mvoYN_9tug82k+uc0t(` z;&1UiB~^LTdSn{P%BkxNu;8D-PHg>B@~SXoZY~;EsSd=K2FVXPQEK1uXN~1d1(xd$ zsi{dYXp3IQV4i6Ne64~!Rd^>bBL>gY-Am~@&YrZO&ih_ z+B-Kkm=2N49m%myX6ub+e#+yx-slhWnygh}5g~TggBrrRO;}pa`qf1yvF5}7M9`A~cECw?#HPmES`7}R_ zJUH+;7dbVR`J(j3#=+>*#{RyU89yrvf48SpunzU%C^03f#Ml>*xQnY(6RD>JiP`Y3 zX*}v9y^&AKgR0kW6w8b2-^nY>+5hr-XTl{e#rpm|$_pv|gOObrTg zQDgb}i&NTfFN!|i8i^&d;j1~p76qQq*qqs+lRw5oj`%6-AvPl4ES$0WZ@@8ilw=zR zGj#TuA^o`un{M?$nYbJu#O}Ch+v1>#H=z}m2OhTWT(pymSe+^;4{u<2cZnxm zpuZsJ@={>U%m8^|+sNC#8pHD0jcpyX7{|nqEMQ-m*;VD{jfQy{-R`qCp+}v9V6Db@ zFhw3V3qgerSh zEYtnXiE}CF+@g_0F5+$3WzZ_(EKa5Ca{F_FNI5KwD_3G1 zzcIcgLvzU=F|8-FS0s0p{((Qd(I;rK7vSLbQxZ8v%nB$0%;;Az2~l-ID?=w7i05ib zP{y+#nw1u0!e1$-gMK16i>jYMsn&a%PZz03fl^9PoSxAbw;AUjXOL3|jvbwNPS{pvwGK)+C_myx`S~P{?8!wZw8B5%xp3czK)oYfY`@i+-y2VpAfvC=% zhUMi`Jbrto^oW-926^o`EkaRrFyM4Ys(x;8;d~H4!{kBpMa5H(>ESZk%WO+itYphQ z8C7Bq_9uWWS}njYPK~+ng|@TLpXy!kP5ZH&MSJ%^PWipH4e8NJBPeMh96Pg?IB)X{ z^vfoxS;{x#MuVI$Z zwH6ati$N+Ce$bye?YCIp`-N}y+%_cIo(=BBf7Tobg4=(ZAS$>5Y@#meQ>TwYAHyYFgRnIc*5#+D$lLBx6BwHtkm8MNoEewZrpZ(hN7;*H zO4`qu6BS8}wWGQws(!&TXA61E%eY}V0uTi;NkqIUUg1fh&Qrd5^?7HUuZPXxv19;? zOq$~@=_)>k0du6-HsT&8+(AxGOIc3tZ&xpf3zyuWBnhQ%Y06#;U2sx5-cx)J45J4h zL(8HH^=dIlR7?u=(Nk1A@|ES1S64yJB$PKs6{5|;JKM`_7@;(@oK+ezoI0y_$+EFz z4->AB_7~Uosc8Ax)CAg?0s8Oa^u(EMVdYk%kC8Fx!ys|WLz6^5dU3@2rzHXd-^U)a zAHbd^uQ$79LVbmJQSpcO82WP1C~JcGK6gRny7Wt-vgx zNy$tSOU3TP{g|BXh~GnATVRm1j`MMDlW=+3Bd3Lw4(`Nhg9+18kuc%(L}@~Ln@KH& z2PE)%$D&N7@!+9usjZ8Y@5uxDXbkjrgttM6lJ7RWSK09L3p%Zw8OIU;J+jk2scu?gaba$DuEWmw*Dt`EAkX(UJ6~7<0Q_VJ z#9f$?x|*o9n+qqz#?1=K>FeT-xGMty#HD=QA=XY%FIp?8or9|c{Xts?J*|U{1ihhv zI=8yJ9Ms-H$=?&I>#w0_?eAnQVnZ(_i6!nUiU4qddO>J?U7THEqP`OJzi~wopYNNw z=xKk8csWVX8>ws2%DH($Y56(%Ik`E&z79`$=_Rpf#XW6oMRnvA{)9k0NzmJSdAW;n zaryZ8aQg6Zx_R1h@ra0saB=f;@$zyYBsgGxu3ivd4p$h%J;Wav@=%zyr-QqfgPSYu zJtoA;&D%?Yo*vOp`zJV}*Bb1LX#8`4`{&>AFfSV}WyAwNVm=4~E?#bKVGeFy4qg$i zzv?4;)z$y5?F##oMFgK*z7TgV9!_p97ngt1fO&zR{$spd#K!(Ru5dFep^!O8yx1Va3u0NkCuVShE+8wR~^`aN-H8(S{KtbUvR*FhDP)wTYv zbMFT`2N(C>8u!wFm9(+`yPUhXr}J+a8*478Gt>n!6Bq)S=bz*e`hQpWmy`Jo{+nr0 zc{gkCdlY4P33@~er;US+s4y?D9Jic|ybzy)0uK*ZSXM+(SXh={SV4$afm@bO{x9mv zt}ri%t2Oj*>JA8XegQtH4X+iHLjb~U&A|_~<>L?*hVpX=S&4}7TiXgltfAb0fzb4H zK;#3&`LC9Edn0gU`M`4W@;qQ}L0Nfugdwsr0(>$GLUKa90t)g%Lc+f>?pYL-(NdP6 z=jHqZ^>>SwGsMf*&C>;uT@J1`Za%Po298{%2N*s<~N1?z88&Fx2KgvHnagS%@9i??-X2|4#USL(;W(^Kt#Z!}*8k z-%(^dy?op}oisf)tsJ4&UjIGLzY_jCk`AJ%!Mr^El>diG{TDg$KcY|xq3h=9_m}Z? zp&oxc{ZVXf@sBQRdx)zY6j4_HOo_j>JN%1C zg;cw4guZjP;+b@v(>A2N5@q2nP=@2M?bfw}>d85aN&i4{y_obKMuxzs*+szEY^G zi~g05KPi%jdO9E`tKjM8@|%j^HA~dyU!*Uu5I?_woPaDJH@5{m<_J_8do4X&PeAs(w`ndkL!~YAxKM>R$tf8(jxBt%ce-aT#6ag;>FK6gqjFoZz zGjskHtM_Z6s52rC5jB;=25JlOcJ`u|RFl)Em38y-a&w`Tb@qnR>ipq*XNNzben-OJ zu*A9kI?x}Ie_4JJDu1*gmUYA~f$Q(vgg>zE%gX=HpFflF|K||2wEr38-{SW_bp3~} ze~W>COZY$0^&h(aEe8HA;r~R}|1-L<{&CO%bwzB0eGtb8p5Q@K#1RCVm8zmV;O_o6 z|9xo+q6O1k=?M$~z$UtXApx>;$qeQ*y)ENva`DE&n>WJg@KWa8}N&#qG-ey(p19}`El zmRDv24c6uA=Gbj2b%@`Ea~6G^1I;89F84voNGW(doNsu^*J_WJYpLdFhl3A?yv+C8 z7g~Ha<^%4eSd+=SE=IDh+cy37Mb7=|+ReEpnadkLkac!{xD!MH9sW{Nsxw7eMjP9` zzKm%q*pJxWtTBe$OnsHYS@r`=rG^&C zYKtG~;`w}!lvwza9tGv7h%z+GlUHrqqxU-fNW1IJM7XfYIjwUdefcA%T|41e{8#Q@ zNn=`wiF#QEz=!Ev6=@)yPq5%H>*W*p_Mf8f$d2zR5FG*yFvh ztt_e!Fo{b_rt-$%NG?B?9?Za^U+o`%M6ytPl!qP_;I5?ud@Yl! zj**n9A8MDHAuMR6WzG7XHtM4)9UN6(nU37Hk~Q)LE#AZHd5j@YfC#gdR(1!zJ`_xe z;W0zNJ_w_H$WsI4T&0cbJpm-{%~=iAsNxUB!fHKUWued+T<9m_R&~V+6av>h@OuhH zbKXT8Rq*B<_LN);I-I6Y=tyE7Q?gRpc_iP+7`T0I|QeGa_ zbZ&ik{g|DSEJileZ?l2J$QYPi*nV3W&prIQ|6>8DM=IxHS~K*CaXrSIp@&2`93LS4 zF*E>vYj&v;Bdd)8$hW)MDSKu|L%9Y}w#=5g2XnwyCkJ3lXV^XZN$n$lA+Vd>)q)hW zTH2=*#j#_sTsufSqtz0)-`h>Ix>t@DW0gwTt7)w&g{5|a!qO#l0vi+Mh0J)&8nu(n zQFA1;7PdML^CG0mzl+_~6RXCOtW0<8^gPtG=nE$mLQlTD3;u-}cwQPGkqE71MPGXI zo=o25RP~4lNVIk(dfJO_UK(AzLg7oP==zjnT zOcf_%zd5phhr41uXLvCr8JA+w8g^j{WO||8Mww2+(bZBYpbg7b3u=5@M=yRkx_(#& zp1zcKJ`O-|zvXzKdLdB(Rh3;V{1t+2mglfrU1PbKUe~!}$NBX0hi>ncHOZUTLHv6; zSgcq`fc7s}Q2CpV4_!EnbSnIa5+aA%I=O<%-SpLC-yRpRd{`?;>DbsGR4so?Lx}l0 z%aFT5B(Q?xdxP1ic&DcvFuZ8T@t4+skZ>~)ETuF|tx_Q{?_}Kh5t;y}5hTs(d3`3B zv9|nDi0t@!;BDn*96j=-NLJf^z0b*4PC2f4eDkT=+TzzQ5ZMn&khRGV-;gThJTLqO z=;n`+wVGJZ+He$?!>!6?0(uF6W50fM`4 zj%U3IlKC4?*9I&ESbmv5uRMVS^acN%8+G43rviisY}`n%71&%)R12(!iBohGc=|2R z0`IIp7;~@`2?p9q_RiYe2ra$zmB^xh_=B<8KbZX{yyi^DF^W$mBD%3jb&|V}M=-oE z{kB>Edj=xGJ}emQ&bd6`Hk=yY8XblyC=_8}RK%e}QLoTG)PEnJWxBfntzRNmqVa zcoXdf(Dx#$nIi7XMuu zADzSL z_|#-yEm_T+DB+vgFq!`wX+(TIJLv;YReR>mwmlnu?ltz7F0H-fYK~+X6$TtW+*$H= zJa())YcKgRnov(k3f7L23Y^IC3d949j4KbAv$#n@aVzR+u7e^W2k^^jr+Y{7R$rdJ zO@VKvxqKylPFY%8Y@w67Tq27I?_@Y4!8_Z9NdY+HSxw$H$d$uS5gChqoGld`oC3Gc z!sc?tySaI{6kMX>*#T31B3;tm{|!fA2lY6cUq)Mx{z9QYaNcu%9uP;u`yd14_|#9>9tgg>*m1uUIUtM8 z@nuQAxyIg5{)H#2&HwQy$|j0bc>`43y!vuvpMd!i2zHil)-V5la-z1OJP|l{so&$I zmd%Ql^oizXp!xL+sl|}~jPI<+w*u+1lSWufa~>%}oi0CF4=+cXBAq97S4Dudb*9<~ zdR<4^McWjYvszKEm&y7{59%@nVL+ELF=1+Lst9yXg8a1wHe+}NDBmGI8Q4GV*tZZ8IG&w z5wQ`BC=#YmKbD5g2;_>(IyxEd3zotB)IF1EUB?v3JA5j8isUF6fX{E+> zFSG!|8AB%bO-Zr~L5xU6ZwKrMpOHdjwS5T*W8eZtj5+W`?jk{|xB5{gob(1!1t2sz z>>RwcGdmU_(;i}B{XNllUsH*Wvgi07f3Ni{#|00N6<=aEq^K%8H_!o@tS{!e31Vae zjj$S07-7G=5gR~)YIU`k7FcHZAt*-6)F%Ok55mISC`fj*XHm1E6OTz|-4_5y@WEXz zAc!Y=Al$_Qfwd;TQwXBhVM@aQ4`BSHZmQ{L_E%<#f$J7ILeKDy6G*{yboR9d>6456 zvf2|Gc1<%u6bW)~!10cNVFy@4mcuiK7Z$Dz-H>fYtxm;tFM75X5RHtBs4>6y+v9T2(KrMgib0|Uh~fb2XP+uc z(*pKdLeONj38lHh$&J<#cD+)i(iogw?l5s0l*zEmwqOG*KOEPX@VX5a8Qsa);3@*)yq|o&^DqpK2*4}3 z(TnM!CPa8f8g!Gs35?YOf|pM=!r-6shXspft?dl0W0D2u^PUV-x9J&L%WCUHm^EU6 zlcGtQsxQVppBTl^b6<*F8NbBk!d3;v@UnbWi7-cK!{5&p|5)2R5deZcPJPAF5Wa|6 zRa(&S9-Uw5L^$H**Cb4JnvO+REiE9@*xK5=b{V>d=H^!Tw-*)f`#E|%B+l(I-S>n30({700~J)fo{0EKtfsl zEXfq8WR!xoUcaPpA7bmlNcN^pZtaWnne@FkK8W?8Pk>_Heqt3>1jSUXdB zq?_}X7t+@qDv20+K%pZa;7`JG;&vVfHt z5Osy1+Ryf^0cNy~sUfSKY=zh`--``Q%uH0bjrR1F0-2cNbb7My!&pU$xc0+d)*sY4 zy>B`t^{+)lI%Joc=C=v``-tTS4+l(lP#}0_YL& zL|-qz67S2Fd&@rcDIxxM;od}r9G9pU(jXOagrgUN1@9s2xQ*4#e`9;R=^l`w1%?-3 zAod_(^h7CF3nD|`06;KLl~}(BMi?BmyK?h)FnxtdxLH6Dp$;eWUqB>KjI)trs#Yp0 z2A6>2fyd4*-#dKsRXD!~SlEo9TwzH+C zr1zGDt6Vtmtq@B^;q@qJ3Ohn6(%euO&zV)-%9CksPHVG-eWKo{&FhZ5g@W$Z8FAzQ zyLNln;dDCA&B42|Kts|M`cH%oE?RH#mq0C9e2~pmU zGL|#m4896Hffvxp!RUE1@{|yw-vTUvdG76~FhdQ>r|RSz?>dYdvQ^|gyEjn*w4Zm* z&~6cjD#2VKYmPKB@x}O?311J(X_epo>h#O?i7Q?+{6%Y&ny8+n@LC@uDO;Uh{wSx% zgC32g>p8~#;OfhJR#v7XSCrE2`#)aK;n5XWAwfxXbWIiiq0&!tCW? zBcpPjZpQUgpm;#+q%)x|@HKlRfb#pa98M?1O>Uu}jI)QZm*ukuAV4HRzCZU+aI&{O zn)p1+Z;2vz*@&=EB{>n48ct@G90o36@A;I>$fT0U9x2i%j~*SMgT3RCatXo=76{=3 z%)H35#}o>AHSUV^Sg;Uvm|Y#9(n%Z$ry4;9B{4+rl7p?Dis0pEEDuj`Uu6uZgJXCK2a>w?fia0TL;zwJW=S~VM)$`^Ky_>&DU(vB)ob=qq_T~3 zMZF?S*p$x5)oTyKn?M8AR8WhWK*-dG!-`DEqa*^2%e$R!)1%0e`rM8mgyHxc-M64H fEud`b${kYV1Az(Q=1cZzF);`G~I z*L^?l^W#001%h*cD1l_gusB75LBEOQ2iAU z?BWIi3UCQ>T7md^fkGl&AVD4xUVaWBFAqqFn@5Nn#K*}466FyTFiGb zUkY*%cPlr0SD3wvGw?4(3riOdm;@c1(|<*9a#dIVAHvS=|CuQGlyQ4oxN?KIc(|RM z{>t?av^z{2@_*R)ze2m~`nW>4wIS{<9&T3f?_op#Uu5{){l5qLFQB#6|G>F=xIzDs z)7pv~0);q1oMG;8ra=F21m|2-)6E|K0WF|%E><3YdzIxR=sY~^twm+|Z=%M0cK%g71wDZnx1g=Iwq zg@tALg%yN&6?kO%!EO)>n2VdPiwpF>s-g^q zdbq>f{x$=_I)XqJ1AAv{7cY0#zhCk{yh7aUJt5W#ZZ1y1e?En%{r`mnULk&d0kD89 z9}ka$JRe+gFi2iO7R;|8tN;>}e8@6hCn5TLd=_g__n+QaewdHRr_tJ5PKh$L2 zXTzUikfu&PU8Rtr{1K#U+Si2m87p(1E6K1oKYZp#*T)WfHgtK_N%%EA_G`wGss2?_7m}&Li~R9wiYbLm+#1K&?NSxGl6=PHx-5Y2 z8J{>9I}+@6DzUfr?o_0U#+BCodrSLG%U<^0w)lj*plPR{QPEg&C~j)L6zX-Il<58S zI>J^x9@U4bw<>@9H-BA#vA-k!W2QC)KTl&wLq%_Mkx6kB6^-zrL9j$=ISJ7?Q^^23 zgtz|y0lr4g0lvfOso3-?!6>zmsem*LfVcdj4r%c;ujMHJ1#&iL1mq#%Zac7^PsSVe zATZnlgQJWP@&E0;v!V=IfSvcKTn)s~odcZddLrMC4B+bFqx{n^9l(Giz2BeY?oc*> z6m&FOU=<9~UV?*Bd>6h$dK}X|G7}j)hP)X}&FV#_{dbgU+g@rs zWSVX(YqgwNtN)0kU6{s{>>j;=bUTtuz5Op>0^JcRP>{;cmFeCJ85F!1vtNhvZx3so z4Q|vR?Z@t3ak`Ko(hk4JSJmj9AZ!i(`!l@d($^vN000n$qH|Mc;X5c4AplO6^Ql9eT8~ z44W&k5+1PqIPh+c^5xcS6aqQb#6cG~pC4It)4{vLVKLiQLlJzc z1{50hP-19R<*s7UUcbUJrIFX91@1S`FXoul_3xtvbD#%!QR0mR_h^VE3?IS#tOocf zhPy$VH@GC?0E-U+C#CUp7-8;1D|KNJEuU3Ty*8Sd+9Qez`DSEyUUwhu9t`aLKq`nroL$Yt$MQn*U@G7 z^zRnvOKgg%nsqVI?YNwkLL8YB@;9P5AYurv#33(5aE8p9j4jK-9xMAVlKeg18iRe> zEH7KLMb3^-oLeCWyMlSTgp0UeAnLOHeO#P|=#l1O+Y(^H|OO!8kTS zV#2bttMN_?$ufprwh`3G6*u9xjy{RV)CW)SNWD(CQhN~dpkpMll1~}1;Y?0)>-y-3 zw=(6$DeF2Y%(zNL$?vS1OkK%unTWQ)-}sgzG>l+p4YrsoADEYeRrqZzet*gOEH>uy z?T3j(E+tXikb9%aT;n3bzL21>te>Cyw^Z*QH zl&6G=kK;;SVV~eN0NJqaR;&eNi|se@-tF?M%e`y*19h2W6EmBQ=K4JLS{RE?apAi7 zXs*NSR2J`)C#nFAIN5{T3cbLGal3UErF*xH;`Bzy>^LSHQ-i%kF_egjvc84|U6M`}fdaFszKkF}Ds7c>YsCDaVnf0KYG}k7WLAkyqhj@=KhsVKQs}YHyI!`ls_VewgujoU}HIuFBiT+W311 zxwT|VY4VsOCowR(Yq)MikF!C?QGFw#4rAtmEMGil{h@yuOIy0cG}NteP@P#U5%4`A zB3cY<W^cIR+@Z&d8@MIMu(aIwUL*0P*QiK zEql>fl(i=^Lep)ja=L6UBCq+E$E2gYpn?q>yfpi^M&nfyiZTi2BgsVyI<4v2( zY8kcYJA^y{VOpiON)gDA=xvQ9FE6@-{5pG^c~s?0xt531K(#)e<-!c;2bS}_!{fBy zw6duugS4s5yKze0JDcmMwW@o4u1R{n)V<_zr8uijj1Pb?@bOvzl`P=A_v=`F)7O@G z-&~9?l2CI}kafQtaaq38a%9?7ofLEL@(u92MLd;kJ94lO;USm2b{WHH=VyGw|>SFU0OC`PfqUW@kEmronZ5 zM>}TAcB!y9(23%ROtV8(*Ycpxce^nYN3yyw>UfCL=H)Dp4hTrNJ|w65K5xSt7WX{K z-^vK_5ENA?2*j~dWWwBQF?u+%e<^XsivnAxB&Tk@dt{FipS#oIGjP#$nXf|~lr|Bt^=1#XwWPzVA`vFz~DCWPktIt%RChd#03 zyl?(p8nwS^`mFtf>4u|lSOpLEf}pH-By2AWK-HM$31hVc#K;@dP z;~ARX(D{3n7O=?@cs*osy_?1UPWo|E@l?;>K!*|O70<8NAO=iJe=9pmkU6yCD#N;| ze=)|YIdC)tY3pmp?KI=^Tx9U^TAXow^;ucpe1eXiE8Optirpq^0cSm3PLgzaX`#CK*0JC8bx-W65#k@c<}%3u>7w zD41O|KR!7S6_JuItM>o?B=~!JIO3B##{wh+MeFxqVkv1r*X}!a;S=-sTp*uaY?#sJ zu7bV3{k!D>eB5>HbTHILci4}Y_*?rl4gxxw_wTLv`Nyu3E-wTG4;dAmLPbg&;neZS7juKTZz32{&4vz=WGZ(ktsJ91b>+vXwC!vq zQxcufw8hx)Nm%_$L_D1HG1Z##k{na2+xyKsgW0rfX28@|@apnh&f_o1>jj#}q}JP( zb?rdV9p+*4O(#lE5PNi4;i$t0l12jFvOY=%4-fDzOCJC}y5RQ&73QL~bn~>eq=_qP zcHy}Z_r|l<(Z*y~8J8Zr{1KI<#ZbQxyrx|_7bwA#nNoqp=ij}JI1y-Qa2XS*@i;pl z2fgOzH39?g*1eqC1npYxbO2Q3fNe?G`zzl2VQjvLDtW9$G7}6U1%v0x{r;FWJJEiJ9maKb6 z-uDFFdR)&*(dhko*0~XAW-$Hmur{P$w1HJz;rJy^>XMh*bU-0%2YoqJRuNAYOJ@0c zg20c|Yv=ip?bs%{_;M>>#kK@M0`hot^c~g1-aEptpL6@nP0TqS`S5`&-i~rOKV1E@ z3~w2eEVUIf&`mDW$!IQ3y0_9SPu#YF#N))rTAgiZ45*}tNR z!4&siItXcasL^6RMB}3~YrsqJ{Eh#t6Pf(qNtrTLr$q-HyuzqIzz9feTcSXVLb8_> zu~SX^f<~Wo$Scn`EWX$4d!@Mgt!x-2keO}x^jq90eLeUl*%&#TXKXD(<9!f{p zzQoRjzEuI`s^VFWy5Z$-eS0W1SW!Exh{DM`5ST|g)ON{u`|60)U7)w6Rn6+B$zDur z{S0Ql>%^;NPiCiOuGhsqU;rJM!TTIHhtyegeM|SxG)<0feCN6L#JUR#ZJ|q8g;G$F zp8iFaW|vD#<(5$3^`0+RMMC#zPQdS@L!_=hwQ7K|PSU`~TlYF^lmy!Tp2%@saZCuQ zAeM4kEFCBcDu7)`h}24Z-6EK!VNefL`>d53t-Do}HJ_LX(I+%|&&8LHN%F&RQD6?j zUoc8S(V;nvf-Skwh+Qt)i-Rt0tyr;Gm`vW7q0Yogg~x1mub`wKta`%)A*wn78AFJs z4I4Eo+BwQd0|G(7lAfy;YE|X5DKxq_8Ytcqd{Y+g6bmR>Mrg>u zdT>+HO6j7t7`K_PQ?I*9}ao`ooO@jiczUb_8${ z!3T}DurmLPgiS@I>?y0VQTU+rFxw6_YXtyTsj8v1h!?Ad!utUB#&~RVmvnp*60yr+QPLO)qmfbez5S4K?;!&0`S(#piGlIQc=4uPGEa@61~Ut|Q;=(~`pU z8-_MDpG-B!n2e*WxFG0BRJCS2bXCBxWWC0wf#mw=AHP4n9bqes2fs6trz8U)n>S|h zJpPG251_eLs?%f@W9JV|tSly-oj?0s_jCAa`iCFRXe?%*uZ-mCn{yb;V)PhmG zCy@szJBv(95W8wH@OWj2}OwqMo1%9qVXpaK@P;Xp&e0}{J z&}{%8r&-B-o3PaKLKaK%?ngmh`RN}J6{!g$8wMr&&QAuOpEbs(VMzcv8zsSSGyywn zHufRNG=;O>Oj$iqZ&q&j4m-|Ti^#&f554x@aoHzG;+(boR6jgjUdumyw|AP&N3oQc zB~JbBSElb{`lYp?bQD~R=X}*hD#*yEtA)kwMWZl@spMGua?M`qlYMk3iwrFbURu%T zIRmBz*jhuz3vY%jASK`c@=vHVCj^jV!EAR_6>oL*-~F6P4KS6iW)b3(Fb?IVsFYnxcr;4P(m{Sg=!T~%o! zGJUR zIL?+}%tgMrW1mI6wCOXgm`zHSse#Es&IW`}WmReX>ZuBqw^)%Qw!jRQE^LDhBNOH~ z7`)AC^d8>*J;o2;GZM*YI^De{#AJ8+400Z(BBbacL^FZEzu0V#H2dv*7$YD!;FQrs z161J12^5F$s=MP@Vm2rhdq8X}5GsrdA@Sq@?A3nZ+>TswTb2=M&| z^Kb%HC)MjlycG_MEHK!HI(-)I{oyhR>Lec?PlY{5w9=&Q66@?W;wQYc*ly{Yg9r}u z;IIr5P%Pqdi+XBa4m;~B>bbnYft%Q$HPlagdO1U>se=EUXP<6X+x-T+!qGfk(uUf7 zl=|hkl{|aT{5Fr0^#BagVg<$QX7VQgu}F-`CU2LnJwJxKDA<=3=8}H{{D5Hj9Pm=eWGBGVF+Jm1 zu_ODRzV_)1w~MIE4Bg@d)kay96!x4TxyXG5IGZokI)dzdYo4>q2&5>7L#I)LtMuNB zz_)HI=p1L}a8bW=aa=Rz7>jyM9{Hf}};w;q{jjB#Hdt8Gp-G{%4 z4CAb7=6%+B%@&VYbZ0#jQt1#;(fNfgzB#BC-_XR>e01c|ReRA%RAf`uyr}dQjd|je z*h6NQ!T}L2HW!iykgPDFnNE`>Y2;H zSwa6I|H9P3MkcX$Si?q%Ky7539HC=bQ2Wqy65pJ<(B|0Gg44? z8w{!)Y;wH(uqXbE)n*7Nj47it<3Fi(X7vy#4M0ux>dPfWdc8dyp>?uk$U7D?*g*C( zG4rfLX@P~YQZ#pjZFHc_vI60hXF<;)4$^BOd?}!;0mUGV06+eH9K=+wF5ELQ;BI^Sa62oOz&CKqcZTF@;|q+n|@ zQZ1C^U#egDB9Dbco<#|V3xE{`8WfsE`!aSvc5G_1T70k?Zue8@Uk*c@@lx&|$gH3L zT%4$Q7mm|7#@;63X`}v--L1mX!vf^)cqEqv4B}9*W;Youu=Fa$2`dO{voM2~rU_du ztte9{+qq}%APl|J1T#KRa#Lc=W%eaO%2^DZ#JU;Dl#rS1`8GzQI6)8hCwMp>`6~z9 zeU&$BPu9rYSl;8JGYNtw|H?%{%d-%3n$SkO0N!ZLm$f!w6^N`6@c83W`6lhECp%5LLj=bFLc=c4%fSEkq-~DMfBshxK(N0*TI;pSLcdSwXvpM)*D^W=_F)P= zx;y2ZM5n|IM-)c}QpR)KYiaDCCO;t7SCAMSaiw@<5f#?Q==YgxoS$##=B40v1D1;x zM(c0jqBS7($mIc#Q`Fo(9@vu1wa|1>9+_M+=V--C_&B$u|J-tvstf4ox@LFLd3VDB zG4wyVx|t(+?kbtHilr%2eB=E-@mA z%4uY5ac~H%TBxAX@Dmu^6Tzigk(Y}ssP-8telo@l{JFSrAQ;(knuEsVyzmX8I`a$Z zuOm6nOIZ~mxNib>+Www5i5^S>?_wKN14XW{q9^{G?EO)GS`75Bzq?uXbkdnHdwhF& z*4F%nC?flS1z=yZ10BsFD1P~RJBzuLXa_Ptc0 zs=GjfPq(+(UZwFBUZphIf$~qyHJMt>no>2LSC1$j2iG~jYH8)5g|K2%B>Rd`Z-MV< z9BqdCrXV#4%R%LKYa>!ONgNp}P(gdNmNzR0JRE?ooQ5KVc4VeU5W{}3P0TqWp248` zPB+7oH|YexNQw;1wE8pNiH(HH_4fBS_Oaub@t9bRwf4(a8}lL}le^`v2dP8$c=-7D zEAb28Udn3i=YJnbjVb4^OKi=MFK--KVCzdT1vwM~Lb;>p@na7nR62dv*sLl<1*IZq z{igL+_vV~V+_7t+IaR0MM z@O*k=u)mtLN*fCu`2_tTu53G9flp=Tu7!NE+$Irb1V4?(u?!;lD`!0tN zv3;`uVejB7-PL)>H$%nrypwiQ)>XFB)^62@)YdFCA_1jkB+~A@cs{f`7e=+gcAil>8@(v3_^;kTK6TkG4T3uVmz8@Rf_SOkQEylY)3$$42aH zE;=p1!eJEmknnBV?q0$$x5?TQ9k!`peuE@%dm|JqBXJ;O!Dp^NC1Kl5muyb@>s{cV zWKpl@qGFx3tp=KR@%P^Y9|yh{kv=%D*4d~8aPy{+56_t z<=t*x=lQjbP4B*#i`E(Wefm$-6i1nL^mBYw?6!J-H>bjC!5b~jilI+O`w{Q7qu)+{ zJJ`*4pDUvXr?w~DSWN(iy+;UGNClf)1XM?AGZi683BnfZVz`X1;tw3$*&tH27UwUTe?u#q#2{+FqFzLaZj-cZ;pe5+UqxC#qkZW{@P&Jp#*Q;So-Y%S>%-Z+-nUC88IjSo{vh}jB z{pkxc!#eRdL|?Zo@T4{fx;Yh9ry|lI&ZJU)!&d=?0MV^<#YmYn6uM#wZ6u`9+YRbd ztOar}?5%KyMcGfo!rhZG3r7x7uw@55a9c9*b4iGx&M%f6R9+D0BGWUP`Q8cCoR-W9 z$>JzOAK)RUvWQ4$?VApFuLeRZ2?lz(xS{>CY1^&5`-a1u?B`AGMYJGeU5>21+O@Sh zySP`A52ZTM2%trmO-gy?gA@+*kE7H^DS{r)>U(?UA-JL&R$%TZzMP^Nh-w;D-LYSB znU}SgUApQp3Ypmq&avlI1=N;)aSbxnylnmM-O}_6ttuJo%r#A z4`_!qInN0G^xY`XB4-@ptLM-a#g1O{#40q%+yw0U71qH}K50)vR8shbelkF>VqQ%f zt*(X5$uL9VQW<7c6GI3+O>qn68$PKl_l3m})kY47Q4cir*Yg*N6B!Doc(C-;cEepz zto!G->(~VC*jPLGpBeF0Y*70Yb{i@ZN$7EbnGHXS(Q54`X&k+#%if0oOoXU!{QM2N zIf37$*Z6aeP4($YCik+m6Nfjh9)2E{w<1#)&iA_FuhViaYpIHM^qF9^J>Ufed9O@* zjg?Qp(;Ku(MveChgTy&io%r>A%>MMb07?bqL0S|m*SLN?Gt1r{_6$&tE#EMb$eVYG zrRToHnrR27r6$ADP+aj@%<35mwyff0Ml(J{yq5FL<}-b8^O-cBDV+CPcpTBR<0t(a zU7Fjc$hp5ks4+=Aim79=i>Y_B27ml?{r6_h98b?_{h0wgOWKkCvo)llg5sbG!;Qv^GK)Ob2_TRF%}Ub3x^vfQl_Ad{Wbo=?&mw~#EWv+mWU zCCT8Thj#Z4;C2)^BK6l~J$FvEK<3|j1qM_ccI~oxELS@@99_?szB^l)dz4y?Xefn? zuGA7?E%>~g)J^7Hz(?JY0sTD^wQ(G0HVkV@nQNUXC`Y;x(qk@QcKwpGVFS?G+ zDsT(8Qi|K_wEje4Kegj@G`XMd$cpZ(*Yba#9qTMtuTHNZ!r&>r!(vna!#nph40>=& zmtw)YjA)#LyK{DzcOQKBmc0Z`34~j&PzBUu~VT6>t8I?s?fA0YMV@x=) zlRy3NpLu6g>IGLOg@o@8%m^Tks!uFp>~r=0^MnM~h!mId1>F^~#W?;G2q>%H=d8KP zQQ(_iqi&xf>W2jmo}vJ}iJ{-=N$*Kq&+|G4K91daMMTWNqtP;yHj5}BNdUgB-?62C zY8ue7)q2eNU<8QWsdc={d(gGm)O)t`C)>rC>BgL%x}bi>)=_%XR9UGg<;)$(A{Wa8 zK;voP=)#AvasgPD0v*>jcXVa@Ja@zUj;S`sk z-42CU7F}D({zG!6Qt?nHF15iwA>^*WJv1y9*PitAMA?&q?BF_CR@4R9ujo_K&o;zY z(-pZ%mjcAfX^WNiH?eYUHNyE`)BwqSfhxseicP2q823pZiMayx&4t_^zwy+k`w@ujAc z`sSw!iIn(&`Ep`SBPR4tm6~M^kHq5S?Wh!h9M?a@PxxncSK`eS_5l}}?|*oHtnK_L zExJhruFX3xghs2e_OVcZOda1I8E!Ar(KWIS5Hy{tx$i78uc;4%l(gw?9F@?>`TD}G zhKMxa+skn?!w##kt3>pcK;&+P@h`(RIZjx{T`?)FsuAYtdP!U6pa+@rBJN;&zTs>A zB6SM12;BfJnY6pA5pB05`wVaSPX3}%QbatIjSJgx=KE|osVaUXAnNfi^HYYfXihMM z0I7&)Vc5{<9D*tCPs7xcTR-8;$kAlDR@>Rkr zW!M)L=I8(ABqKj61WheMt$H}IfKWCyatkt0pGzmTnbR&FS6@} zk9)1pH~>Qu<3-$;Tw=RU?C+n5cft`(ol2uFb$P{8t`}0Q^`j0RO|b2v^f|I@mplec&=A!<(nlAsL!>T2Ckv?@SuWy9C#(7_I%$$?2LJ|qiC<5*7lpISUx*q!%cbk&kU=LLAuoU7LE5bD25_z>P%6_XB zg#yZ$=)9mVlt(e6xM7s_-Su^+paTC%^DBFIUVBNh^}MFMk?4(N15T09@NCEH+omi5 zAw`i5jJxS%cr+Gd4-tVTA0#k!-P~vjgc1T=QB*5fWVNRKocOKSk0lRTDcCF0ZJq^U9-3Gi(Hk zUvpPT7A(?BThz`)mA?-AJg4E-OWShS-^h3u2Xh=mYZ3AK#bLvdU4QCGLo@;p^{fSl zcV}ou6p!pY)m`9FUF>vx*7H{^CR%bAVr-rDnPpOQnGT$_f zCl{PKyoLb`J=xUSDw<1)IL7Athqco8{SD}gQmXAk$@)Bfxkg`VBbtlRLzdj@(#JuO zoKR*m?Mvm)t(EwnxF}}NzT!rm*4;h!4|$s8t(PSK1jg4>EHDA^$)~+G4{QzYX#yYr zJhrE}2VVW@)bY3fjo@+N-o+UDM&dVGS0VNDLcle1SG%VA$$CY=gRPe!5+MMO!Z`Yi zoqea?z|P+1MD1pV?WFqoTZ|+$8V$C#;M~?T_DkO{li%?PY?rJdOZr`(^UW)g&*gGk zk87FFOv#^sHsTnQ3(_Itf(`R*t?lsuWQOh08?Nfx8ws$BIwKEouLE9JGB+S~8)sAe zPugw_5RS~eld$nU@IKb7^lw7n=q2FDivGG@tI>IiSi7);cS+k)cG2lIN&qrV*nza1 zT}H6^*3~Scqoh4JjdQpu4h8nr$;7=cEjU+I2bzX4N$T_SQvoX@0($dM!9gK1^WXgB zAvaT?%+o>5>JS)UxZJ{GB&?@pun4V$rR?vt=OrTcWzr~UV@S>-9jYdj@KI>rh40t( zgh5`c5g7p;>F+6$=+84e6Q23C8Xmy&76!0WVHsn$9ilic%)Q6n`zq_O+q$4J z7k0IaGPIvq899_3&<8b-duyg~GSzJj?PcZgo$Qa`qPVa}$q2wpAM{q4N}k3*26DLF z8*>AZ0YY_iwH5pCMrppVXc{h~7ZUcqg}tnunbOiYFyPk}?x0y32sB+h8ScUlKe93x zc6K_b&qs5bggABuUWF==WN5z*0?5gltc7ninJ#-v1K8^l9gMueY5uc(9~&wYW)s6c z;$)J(v(>#f&W=O*K?vgBYa;&ybxOe|Y}Hk>rOO{_9kEfF!lhG(FT80)IAgyaTU0en z27VmXY+3+J-U_P>kF%9(ch6 z0G)Fx_XUPmOzf)l5_-hJXfh5*%!o7(TesHuwVb}1+3m1Jkmin-4!^$H85P_JxALZ5 z2svIP%cQj71t|@#HlM2Wu{eS>4wdL;a=}JuR1C{9 zzTz&Wz9CT876kCqKER zi6G+PeSjBP^t?a%YERN;4zRP8o7Ph$Z7o}gH+U#5K`$ab=vYtM1djJ6AKTzlr4__$ z9_E5sw@2(zX%J5zChb2h9||=Tw<9@9ibQd#YNoMjQm{LsE$+TE#ORZW3%1VS=g4-E zbHP3lFeVim+HX58*UK^WyDmXT?dHTW6pc5Cs4m1&z;M<|ufbCFRF1W1s;;C+tb_=7 z6WD+E{CdAk8K!@D_8b@3Ce40Z+x^kFXNoW@#lgH@JY=PHyyn`j+K%ONW7HykAX#{@ zB&9=*=WW7(M+A-N^KD+A5;fiSM`DOLS(dv@nh*TE!G7H45!o>RW8F&t7agh=P%Wy~ z+fkTs$lnu<9Xd9nrw(dT`>Y~xqlH_EBlbR+j{ORa(mi8Fx*kvVT{|+(4~3Y$Mo)^o zJL{0W%>xroBfR3@DvJiD#i1KX?}c108nt)zZP$!bFJu{ThXm`$TvGz@u-`z`UPQ=i zWO-SU#Jr1G8}0?v&KGQimHdL&;Xuj2$jCH^e8V#i3PzY5`>gJjvB0>R)=Gy1aYgX} z5+@-wLXGLapQ|FeI9UQ7xuiuBayGtREShKS0l1>Wqr?=Q#ocxBzPrDOjT+b^r11Rq zWBGZBWdAW&KFfUIBVP6~7KwS8rU#iQEAg6KoqmrGC09*wgB1m@bKCZmE_d7DLv!QrSnmX$XX~V6Nc)$6wqs6X|Sv){H=*iJZ+myb$+8QlS zFgnHv{pXr(4%OPtkokFWFWz0;MgIj@SFB4@(={qAiI!i7BTEL zStENZn(O04Gd%3?uUH0bfQ7Sv2x~Xbk>~uhYrIoAGTD-;z|d7&Mt~9Apg`^JdL+4?yG=wFA{v`?Ltbvl_!vstT!GVuA_&9nY}m(36(5rt+~`F z)?(rzlct~eI+pp7~{B^cVF4j-}HkxAv!*}C!#bu3fp6etDg1g>SvKtv9Uh3cKF?>Yn&IJcbJ#5{8tswXU zkz8;_MXj)}iixgd+~*;rYM?zS-#}O1ou8z%hoz?%L~?!LZs6D7A23QVWQ1o19pD~X zZr9nNUXK6ebcKfL_T-6}_bWUsQA8+sN)LSE>B8rc^3?ruDBBctj7sr+@bjEoig~YR z*1;>r{e&Y!m+zUjn~397tnlnuu~zc3xA8312+kW zj{==OdmiE}=r7W#(m6y+l1qpbIiY4&u5>K?UPLsu z5uK^KVH zFEU}#W~D_y$Ml1T{_zpN_SX*F{HT(#UhS8N?EINsXrZ7o+P`35dEukCs4jIib<5}R zRW0MOKNy%mVDSuUekf4XMujy7LE`Lk$x`jvg!e~a5}W4K(h_% z>E2;seI9Pm;xQhvX|l^hJo`NB@|ZTV;Md`8uG;s^R>SCl3O5ki z^|t~zcH%O7J<9-IOGJP>Hf~+_g7x2;eTGPCJkVmp33hnPk7vi~tzd+I+Jj!gQ<~jY zn%VG$^6&3R8bHU?)=W~;vhCE?bkY59YxfIu>F?I_pR3Grdp#6HZ=Uc_}e9%vzq*Bxwx{@lk^o+6v2SuOgr8mj;k zFZt66zi|S;AyQ5+52trNtHCB8nB17f@Oui|NpMD?N!xT;1yhN@Bn%Kn3qK)+%*;32 zE>oaOORuTu655D}{&xInRPQ>TT2J)TS0$Mc0* zyapGlr^(Ua!_hRmlnf7`g6IH#O=VJrzsiPz(LZK?ogLfgaOy3cs6%3s>FDNKpX;X~ ztw*;B&Bmv9GJ#DAk>Zqz$~BZ9taUjTItDNO>$dN>ckbF7$myq^u)Rby8Sl4Ur}ieD zZ+4IP+;_~j;wXH&n6!mmDlf$(B~22`8tP}saOm#?P<0;lLhnoA;~_+4{P4ky8{W14 zR--{80T%4(&?J0|5C@RMg6DBNT<2Ff?GO{D3m3Hu=|xOs+0SVQOkfh6D8?hVbIOWK!^{-#Nm0{{|Ef2cUHrw+z!BN=X>)5DGv{bDw3W*V**}qZxVITXs zaF0{*?5r%tBkhdWqfg^-J~*f`6o+ES)g-RT{JYiwFMX2awl+)?ERWG`{PjlmR5-}X zVQ_mQ)9d~|L*`?ky(#T}802~qR_K)H9AD0@y>?mDSb@3X+y4+axlRJWNCV60D`Kk4^Y*BQjwvu2}tROK_jHjLU zDu#|)KjXp!8_ZD>Xpc?A)BzJR5KH)}iX^kE|F5qX8d8YiG3*|`q*1Ypuh=H$KFB0I z9F0W^u-!G6+bGOp=%BVvWJ<2A!>=NVCctC=IJL(F?OmkDJzk5$0}_TW)x>5sQsFL| zsWkh~;&KS-rqA;TB*p z)<#|IEKjFN+kI2EX#HhHwNnA}RTss*T~3Ly=M?9X23v`@1S3@#DSMOzu!vNkiJTd* zi9M0irW&}BjL~1H=HQSDycO()20sv zM-*0am9pBGBy@`$!022&q*LOx7XW5T*4_0-r=n;TW_Czjug2lE(R{0kRavioJrxV< zaEQ92meZ$1RlqmQ@$A4dy!nMx{4>afTxFPS!+ujpETo|j@Bd*OVdQfdNK_oE+0~i*pOA#mXkBD16v&p zElHxM>Y}G(_0saVpdti5(B8KfPm)N5@AvmDiUy99|M-=q(LbROeJzrSuHQc`hU zGpT9d>ek(%X?BHhQDjO-cT%RU_9AvBOme1``%FLjM*GdSDc6y$^<{dq)Wb(I#1#}C<(8&ClSLui(U$%e05WN4X$?lk_=`M|w(z0~;a?-8tT5W&$ z^pvm0OZ?IvBGhK}Y6=e*=%QF2Mv6hV<%z8=)+dAS&Qx2T>L<_6!smc*NtRi^23d)0VGEPAoJX)gmVS4aHy8C5xB3ffSU{l#GoH2$ z@S^OwOg$J9Z1ZjEmq48hgJU&yklCl14=&Px{7;h$_!!l?>J%E$1XQtv^Q9vK9OHuztFQ36*&Vqkl<8u_}o0ivSWzJHLuf?95^ioNnK=xAE{Sol$pK zCU#O&HMFH{P8kHkr-KsJc}0K0vFoqbE4{BWG75iTQ+F5@KaPOPR9PUl*0Vly7y(!UjQ0 zDRwEFu@%s>Uk0%tzq_f-3=HsoCJIB8*DDW=Q1~ER(c8N1HIH^*CAw3E;k7WB zW|f`m6a8KsX@h2<(4T!5pGM|USnK`jo09SSDyogh#rE7{;mhfs2iZGszDTX;P6>_Pm zx+&8GKUSu0D;X)Ed9X$FM26FiNWbY)4)W#cT{9ZceS9Wj{L)Co3QymDgkK%tyt-Zw zTptscvt!lMLRF@}pF=#pT&b|EB1fg|_g7t?iL)cvZk8ic*v#p$MpFiqJGhb`P9co0 z%fCSG$41qjah})UO)MeliS%5}9rd+Sw=rzWDD3$qT=cgL0y5DgRC#~gr&>cx%95c? zvCWKpiHrk=Dbq`y>1Q>n9lq#P!KkH~STB@&?+Xihq-0eA9fJyzAn$#N{Oo2h znaGmVjadthprzK>{6oND1Vp?ScR$ht<%{Axk)ceFnAtAg2on)Mu9 zVH%x?mL;V)4mr#N=#Y7`BOO84O{0t2Gm^o`FF!UicZWHz>|cH|iR#}8Q%RkI8|}Fm zsB+z&Muj(~vzx!b8zdzzSyVY(XvcGgUQX7-)JS-1!-DIY8>)x-X8{#%pt!S zfiXYvjUXllDe%?>JSX+?bXH7l_-`~j7B3R z>4NLN8sUW+#VI?@^sg4lL2Y@Xw8H@3QLjupP-vyKL=rZ{6!9qlFwj;xTL7_KptW5b zPy)byWEbz zO?7X0N{=2P!cZDS8l(iIQ5@0(VS*r~(nvQF(%sUHqB1~Afgz#4%g6Wq z_UG9>=f3ycd*A!U^E_Fh)=Pbtsc_l@{%Njn3}p4o&t-JPpFY7$3cN$Tu>rCrsQkWv zo8NW8y;JqJQqTjgbsW#;C{)tTB?HK-sP}ZDE-%1*;#rmuo9E98Em-cZ2L?cHmx<$858xv*)Rm^MFg!j!9`iSfaZsA0KB)n14v?h zq7z-quCO&V7**e<d*FlsGI@OJ_yRb>St=EMBj^`J2Y;W*4%rHnjIe zJYcOkLw@Z*eOqQN@*_~>IsVbZ#_-b`>+}q{+@6T%EHF>F8i-#-)Ki?MeBXAO4n~J@ z6J-p!9jw}iqwR9H!ptS*D~Kc`(dXNw$(XdS0f+n(S&U2*88Tn>g{&V4i%& zZ}5#)7TZ4|;p{UtnP2AKe%K;1Q;1o?iS~^e^WLc2A^8o11N^N|v(5siyRlH4V-yekG2j!?di;!~;Q-X-#j}ng zVA>5}GSTgb%F}F@LK1?!BDoT1rFU4={zk17F&yL^sp?P%M+&>ssDf;v^;X*^5rZp= z$!neIG@^v+_HH_-ZwXl)A^K`#gb;6#m+l);p2vCki*&g+er@n~AjViX{l6~1zP+Cb z_SrJK7eE3p-KbnekP?Fe^0D0eFsd_2Qb?sfPondoP4P$H6egls}B5vu^bzHEkQRx_vnfEv~WP9+741)HxS4y}yKJjS1r zzdw}IjWd)VrUCVo(}T+8k=pePHz4-G9U9+8$szC7ZDsQ9DYFuOLkD2*Crrw20%_pa z2?lF{!=tkqQ<_q}uU{%>sH{9X2*pvOwC#-4;qJ*cbj~`_p!UGt+9%py5Wg)Qd2z-G z2frMCgJ{k6q2dIaNF~vbTN@YhJf0C7ZzE zf!-RN=D)+F*bc)K5%Brk;3UWL_(q(lVgn;FOPBYFTnkVU6#R2>SmSZx2Y&Jdz8w05 z7wB225Uc;uv{h88RTOlc5P$W^OYjBVb7D?$`iAcFKvXX1H#U{uDwe9S!@j1#YOe_I zU_+j-(V8-YHwerXjs>u73tN(-?mubDd%ni`);BOP z%S_^zWk|GPWY>ZLs)`thc&3NW7W}u#S(a*}`g0I*L_X7M&jRsP;n$}rQ1Gp&w~3)+pwTEcAJl5|_S2ysd{p}Q5% zRHUX-+Pi2s!z<~77+y8rHrhsz!%QwD@-fr3CJMA#ODVV~ihCUkK`OqgmQ0FYto-%hhapIo7u9eg3jAi2}R=!gJ#xC{a}V zMlllwv^Y;b@|o3d+MKGo`k^n5&j;lt2P(0EkCSdZGgi(}ShY1!fx|ezF&6^``1|pU)`C8uKn8%Rb&4wU^07<#b7jX(PF$FQTY2UPjt}dO|tUWF=wIE}{KBM$VSND`Ar`;cicAx?dLE zT)TdCXOh7oY7~~PzdtP-IY0;aqmK6YMPNhBszi9a%)u_N)`b8Y5Z$4~2hYf;3+CzH zk21i-WUjcrG_%g4ucnaiIp1UKnP$e5W zEt-%h?8l5jecB`S&I%KS+3+CHiY(F^2w*B9c7*aS5!Q%L z3`O5N=8_%(yQ)e8_x%i1a)FQITBQh&uj%H@w#Z?4SPSFqIv;slPy`)lsh*BH#WsKg z#Z_`Jr#*0_I+9wg5ROD2jDvy4PUOe@apb)>icfbjH*x+zjXJRZbE0~canU2+6$8Bm z3RJ&*w10pUn$Rq0t2ohZ2_(ItNz4X%!HI(&B`(1w7AknG@%ELKp67OIY zXh<>d$(EjT@gZ^0yLUT=_utWS5(5$W*Doc?N8hah)Po$WbK0n@rL!frzJsf{I+wJ~ z8VTG9R`m9B{ao~jS@*`#`6Ob$uK7l5R-@gy#u%IDCnOpJ#%1;EtBM|W*N&-9e5(Q; z43y9}cF;rw0OpLKY;TAW*AC}@U|_g`ZR*TrBQ(`|5wFu(G3y%!IWM64wK0^iqQ(F1 zOeEbp=}Z^dB)icOYC;oI_d{E9)cR1B`<-=BaQ4MGvzn2>e0@2mfLQ%o<=}N7!?9i- zYxZ}$_fKa;MyT6RF>5XO)6Q?wtn|$c3E|?eE*)`lpu~pGjQCxBp$WYc%Gmy(8zz z5XuT&h_ANCCzugJPDR2+zxRAJv%UvKy7UrnBI3_~Nz&=#7qgZ<;pC7Vdmd?sW5~n$ zyp&CsGCs_WD5z0@cKnx@$@6fn{SJHXUqzz9EnShBI{VOB<5P036Ma6s#WmN3I)bz* zv*Y@XnvWGPN!yf4{Ozyz%#`z1)`bgRZE566EM>b%0ojdd+50HUh0jXG7 zhCP3<&u*G!WAkvVaeugn6;006=ajSVy~zGX`T{gLY=%z>J+y*qTA-~wo3f*z7n4cSx~jCp1s|BRJc9L)%AX%+lI%0_ zKFZS}YIw;k5E#B;OqnxYGEa}9m}F0}E`0#BXnw9>-*&pVQ}i#H?s_XaxC{2%_7FkQ z2;UJxnA}`jjpwuzKuag<2=k=LjmcfOrb_GT581KIa8#PA`(df6BaSJ*H4Na@iq^Us zWn}c?vm+&jN>N`h_6^Ug3FP;==5-9dPp_feA}?}dLssvlxS5~<37U^Z^0zlN3MPq< zkyLK^LTO@^ebqrTm=r%Gn2xv^dmOYl5rcjysV(753O{Ub7(FlX$JGXJ7_$nY1!_E2 z*5#l>fl>EZ@CrJ>FO{yFG2aH>$@rD1YU|BPDs{Jnn>wPM>J_xJG$GOrjuFHbq>(MH zrP_asI9_;?mZnQVQ+kslNPk!e^gX2QFdiIdW$`%)1UNo#i3cr=fpKJF73D(1!mgnrDp6bJ&1NwlXPj+l(69~r0~r?7o~vpepsn)c0Px!m~p>Ym9vCoOx5 zhYGV(G^5ht0$uK5b@~YAkTkN;;Q8l^*H67x%huvzjCS&}rV2Q}wkEdM4%F4ryOJO( zdVSALpMH0BF!tJ>AL$JcWya9GGj>$#&s>>At4$NJ->yw+t{g@nNDIRnmm zul?H@qV5@+7S>h08nQ3RJ&_z-xwdEejD=r2a)>FV^icgYMCiGPFM|{ow%3jabwiY} z1WPAW;jc)RVTc;cRiYrwOhZ z_w!$Wi-}K10zT@JK@0h!gxh^LcB3@yh6dAuPDS$W2aLz9mkxQ#`G-HNI4bs;3r0Rs zbY8&Kv~fO+vwMp!^8&hU1nc}@HfK4~9iMo{0_}~#@A^V6_Eu&(f=bnd((4y_JO7QJ0e?iKT1B)$ESi(cnb$pnihTN1V2i&~S=XStILk!cg_xfh9J z7R~o-U$DHUVRrw!B6b}Gi6-~JHaku`0c^bE;vbFkrN|L~~8CmC+tGRab_-nIV%r4btli&eCeuDV!4OuTNAV(S1$1 z^NhqyoW9jdZ&5p53PpYz2sQR2m`t|nt`$s~-{p|G_30SbOZ*xgU*E3UY=xY7$~L{% zlQa6n!vU$A_|U#phyV1;A*NdY<)>hjBa4gF`1Zc`(@Q&j7U9C$8U~98 zo6#kDD3^lN;6j6*2UCL2w7+tzo`@ zm5os=I8qxL5Lis3AgM3gMx^-o<^6=HLin2JUxlBhO~|BS7kLjE(a^+kJh1HQ6F2U{ zUVSzBtq*Q?t1=?yZng10y-BOH?{5HEL#{m(*FaBy#PMBkG1JSmY%=5}NF9t@_4g6O zHT~CqlJhkxuO1lhc;+(bW;(mGTII)tovz!+^jei!II8uMF=FQeCn6KfOQs3 z&$lG-*$`Y9HcS~6O|m~M&E?(w)YMV%(cHj|yK%+GudG1v#TlEiW2?rDYVXg0j7h$y zQ;WUd;vT*}Ivw*S<}fuNT3{Nks*+?;oFrgUEcB2GkgrR#`{UdvOFTUokzu}gR=ImY z6*dBO=B->!K!Z{UMox;~8$yPEC}?;hx}ZQ&a;lPKx%EA8H{0 z;mUW<7woXV*q_Wi{S1BfB7|rGv1TL{-ktG@*#Qq6M;AZxsKDc*T`LP8Q0qQ zf|dRt>h#EzQDO(I0aOnGg^U}jAfCbg{%CT;awN^-e)j;T_UC8#8FQL_-LQSDJEQ%v z{;+G8?KNJA5c9l7e@@2qY1kvSzf40dhFat!rG`v}$XIk4+Y2C$h1INmB9E^Q0f_D4h93$Q0?nW~IHI;L8(R?YERK zU9#NaEL+Ovj0k^!R06&(EaJCTym6Yw~Gxn=RkVQHsB}bqh}Gl2=({zi%N_vazJU1a;FdJ+jK8 zldm4#;{a9BUun3J5>UBclkxmEZD5lAwX&(XqsHhkd_TTP#=G$7jBl^qZ^vzf+7|@= zo)zg~@cJ4{caP*E8|8495;us}b zk`fFqbTHL%w-9y$m*kJHpNFiBxv>XXe}Qn3aX~z65Irf{*$K@%8yP%tJKmy=YTv#v z$`%;U49Wc!VGyUJx)3io(%_aOXBk%8R%Dpa)$+l5BJX&WX*?~+xuZ#=(5J~KF}-e( zIWmth=5`l^8njM-U1WS&9c$&Uz^DjaXwKdH-SgbXa%pvv`60Df;<9i-P~AbU_~M_W zOeYnv_`;Ak3(Gv$70jxrgBw8G|KTodv4RT{Wi39=(_qD!800sR@-xQQV6uyLKSd6_ zHD}_F-}W7Ne)jx4D@6zd^7Peco1r_a%Z}bomaa{3Cdy+$&|Q=IGZeHGS9VtdqBB*M zViW(!uiDN{*eOB-&=k^W#oX+PxE@egQa+(*E5FRK@F@X6 z)?&MQ@%nnTdFx;mV(S(NUa1oEs$;QOatljB!X-A50gKS|M$^C`iY=s?!F=DpM;>WSJ|fOqVTmnIFyCd zZDFP9(9ilQ*R$x!2`2!@vLA_sMG~?Ny0hV0dd?h)e=cPH&i*TJ14!7>Q3TlqX#Io0 zdb^L!cF(%q&wCRZvajch-Uf9EvZeZCT_i1cebj^CEJzSxK6Pr!CKP4#|@N~6d zXQ8)im2u@tt#_Yz8TSAoypu4&1m`Qx7mAvhUv7ZGVb7{D0&Q8Kf3vpCsiT>2IuBj?XSXnOs> z^H68?7))zzkgFT}GcRzo>CHZcaGa(972CkQy+=6xp!;rcMGx~Uqu_?xKH!{@(=oeu z*FWqviTQ^E5}jlDR+N1MLze z_>`9@I>CC@=U&B|{|2Lg@X}iyNSWtu(`KrfYp{4MBj^6r znG!{N@uI)cK>xWJ#U$!sEK*a*iYS4ce+^-Kc=@*o5eK^*;_J?Mnav literal 0 HcmV?d00001 diff --git a/LCU.WebApps.Operations.IncentiveManager/src/shared/assets/LMCU-Blue.svg b/LCU.WebApps.Operations.IncentiveManager/src/shared/assets/LMCU-Blue.svg new file mode 100644 index 0000000..d513e63 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/shared/assets/LMCU-Blue.svg @@ -0,0 +1,70 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/LCU.WebApps.Operations.IncentiveManager/src/shared/assets/LMCU-White-Logo-Only.png b/LCU.WebApps.Operations.IncentiveManager/src/shared/assets/LMCU-White-Logo-Only.png new file mode 100644 index 0000000000000000000000000000000000000000..ee084f3f59959b3e646ea30ea642a16d627cb249 GIT binary patch literal 7637 zcmeHLdpJ~U-yU{y2vKqfnWRF-oH3K3F$Po4hbW0LvoOrTo*8C{a!BovijpWqHjxf0 zl0+#sQA#M14w9slk_vg(43)mGZ@=%puJ8RX&t+!T@40{X^E~(Ou-3J9IXl`a$SszG zKp+YfJ8M_)y9ol3SRgYO{B>#D+RYG%j)LbgRqjv-OU<@m{g{!W9>~-#q;rS$P>id%)3S8$Z^sCpKe38E``~2XoWl>j2 zsyw^k>~%MloeYnMUax&Ry1(7czQot5WJ^l+XsmF4p^9DQ3g)&oiy|jKKI)H835+XI9{XFo2RiZFv*CmSR?*>3s9HsX^Bz{wtAthiC;!~ybueSw&XoyE*ZDlQ z%Wzt{tHjB3PTasTJMyg(Tjy4X-D?$?%*( zLM=8_4bbM6$Mv77pW8<$7v^FOujQg7mk+u#S}W=KJCqjYF!wg^23~ARo>xIOZn241 zxOUM@-NYhZ=5ExUg!JCB2`pFmdV7IA!sN`<>4Y4IFO+$-W-Dmo)@QO|u@{6L*sB?R z0ed_kRy1DQfk)omAD@yYn_V5eOxlL-`R#51o;ZIlf^9gw(kJ0kijvgJZiwrg-b6pU zHPC@>r8Lk-MN#EBQzvCyI8iqm0{syuN3Sl9{c)Up!jM z%fMi;AvJ$TapDS(mf%5RQil-^Xu)}BAKZjJLl3^+{2Ko7M4@ce$xkka#wa$*lp4#qEgQsENAMx^Jg~4sdrhwyWX8`AG?E*InVh%$Gxs3SEBTXyQBk?MC@) zX3Bqb>&3+NHyYs!uBAPotIm7stiR0e2-B*NA}`ZMIT8PQ$l|X_)r#CN-VZ){_Fc6e zga0L!JD%PxC>s5=y5iRhb>wKK#`-Euj4ik<8Po$U z$>!7Sz!FGckVwuH66tH!gSmb6mEj`eZ8ADoK5Q%J|#Y51+o) zhGGouu~jP|9%qx?%~u5QO8jrFl9bWiPEDpX-(C3Py1aQqCm|}TI%&jcn71t@H`P?* z(ZXHNGuU3&G_Rqv(r-rOm^H6WwTf~yXSYi2(y27Any9ZFI9-dbvsr<4(psUKq-`WX ztkN)ii}~H!1>2-nX1wZfg;~jg#UVXuHHf{P>+ftT#BDcA-@9gkF8!{vjg)HMx!_|N zcDJ`S*I5pHOzTT+^q9M>L9*C+DQ@c<%a1*!8!|HaX_Cm?@$2Q5HZj>gxf?284b!^W z*^n=RkJg5amnissNP8dYounmkl=bMz#nQ$twa*k0Z;q|o(Py_c)9NVXQqFDDXE9&A zb2%-qO3&|UlZKLfV7(COowY&x$~p-Td%Ev;*92~yoP->Y=2p{Rwi!SmN~sL64S6}( z<7ph0F_q5o2aJWRP_RuwAm*0BP%141;6wd^Kn9zjJ6u(x3uVv=x}I1EghMC^2x8bp zasjtUM|WCe2n|QqwX~2k7vezz7Qm-Mg)AnUhZhob#k_d%x5y0Fg^D5k5Q46kgEN%G z;Q~;MF~%4HBMTYfC|wIVs5zG&fOoaFnV|r$2)aRhekdLe7YGE#0<L&!C4wv2pVS= z5M7Jud3-vY0$yOi^MC+23V|?#Ay6U+m8Acq5N{<7JN^7yX6cTN5S3K0GSK`1kfH+$1zJV0a; zADl@KfP<$JZ+-TnwiE~FZ$6?p1Tt8mVh<5|Hj+;Jh6@ejGQ}7=4Gu5?7I+dK$c+2} z5Bh&|_~~K92VbOs%7hC-1LB%-w`+QtTnB%4{`Ow7!zFlIKU zC>w+o+Ip5dh0Wtr*)-s*J4577r_xQS0T`Gm7NEi~rWk*izln(n3}c4GVE`-@i^Jk( zQ8;lKU_DTovv(2}2J%>;$s}uQBpG31Wo->^#EOVT6KzaMrYNk9wW+C@m_wu@p6E;= z=%S1f-!0BeDnEe3Wr5YjVADAQ-uEeY1`BZGQ$-pfv8E;{EDCFaLtznEH2OPeJ;3FG zSuSElB8-t3@eG}Yw*fh+V74(>)Ib0p$_^AyfWqNPT!6~waNRi^CP7!UE2zj-Tsu(n znX16sb7)ji^@za$T~t^zrDa78go`iD;eQAIUrcU690B`37DQzS0$^L6DT%Lk#!oU81q2|eD1R!9YJvk`7%ERfYPxPfwZJBZ|EVhf&VMFRMI8=L5fy@4SZFAd z0dRke-`9NpKXB&X?K8FW@7!m@roBm=&sr{8JfKz_KA*#aS~0@_sOxm> znT#1KaVC7_GKbGzXc{@|_6xd9+rZm8_>=(u_LwluC2A}G&A*vq{BJG+h5lOPAL;u` zu3vKfBL)5u_*ZuQlItHS@Q=X1vg`j%F1hb78UQx<7%Tu^BX}&nxeI&+G1q^ctu#hU02zDMn8 zanbsuG51Yjp-QcttUB3`H78)17yoU$^RucUnW5c&owhogPUw1R#WpA#X~w>}epy1} z-2C@NN{e@V8W^4$_9P!DR+68n%GugmQB`<+@S@{<$J9$97F!R}?@wrc2s_ee(O8qU z&39$xi_hc|#rP59MxE5da#Dotfd~3%?}l$n1Bd#I8l@lm)wWK+8a>=1Y!>YYQZ2C( z8@kpf*G4bQ(CL=3*CgVcM&e@-PiiM{jZP$~XL^kQI==Q^Y3e2|{6~07k#3EQ>_TqW z(AnqF9{X>QeC2JPH2kUkQcwSeo@I${^EHpQ#GVQbka*j51K<8<_{NH=?Z8q!l_8(A z9C)`(Q^8)uyVIMNXzte;2`$j>A3AZrbAG1F&CJKzYu{eh7!fEoQI_1qRdqR1UZ+K+ zDX1-1^X^};^eE=W^Zo0to{*)($2y^N8?<3^dhQC^Cmp(perM|SsiVO zd6~64ibpQw9iAu4JZZQme&kA5^3|!-i5^t))t!ZZT0#l0=^5{TsanqZVX;E(NVb%8BK*F4a!0OG{JcPo=7gjf< z;E@%7)t!ye@a?`XoCsGI$kwfMEWTLSaG&Q)KEtx{r^>Ez7L6ueMO|}TuF7!B5{bOP zn~}Tet1rEY=(ZzE5Mwt80oe><$;Nqd$&ZF|Izog3*kn#+DP?qk_VF10CHBy=XROrD z4*&d{W7I`J)arVk=44B=;W})2@lyS^`HfmvxuykvGEHVAZk6pgqTsZ-Vttls&2VJ( z;Cnu@Bl*s-@@Pwn2fcF{n-Ckn#(VCh&ygid1SRKhjFsA|e`%B^IrlG2J$lvkfRa4% zkf+^enH$V*gSv_LJ3o--wijQ|sVX_^7S>C;kcirIG`csReZJ0dbA`%ap5cz%=92be z@2<)!xq9aC5~cm&t0+0P?IbI=unAuCn9Kmasc-JsQR#QOV`_EgRp;E&v}(_920RLu zhCBLz&W3BoFSngq;-W5x%_0$35(6@9I4kv z`lFCjzB(F1@)P4h)dSa@o&GQ~#%zRN!IzU4s?24*D38|COdoA1t*lQ*J;MP-8}gED zi!xn4UP8O0Kl^maw=nw`6Ny31`sdU$^PVq8udU!U-3?IT?e3Td%gmK;EL|eCy*`;c zu_KD6(QnzA;d1fSjdd9=i5Vg8`?q%OrKQyKCer4yrW}uWs-xGMvTfBbyMl}cxv5@N z@RDmm{g&vpRfO>9N1dJ0UvxqqwsjThzw3(V*ZPy_jUm1m9w_8GU8oN0nCGtI;ed5> z0dwgkx#!Ll(OGE)@yu`Y_GuLO9a!${qHYpuxINdt?0j+gP+yqSV`aV4gSfsJ=%UdK zH{zik$QsYhUR|4=EA9)*&>hR}-DY_%l9~eyvSG0uNe8?0FV+p0j=GB2!2uyk5Dl_Q z-o;7AJqg+ksvnbk`nQVRGIBCiDN?Z_l6eH3WAso0|5tsXP}Fcc;{#=W?FmlCwfK6&Jj+WM#QqcgI)k zS3HY*R8hR1#sd|O^xJbcNaLKbNNw}{yT4ruI4m4D&vdKI?xg#Vrr9Mtl zw1a-l?|yTDRe=d2q1ZheZ>&S2{GzYkr*?ep`kLTsznvW=#V;c@3T@IJ zPYGL%Et7Ce^;4bbrt{}xTH5J^e7#27mlB$j=!grCGW1qbEEv`2Hb;R?Dl3d!iM+eo z?(~^g7b%x7p{8!9Ncw&}ylb$rbaPIoNpnhL#^`OshZvS;_WJ#LpB|dToI2~wx)5<; zPjOxzqCatjjJ@yflG=AQJU7%A@O#^QY#UIjf7iq0u*Avih~ADe#+CGqaJk^>*IDT= zw;oI^`w}-+E}amO6ZkNEB>09}X=dXTe4$popKovA4f0s<&WdK=6}XC&=6g;NkM%l? zhY!^RT~=1nNcD-=HB!+|jgAAqG*hGF%M_EkH)Yg4y4;yCI#~O>_sEJ*xE`OtSmP7$ zEhj!qUM1Csw9uMXgK^W_;kR+QKTurnW)uQbe*5 zRIR~%pGm-e*3Go2gMH2A{<*3zY27qp`{TMHr_9q)o$^zy2RxQ!r*qpqJl#e=dg?f4 z?dioV-MFpR;?>>HPc0qRtdG7F`s`Nz8p}F!8M(a^I^A~CG|A(<7Vy~uLLoa^pS1Fe F`xoqU)jt3L literal 0 HcmV?d00001 diff --git a/LCU.WebApps.Operations.IncentiveManager/src/shared/assets/light-house-corner-watermark.png b/LCU.WebApps.Operations.IncentiveManager/src/shared/assets/light-house-corner-watermark.png new file mode 100644 index 0000000000000000000000000000000000000000..0d7ace6c67a401069ad980c309c9d03cb66a6905 GIT binary patch literal 8305 zcmZX4c|6q7_xH>&80*+~F)E4~g9zD4%38wM$4=Q5vNiU7sbpWGkR>Lmk##U6CR=5R zEF%$OM9BJle4p>@_q?9xnLlREx%Zy;d7pF7`P|p*b0^8v_!v~(auJzdlgmI_8_BR?Hi4^REj09Uh6fVb;CN6`>( zFP}h_5H-Yqa8&^LbXW!<`ky8D?y4cQP92J3j7&v!d;?rX6;O&uXW2_=(aXvxSw-|E z1zBlPIkfC$8MJ~7S`mqsRY5DM$jOTS_k&Pp6jcpyaZ|aatM^}LfTo74Ly$g!;-?V*8$;JM&^f@<@1CcxkLW3; zqm%Fbduj-T%&8NZ|9Sz!llh;#YsiR8(fkfu7>uTK&Dg0gZ%V%0Y zJI=(%+N-ADysmDgN^AGo$i`WQ;ekC@X1>U0ouI9-=9(#+$!uwxY`LWmi&cp)a<9uh z`6#GW%%oVN6- zu78myWPMLvw>N}okB{h_n;7SjMqql4yXE1;rV4G<}{QxV%M?T) z#WYGip)_n^F&Tq0C<$wAnS6@aoHNJ-KSR@sQ}|0g=%-K7+aBu;iS$>7QiX9Pj!tPu z-(lp7C$w{{#JevqN(c;BLhV6&oQ5y1ach)-_e6+hASM7D7QvWXlS!~TYWiq*LE)ys zEKgJapKN6amBG4POkg+%H~C;pH*1a4!Wi>qv=Z7*<2O6EyPlE|(A6DPSq!NNne*lJOEX2>8YjYTg(?H+zrWm3;OQ=PV#T-}WRGr6-X$t9 zRt=c}cglNC2^&X0--dl!C#5Z$5v6E%v5cIO^}P=^F`eo^zSrc)WYHVvC{Ju!9Qh%xS+`pB8? z!3~b{6H*iZ_HTu^eP4Kgd9)lSfwvi!&;4pgjDC9=G}96KuWW_p%ikm8Bn0ecl!nJNcp>k3BTlY-@n7OM6>XNP%UrsBRhbCd$z~XB;%+ zbW%q0+?=)z`r?U_b>{OMA-qV@WEvn7VbwYQ-p=N#mot>Nb_nRGKdQFfBw)(}>hFoo z@sqWWVL`(dAa!ExB6stajXSy|1QK{n-RI=*O}|vr%`)t-&vHd?R~6l)l|~ZUwEUpU z0(hmxcg6k=V2$Xc-Ac+V0X=ZQbDmN`@eaT(5Ys=2o!K0&7eUm9`inB#f#_^28JuSB zE01wp<>o5&jq@RTZ>m|(#_YXm!Pns4Gi)tUUgPXaH^7I2zr{Z(TI}=vH1>N(qMy(b z(T;bzwSQUg(8Q5fW}Id89G3dkqOoRK>U*>=dSvw53~|tTwC)=UQrvYB=>9Rk^tp}5 z=m@IfZFBGZ!%x8@9kYl{n(y%adM@N=tH!#uD-#6EeJR7jSvbj>wV72Q{Qgxza9{=h zK(YqcY#9BU<`&w!yLg&^bYc@&lI>DU&twuexoBA}ae_UveNU<;drj)u*Ya~dOh=<( zw1m03y<-Jw_0q3Kz2`g(1cB|a*yT4QN##r_(bS27Y(wzypik<`El2 z!hZ+r&)&fgch99jY4;lo1qlO1NvKY)^A^_QLnD^WHUpHqV|A8h;F8YR1Fj%yN0;=v z&DtXtN$c|uYMOyoJ7m1l*J@XcZBwgPeVkk}f1wNZFykKN3Z1>b8!n1Buz~zxoiluM z=Zp+pA`N=KDQ1;g!-?jkbP#D(qlBN0qX(vbfiENrKb#%}o`RE|TsE$Zh+;qG56xFb zg0Z|d20v|hG4p2vU-k7$;ewe^n{LS!bUOsrU3~ubehYTzUuLzPe+VrS_ zZ(yy>FK!T3sTu71YF1WZG+XjvE!=IP|0t|PyGzSI#HFV;VWqaALPrG~Vb-nVD4Hy* zU!BZgJK=Z2B4W22swaM;{LC3+WPV~h#In4sx`dx%Y9yI*DX#cly6p@G*LkkO$g%JI zrEgq9c_NCQO`YdxrnTMT=4GSkX8l`cJc~yBbB3j1Uk_TQ5sULX{^-hF_)4pla z5a)t{IH?=@8kT)e9zt8p)*wA$bOT<62+TJ-6#^wC&9g>HLXh5$tHDD{dQJkpsU8q2 zbAh&pe{igUe4`pwY=wp17-*|mN-EXO-Z>zU-_CUE z?sJQM=RSB-CZ3z`Bqbsx@67JSZwzm~*glq&n-_h7+|%3@s%{Z6Kp(&Qd=QJ${rbD% zbHKvCo^uHfhH#(K@S-V%_fHlx3v)=t{j?SWpI;B#j&LH4HgEJI*+?qZYGsWQD-9nT zkbMa@ByRu}qR}4uFIo`MP;EYAhO$5B$B^6aR1xKgN`5-__wF{FmHGpHu`c>+xf1Vt zwpMI=cc{xyiW{1m zC?2vFcsAcCe~rCmY}8|B)0Di!PhP|z&hF1Tsx6Y-eA8bvUww)BjiB1ZqLl41H~i1_ z3Ax?G{E?F~w!7*2zS=gLUt64cK2}Ga4a+;@fK?N8fQM!U7scvpSe!Lp+p_Ph zHCC-*Mza@nO52ur^(WqT)u%LCsTaB`ABSM1vlW!dASyrQ+Da=;1Jrg!AOe#Yr5_x3)ce_R=N`NiCy< zX?B#YcO|&$e5xB#h$&xJ;f(eS<7B)_o=tIA^r{hEXS@b0zepAa+R7YRWxoP?Ht;| zZx#oKy`8u05S5lI_a&>Hr=)THEAHr8KilfOCcgZ~e}2_(DCG>k{Waq&IeOm@>)E3b z^e^rRO0A3U(ESdcUVB{KIXH{$YLz;gq0jZ>Vc-ePc~ri+!zjTLnbA2TKCzM(B88g> zw&>r`^vW+GmX7(11mAq{FUHB}wz=rX#~Vwm_SrrxyH*SMZX?bs%K6z>yyUKQ{qt<> zzt>4g!~D2&kN9p;$61Xyt+q|m6_4EYP+ZQ}sJ#D^ZW#yL;~cw&?{AxU;?3#R8{(H2 z@edS8lPi|A+m|@b;?vN7s7`<`1eSE!vJCn>?;yi+1U`Oy>YEv-y5GONiQhQ?g>=YnwSwC zRF>y6>|9()GkP;ji9No+I`!zQ+c|ynxKuQue|n9jD}FYDhod_5X@;Xko)1LuWz(vA zD_h_BGx;_v#iF+bEb)y(VFq?r)VOm1bs1m~TUUyoP8DCHYlN9qO ztW?pjEp;vYVByDUmz&-XVr~rjE_{FN!1jRt1|<9OWxKx-iVeXmJ(*nrFO|Br>#%in zcBx9@olF_QecmNv?NUWu@2nEH$kR%NvRBuFTz&JFUY=1b+CoY$?zY7ytqQmmNrZSj zUC_Q!;7@;8AdivmM!+04n4;qb^Hsp|iC0{BoXA;B+^ydqYD+%GpZ#Q)>ru3teEzV1 zt&gLWqmP)O1Ap%?ZlnFCv|2EYR8^aWoE*kx_}ju#k%7t^wlnYmvjz5Db>(;~H=ZOP zEHBwImeFO+>8x6Lpbb1Y0e^W)Odeh)(hMqrlR_CeDeT&zX*Hht(~}-e6Ds{o^k{bZ zH=TE|k4aJ5{6DQee0!SB(JI*c`^C1q)zN}m7P%cQS!-k2xAuuWdhdO3kaEtTD4O5W zxj^RY-#b5y>cxT*qWCO74XY_d!>F>_pe9e5HNN5uXl*N7Y(wOlQ@FOrSa4g4PoUVo zfOqpJHs0UZ3lVgj{FbSOh!?BIjvrconF{U?hS$l*3g7f)7s$l0tSKy!tJci5e>;B# zCAgoY`(@^~EQaN0AAcVQb=fA3uYiNra?_=Phej3?V~9p1tvP2}LcBrcved3hG}cTk z#=J=9KsgxyHuwyjWN;?^yKU;bJUYq^N0BU}v6l7NRA{>1vnI+y|J=sS%@f6z*9|U1 z;R=F(C!cg0t1upllGM>mJfA60hckcTz|Sz+9Tuf39ZfG1J-M5FKQMMe&=b#?NV6qZ z9RlkRK}OT*H~0$e#4yh968_|lW_lq|5g%3|9i6r;jJax`{Ht0xDfyw(+kZsHZcK*l zzi^s7Qoz3(5q1n@f({JUH)+MYnr*`C7?OrIAwNY(7Ei_Btz_R4+n)sXX@fNg<>MIn z&sPbD%d0(xw3MIBET#xC>aQ~ed+`Ng;I!=0W8f%mOTU*?6;@5o;0vRU#g$>mTgYT`iX3gaFpdC6`{QzsF! zj~~YenYvw~+>bIG@@A}_H2m^Q*q^n+y_Gv2+#wm^^qFxeS z&dLSd+TZ=wa2UT~*O*YJ*BT=w&+gtc{(+&6ULIb=qta~qirAAk1nVwD<8v1%mg&)mNm^SN7kJ?uukr?U^37 z9=i4S&;-3vc<_0#r^x8U2i(#%1ZNZx>a4zDD=@tAdph?EEEffqav;}4s*(yOHdGZL$?Zc?lspN zUJ=dsYFGA4!fe*SF)M<~SQ<`t%sb-V4*-Fo|W zkI@|VjNAF*we#QNqX|)_?ok6g$xc-qvPi!NDK+Q252_<8tD2O8*W{!B9yiXbJ%;Tt zaa=`wf$cGlogs66N`Ga0?^*M50{v&Y4 z8W|~dXUFln3enw=#ws>#d%phG=$1X7t}Z)m5Pd`>Fo@Fx{r2L7nHd+o7L%~-2#Ci& z<3e`^R2_H7;_M!fRLs2-A{3j1_TRdpQ^-H>%cA=sB2M`3JkA2T2Pvt5YlaS!-O_rd z_yw)6M0K%QP0SVc;C6OxkBbHh9J?{m({?g`oo-YS_8wJh#~6K zTYFD^x#Q~;itwqHUKCG}&O@X= zS!qcGZ$BI{bks3!Yzh=LB|1;Ox;AxznR!Dy-g4b)eN+?h^n)g2F)$s{43Chh)%I=$AHfy}1Fer;e(bkMoAdgmh_+${Ix3J(*rert?VGQRX3z4J;Z zoE~S8+)H9y=#OB_m#xTZ!Uv|7MEHf!UlOSs;L6t9I$kTIS*8h_7kjp+@PVt9^|7p* z7^wU+T@2yO^8I!gW-hzK%L z=(;c_2*R|$g8Lb+nIodT-;HwGLS-)M1XjTqGFbS!Vg%=J-xT9q3$cJ-hWB2aWUxe0~7mY<^hsa-rjD2gW-Q@9OEXxBcMKzB$ z0NySTuRmQ$>Vx}&E9b)Mo{6GtTA_u}OkjQDBn3Igg@so-KN6dfKtgLS)A%t${jE+% z0m8B~kZd;Toh|?&(G7}Cx-^sG0bCxK;id*xFfNI^*9|)3a%n;!Mt6re0Bn%NHV`Zv zHj&9zc!9%D6EXO#}s8?SS9(gQ9(=iIM{N2?U7A z=|JqSK?a#&dtw}ZdiumwkEu!Wa1w`=I9S7lPB_PFILSJ}#;2-Of)g;XZod!VtPiRi zI>qe;Zgv{9C04|$Nv^T9(hYi$I=#CKa-9cf%fFH3WAt9RMFCOA9>G-<`JX-6hPxrS z$gVhGh?^k-`yQUOfSkLXryVBY9<>X*@wMv{_MGcm^YcL2n(3i*>GX*OH{m&84nGA> zkIy;MnYbE2h0RDvezmzI<*Au)tnYu_}mXbvI2!L zBi9W@0KP(JFC-3t0D;WGNO^n>GLueQsKh}HYLpZAZ#6OQ=SkNw{=p=X3J~Cl@2zz^ zrRv7oLcG;(ka}v5Vtx9A^VI9}wkMo``5^nHxwD=?R$|ZrAsNqscBv9SqvU^fF7)hN z>?0gOJ^>mKAkJ|WGQ;`?j<|Vt?ze}xEk&O=HR6>>0_LAf>rz6PlZMx-K+^L<(7as-m?Xw~w^s7BX7hENr*{#mP!ogmID10UqKem;h)B4b6jTq+S967YK(+yd8+s2YPmi zNXtX2DwhN73uBmM;R!J5`3Kd=QK*O4k8_=`}BhpS%U$x!h3K3E)*Z^@lm1 zyHE)54pn?h^T6(2%r%G)-8I7`QYLPy^q#eoKJimt=fwaOH`siHTtI^xGo>{nFLC&z zN5a^LbMds(tmZ#BfKpaY(xMoh5+Mn_`;Hs+gaH{UJp2g7nduX+&m*y7JK7MRK}I6S zpO_~=OLD~gCl(TfDDL5T^VTU7Q6E-Ld~{PRl?M+G69W@;)WZPhh`VR!g8aM-^=XSz zgGdG`x)6Qh@43VaugLneuqI)%<=k^|yC3_I)s_aXrvVYB(AQp}T4xi{K=k5Vem=aj zqCgVU3C^!l41ja>GyznfK8OVHs1M}oVqVcVpaWR`iVUrGPRq&eiA z&`o}2vC~!u(lhcHnUbIn6zftVu(v>TLNQHd6O=)r4Urfi{$SAhqnymi;lPz6z1Z@Y z(D%Z{4z?B%S3dOSo?B~Di?a)Hh?q4FkapCo;{6Fao*7Ru&sZe@B+UXST zq|9ibEcjs { + const hostname = window.location.hostname; + if (hostname.match("localhost")) { + return localEnvironment; + } + if (hostname.match("qa")) { + return devEnvironment; + } + if (hostname.match("uat")) { + return uatEnvironment; + } + return prodEnvironment; +}; + +const config = getConfigurationFromWindowLocation(); + +/** + * Static class for access application specific configuration data. + */ +export default class Configuration { + static get Get() { + return config; + } + + static PrefixAppUrlWithBasename(appUrl: string) { + return "/" + config.appUrlBaseName + appUrl; + } + + static PrefixIncentiveUrl(relativeUrl: string, append = "") { + return config.baseUrls.incentiveApi + relativeUrl + append; + } +} diff --git a/LCU.WebApps.Operations.IncentiveManager/src/shared/dao/ProfileDao.ts b/LCU.WebApps.Operations.IncentiveManager/src/shared/dao/ProfileDao.ts new file mode 100644 index 0000000..a927c53 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/shared/dao/ProfileDao.ts @@ -0,0 +1,133 @@ +import IAsyncStateContainer from "../types/IAsyncStateContainer"; +import Configuration from "./../configuration/Configuration"; +import Axios from "axios"; +import AsyncState from "./../enumerations/AsyncState"; +import { + IEmployee, + IEmployeeDetailsUpdate, + IEmployeeDetailsUpdateResult, + IEmployeeSummary +} from "../models/Employee"; + +/** + * Service for retrieving Employee profile + */ +export default class ProfileDao { + static getProfile = async ( + employeeId?: string + ): Promise> => { + try { + let url = Configuration.PrefixIncentiveUrl( + Configuration.Get.urls.incentiveApi.profile + ); + + if (employeeId) { + url = url.replace("me", employeeId); + } + + const res = await Axios.get(url, { withCredentials: true }); + + return { + state: AsyncState.Success, + data: res.data + }; + } catch (err) { + return { + state: AsyncState.Failure + }; + } + }; + + static submitEmployeeDetailsUpdate = async ( + employeeDetailsUpdate: IEmployeeDetailsUpdate + ): Promise> => { + try { + let url = Configuration.PrefixIncentiveUrl( + Configuration.Get.urls.incentiveApi.profileUpdate + ); + + const res = await Axios.post(url, employeeDetailsUpdate, { + withCredentials: true + }); + + return { + state: AsyncState.Success, + data: res.data + }; + } catch (err) { + if ( + err.response?.status === 400 || + err.response?.status === 401 || + err.response?.status === 403 || + err.response?.status === 404 + ) { + return { + state: AsyncState.Failure, + data: err.response.data + }; + } + return { + state: AsyncState.Failure, + data: err?.response.data ?? err?.message ?? "" + }; + } + }; + + static profileSearch = async ( + searchQuery: string, + role?: string + ): Promise> => { + try { + let url = Configuration.PrefixIncentiveUrl( + Configuration.Get.urls.incentiveApi.profile + ); + + url = url.replace("me", ""); + + const res = await Axios.get(url, { + withCredentials: true, + params: { + query: searchQuery, + role: role + } + }); + + return { + state: AsyncState.Success, + data: res.data + }; + } catch (err) { + return { + state: AsyncState.Failure + }; + } + }; + + static GetEndOfEmploymentList = async ( + year: number, + month: number + ): Promise> => { + try { + let url = Configuration.PrefixIncentiveUrl( + Configuration.Get.urls.incentiveApi.eoeList + ); + + const res = await Axios.get(url, { + params: { + year: year, + month: month + }, + withCredentials: true + }); + + return { + state: AsyncState.Success, + data: res.data + }; + } catch (err) { + return { + state: AsyncState.Failure + }; + } + }; +} diff --git a/LCU.WebApps.Operations.IncentiveManager/src/shared/enumerations/AsyncState.ts b/LCU.WebApps.Operations.IncentiveManager/src/shared/enumerations/AsyncState.ts new file mode 100644 index 0000000..aec1840 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/shared/enumerations/AsyncState.ts @@ -0,0 +1,10 @@ +enum AsyncState { + Inactive = "Inactive", + Loading = "Loading", + Success = "Success", + Failure = "Error", + NotFound = "NotFound", + Invalid = "Invalid" +} + +export default AsyncState; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/shared/enumerations/PermissionCode.ts b/LCU.WebApps.Operations.IncentiveManager/src/shared/enumerations/PermissionCode.ts new file mode 100644 index 0000000..12f6697 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/shared/enumerations/PermissionCode.ts @@ -0,0 +1,53 @@ +// use these instead of strings for type safety +enum PermissionCode { + ASSIGN_ASSOCIATES_TO_DEPARTMENT = "ASSIGN_ASSOCIATES_TO_DEPARTMENT", + ASSIGN_PERMISSIONS_TO_ASSOCIATES = "ASSIGN_PERMISSIONS_TO_ASSOCIATES", + ASSIGN_REGIONAL_MANAGER = "ASSIGN_REGIONAL_MANAGER", + CREATE_ACTIVITY = "CREATE_ACTIVITY", + ELIGIBLE_FOR_REFERRALS = "ELIGIBLE_FOR_REFERRALS", + GENERATE_PAYOUT_FILES = "GENERATE_PAYOUT_FILES", + HAVE_SALES_GOALS = "HAVE_SALES_GOALS", + MANAGE_COMMISSIONS = "MANAGE_COMMISSIONS", + MANAGE_DEPARTMENTS = "MANAGE_DEPARTMENTS", + MANAGE_DEPARTMENT_GOALS = "MANAGE_DEPARTMENT_GOALS", + MANAGE_EMPLOYEE_DETAILS = "MANAGE_EMPLOYEE_DETAILS", + MANAGE_PERSONNEL_GOALS = "MANAGE_PERSONNEL_GOALS", + MANAGE_ROLE_MAPPINGS = "MANAGE_ROLE_MAPPINGS", + SET_ACTIVITY_ACCEPTED_DATE = "SET_ACTIVITY_ACCEPTED_DATE", + SET_ACTIVITY_TO_ACCEPTED_FROM_MANUAL_REVIEW = "SET_ACTIVITY_TO_ACCEPTED_FROM_MANUAL_REVIEW", + SET_ACTIVITY_TO_ACCEPTED_FROM_PENDING_REFERRAL = "SET_ACTIVITY_TO_ACCEPTED_FROM_PENDING_REFERRAL", + SET_ACTIVITY_TO_ACCEPTED_FROM_PENDING_TRANSFER = "SET_ACTIVITY_TO_ACCEPTED_FROM_PENDING_TRANSFER", + SET_ACTIVITY_TO_ACCEPTED_FROM_REJECTED = "SET_ACTIVITY_TO_ACCEPTED_FROM_REJECTED", + SET_ACTIVITY_TO_MANUAL_REVIEW_FROM_ACCEPTED = "SET_ACTIVITY_TO_MANUAL_REVIEW_FROM_ACCEPTED", + SET_ACTIVITY_TO_MANUAL_REVIEW_FROM_REJECTED = "SET_ACTIVITY_TO_MANUAL_REVIEW_FROM_REJECTED", + SET_ACTIVITY_TO_PENDING_REFERRAL_FROM_ACCEPTED = "SET_ACTIVITY_TO_PENDING_REFERRAL_FROM_ACCEPTED", + SET_ACTIVITY_TO_PENDING_REFERRAL_FROM_REJECTED = "SET_ACTIVITY_TO_PENDING_REFERRAL_FROM_REJECTED", + SET_ACTIVITY_TO_PENDING_TRANSFER_FROM_ACCEPTED = "SET_ACTIVITY_TO_PENDING_TRANSFER_FROM_ACCEPTED", + SET_ACTIVITY_TO_REJECTED_FROM_ACCEPTED = "SET_ACTIVITY_TO_REJECTED_FROM_ACCEPTED", + SET_ACTIVITY_TO_REJECTED_FROM_MANUAL_REVIEW = "SET_ACTIVITY_TO_REJECTED_FROM_MANUAL_REVIEW", + SET_ACTIVITY_TO_TRANSFERRED_FROM_PENDING_TRANSFER = "SET_ACTIVITY_TO_TRANSFERRED_FROM_PENDING_TRANSFER", + TRANSFER_ACTIVITIES_BETWEEN_ASSOCIATES = "TRANSFER_ACTIVITIES_BETWEEN_ASSOCIATES", + TRANSFER_ACTIVITIES_BETWEEN_DEPARTMENTS = "TRANSFER_ACTIVITIES_BETWEEN_DEPARTMENTS", + UNKNOWN = "UNKNOWN", + VIEW_ACTIVITY_AUDIT = "VIEW_ACTIVITY_AUDIT", + VIEW_ACTIVITY_REPORT = "VIEW_ACTIVITY_REPORT", + VIEW_AUDIT_REPORT = "VIEW_AUDIT_REPORT", + VIEW_DASHBOARD = "VIEW_DASHBOARD", + VIEW_DEAD_ACTIVITY_QUEUE = "VIEW_DEAD_ACTIVITY_QUEUE", + VIEW_GOAL_PROGRESS_REPORT = "VIEW_GOAL_PROGRESS_REPORT", + VIEW_PAYOUT_REPORT = "VIEW_PAYOUT_REPORT", + VIEW_PERFORMANCE_FOR_BUSINESS_LINE = "VIEW_PERFORMANCE_FOR_BUSINESS_LINE", + VIEW_PERFORMANCE_FOR_DEPARTMENT = "VIEW_PERFORMANCE_FOR_DEPARTMENT", + VIEW_PERFORMANCE_FOR_REGION = "VIEW_PERFORMANCE_FOR_REGION", + VIEW_REPORTS = "VIEW_REPORTS" +} + +export const RankedPermissionCodes: PermissionCode[] = [ + PermissionCode.VIEW_PERFORMANCE_FOR_BUSINESS_LINE, + PermissionCode.VIEW_PERFORMANCE_FOR_REGION, + PermissionCode.VIEW_PERFORMANCE_FOR_DEPARTMENT, + PermissionCode.VIEW_DASHBOARD, + PermissionCode.UNKNOWN +]; + +export default PermissionCode; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/shared/enumerations/Product.ts b/LCU.WebApps.Operations.IncentiveManager/src/shared/enumerations/Product.ts new file mode 100644 index 0000000..168fa8f --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/shared/enumerations/Product.ts @@ -0,0 +1,202 @@ +//TODO ALL ENUMS MUST Come from a configuration endpoint in the future. Eventually, mark as deprecated + +export enum ProductValue { + Numeric, + Monetary +} + +export interface IProductDetail { + product: string; // the code comes in as product + title: string; + valueType: string; + minValue: number; + maxValue: number; + hasGoals: boolean; + hasCommissions: boolean; + isSellable: boolean; + isReferrable: boolean; + isNonPaying: boolean; +} + +export class Product2 { + private constructor( + code: string, + title: string, + valueType: ProductValue, + minValue: number, + maxValue: number, + hasDepartmentGoals: boolean, + hasPersonnelGoals: boolean + ) { + this.code = code; + this.title = title; + this.valueType = valueType; + this.minValue = minValue; + this.maxValue = maxValue; + this.hasDepartmentGoals = hasDepartmentGoals; + this.hasPersonnelGoals = hasPersonnelGoals; + } + + public code: string; + public title: string; + public valueType: ProductValue; + public minValue: number; + public maxValue: number; + public hasDepartmentGoals: boolean; + public hasPersonnelGoals: boolean; + + public static ATMCard = new Product2( + "ATMCard", + "ATM Card", + ProductValue.Numeric, + 0, + 999, + false, + false + ); + public static CreditCardBalanceTransfer = new Product2( + "CreditCardBalanceTransfer", + "CCBT", + ProductValue.Numeric, + 0, + 999, + false, + false + ); + public static CheckCard = new Product2( + "CheckCard", + "Check Card", + ProductValue.Numeric, + 0, + 999, + false, + false + ); + public static NewChecking = new Product2( + "NewChecking", + "New Checking", + ProductValue.Numeric, + 0, + 999, + true, + true + ); + public static CreditCard = new Product2( + "CreditCard", + "Credit Card", + ProductValue.Numeric, + 0, + 999, + true, + true + ); + public static DebtProtection = new Product2( + "DebtProtection", + "Debt Protection", + ProductValue.Numeric, + 1, + 99, + false, + false + ); + public static DebtProtectionEligibleLoan = new Product2( + "DebtProtectionEligibleLoan", + "DP Eligible Loan", + ProductValue.Numeric, + 1, + 99, + false, + false + ); + public static DirectDeposit = new Product2( + "DirectDeposit", + "Direct Deposit", + ProductValue.Numeric, + 0, + 999, + false, + false + ); + public static eStatement = new Product2( + "eStatement", + "eStatement", + ProductValue.Numeric, + 0, + 999, + false, + false + ); + public static FirstMortgageReferral = new Product2( + "FirstMortgageReferral", + "First Mortgage Referral", + ProductValue.Numeric, + 0, + 999, + false, + false + ); + public static GAPInsurance = new Product2( + "GAPInsurance", + "Gap (Sales/Eligible)", + ProductValue.Numeric, + 0, + 999, + true, + true + ); + public static LIC = new Product2( + "LIC", + "LIC", + ProductValue.Numeric, + 0, + 999, + true, + true + ); + public static Mortgage = new Product2( + "Mortgage", + "Mortgage", + ProductValue.Monetary, + 0, + 99999999, + true, + true + ); + public static MechanicalBreakdownProtection = new Product2( + "MechanicalBreakdownProtection", + "MBP", + ProductValue.Numeric, + 0, + 999, + false, + false + ); + public static Vehicle = new Product2( + "Vehicle", + "Vehicle", + ProductValue.Numeric, + 0, + 999, + true, + true + ); + public static Products = [ + Product2.ATMCard, + Product2.CreditCardBalanceTransfer, + Product2.CheckCard, + Product2.NewChecking, + Product2.CreditCard, + Product2.DebtProtection, + Product2.DebtProtectionEligibleLoan, + Product2.DirectDeposit, + Product2.eStatement, + Product2.FirstMortgageReferral, + Product2.GAPInsurance, + Product2.LIC, + Product2.Mortgage, + Product2.MechanicalBreakdownProtection, + Product2.Vehicle + ]; +} + +export default Product2; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/shared/models/Activity.ts b/LCU.WebApps.Operations.IncentiveManager/src/shared/models/Activity.ts new file mode 100644 index 0000000..182974d --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/shared/models/Activity.ts @@ -0,0 +1,328 @@ +import { Product2 } from "../enumerations/Product"; +import moment, { Moment } from "moment"; +import { IAuditable, IAuditCreate } from "./Auditable"; +import { IEmployee } from "./Employee"; +import { IMonthRange } from "../types/IMonthRange"; +import { IPayoutLineItem } from "./Payout"; +import { IDepartmentAssignment } from "./DepartmentAssignment"; +import { IBusinessLine, IRegion } from "./Settings"; +import { IGoal, IPerProductGoal } from "./Goal"; + +export interface IActivity { + id: number; + product: Product2; + type: ActivityType; + sourceSystem: SystemType; + sourceId?: string; + sourceCreated?: Moment; + detail: IActivityDetail; + metaData: IMetaDataLineItem[]; + departmentId: string; + employeeId: string; + employeeEmail: string; + accepted?: Moment; + state: ActivityState; + activityPayouts: IActivityPayoutLineItem[]; +} + +export interface IActivityDetail { + id?: number; + //personnbr for employee + dnaPersonNbr?: string; + accountNumber?: string; + subAccountNumber?: string; + subjectPersonNbr?: string; + subjectOrgNbr?: string; + memberName?: string; + stationName?: string; + branchOrgNbr?: string; + branchOrgName?: string; + accountStatus?: string; + agreementNbr?: string; + accountBalance?: number; + accountNewBalance?: number; + activityRef?: number; + debtProtectionAddDate?: Moment; + debtProtectionRemoveDate?: Moment; + isDebtProtectionEligible?: boolean; + gapInsuranceAddDate?: Moment; + gapInsuranceRemoveDate?: Moment; + isGapInsuranceEligible?: boolean; + Activity?: IAuditableActivity; +} + +export interface IMetaDataLineItem { + id?: number; + metaData: string; + metaDataType: string; + activityRef?: number; +} + +export interface IActivityPayoutLineItem { + payoutLineItem: IPayoutLineItem; +} + +export interface IActivityStateChange extends IAuditCreate { + id: number; + activityId: number; + state: string; + acceptedDate?: Moment; + note: string; + activityMatchId: number; +} + +export interface IActivityDetailUpdate extends IAuditCreate { + id: number; + activityDetailId: number; + note: string; +} + +export interface IActivityStateChangeDto { + id?: number; + activityId: number; + acceptedDate?: Moment; + state: string; + note: string; +} + +export interface PerProductActivityType { + [product: string]: number; +} + +export interface IPerProductActivity { + productActivity: PerProductActivityType; + productOther: PerProductActivityType; +} + +export interface IPerDepartmentActivityType { + [departmentId: string]: IPerProductActivity; +} + +export interface IPerDepartmentGoalType { + [departmentId: string]: IPerProductGoal; +} + +export interface IPerEmployeeActivityType { + [employeeId: string]: IPerProductActivity; +} + +export interface IPerEmployeeGoalType { + [employeeId: string]: IPerProductGoal; +} + +export interface IEmployeeProgress { + employee: IEmployee; + monthRange: IMonthRange; + departments: IDepartmentAssignment[]; + progress: IPerDepartmentActivityType; + goals: IPerDepartmentGoalType; + activityAudit: IAuditableActivity[]; + goalAudit: IGoal[]; +} + +export interface IDepartmentProgress { + departmentId: number; + monthRange: IMonthRange; + progress: IPerProductActivity; + goals: IPerProductGoal; + activityAudit: IAuditableActivity[]; + goalAudit: IGoal[]; + employeeProgress: IPerEmployeeActivityType; + employeeGoals: IPerEmployeeGoalType; + employeeList: string[]; +} + +export interface IRegionProgress { + region: IRegion; + monthRange: IMonthRange; + departments: IDepartmentProgress[]; +} + +export interface IBusinessLineProgress { + businessLine: IBusinessLine; + monthRange: IMonthRange; + departments: IDepartmentProgress[]; +} + +export interface IAuditableActivity extends IActivity, IAuditable {} + +export interface IAuthorizedActivityCommands { + allowedChanges: ActivityState[]; + canApproveTransferFrom: boolean; + canApproveTransferTo: boolean; +} + +export interface IViewableActivityDetail { + activity?: IAuditableActivity; + stateChanges?: IActivityStateChange[]; + detailUpdates?: IActivityDetailUpdate[]; + transferAction?: IActivityTransferAction; + authorized: IAuthorizedActivityCommands; +} + +export interface IActivityTransferRequestDto { + id?: number; + activityStateBeforeTransfer: ActivityState; + requestedActivityId: number; + newDepartmentId: number; + newEmployeeId: string; +} + +export interface IActivityTransferAction { + id: number; + activityStateBeforeTransfer: ActivityState; + requestedActivityId: number; + state: ActivityTransferState; + newActivityId?: number; + newActivityDepartmentId: number; + newActivityEmployeeId: string; + requestedDepartmentApprover?: string; + requestedDepartmentApproved?: Moment; + newDepartmentApprover?: string; + newDepartmentApproved?: Moment; +} + +export interface IActivityTransferApprovalRequestDto { + id: number; + approvalState: ActivityTransferState; +} + +/** + * Used to Post an new Manual Activity. + */ +export interface IActivityDTO { + product: string; + type: ActivityType; + sourceSystem: SystemType; + sourceId?: string; + sourceCreated?: Moment; + detail: IActivityDetail; + departmentId: string; + employeeId: string; + accepted?: Moment; + state?: ActivityState; +} + +export interface IManualActivityFormState { + product: string; + type: ActivityType; + sourceSystem: SystemType; + sourceId?: string; + sourceCreated: Moment; + departmentId: string; + employeeId?: string; + accepted?: Moment; + state: ActivityState; + detailAccountNumber?: string; + detailSubAccountNumber?: string; + detailSubjectPersonNbr?: string; + detailMemberName?: string; + detailAccountStatus?: string; + detailAccountBalance?: number; + detailIsDebtProtectionEligible?: boolean; + detailDebtProtectionAddDate?: Moment; + detailDebtProtectionRemoveDate?: Moment; + detailIsGapInsuranceEligible?: boolean; + detailGapInsuranceAddDate?: Moment; + detailGapInsuranceRemoveDate?: Moment; + detailSubjectOrgNbr?: string; + showValidation: boolean; + deadActivityId?: number; +} + +export const generateDefaultActivityForm = (): IManualActivityFormState => { + return { + product: "", + type: ActivityType.Sale, + sourceSystem: SystemType.Manual, + sourceId: "", + sourceCreated: moment(), + departmentId: "", + employeeId: "", + accepted: moment(), + state: ActivityState.Accepted, + detailAccountNumber: "", + detailSubAccountNumber: "", + detailSubjectPersonNbr: "", + detailMemberName: "", + detailAccountStatus: "", + detailAccountBalance: 0, + showValidation: false, + detailIsDebtProtectionEligible: undefined, + detailDebtProtectionAddDate: undefined, + detailDebtProtectionRemoveDate: undefined, + detailIsGapInsuranceEligible: undefined, + detailGapInsuranceAddDate: undefined, + detailGapInsuranceRemoveDate: undefined, + detailSubjectOrgNbr: "" + }; +}; + +export interface IProgressTotal { + goalTotal: IPerProductGoal; + activityTotal: IPerProductActivity; +} + +export interface IActivitySearchForm { + product: string; + type: string; + state: string[]; + dateLow: Moment | null; + dateHigh: Moment | null; + query: string; + departmentId?: number; + employeeId?: string; + sortBy?: string; + sortDescending?: boolean; +} + +//TODO ALL ENUMS MUST Come from a configuration endpoint in the future. Eventually, mark as deprecated + +export enum ActivityType { + Other = "Other", + Referral = "Referral", + Sale = "Sale" +} + +export enum SystemType { + Action = "Action", + ActivityManager = "ActivityManager", + Allied = "Allied", + ClientCentral = "ClientCentral", + Dna = "Dna", + Manual = "Manual", + Redtail = "Redtail" +} + +export enum ActivityState { + Accepted = "Accepted", + Created = "Created", + ManualReview = "ManualReview", + PendingReferral = "PendingReferral", + Rejected = "Rejected", + PendingTransfer = "PendingTransfer", + Transferred = "Transferred" +} + +export const ActivityStateTitles = { + Accepted: "Accepted", + Created: "Created", + ManualReview: "Manual Review", + PendingReferral: "Pending Referral", + PendingTransfer: "Pending Transfer", + Rejected: "Rejected", + Transferred: "Transferred" +}; + +export enum ManualActivityState { + Accepted = "Accepted", + ManualReview = "ManualReview", + PendingReferral = "PendingReferral", + Rejected = "Rejected" +} + +export enum ActivityTransferState { + Accepted = "Accepted", + Rejected = "Rejected", + Requested = "Requested" +} diff --git a/LCU.WebApps.Operations.IncentiveManager/src/shared/models/Auditable.ts b/LCU.WebApps.Operations.IncentiveManager/src/shared/models/Auditable.ts new file mode 100644 index 0000000..0a6ffcf --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/shared/models/Auditable.ts @@ -0,0 +1,19 @@ +import { Moment } from "moment"; + +export interface IAuditable extends IAuditCreate { + lastUpdatedBy: string; + lastUpdated: Moment; + lastUpdatedByType: UserType; +} + +export interface IAuditCreate { + createdBy: string; + created: Moment; + createdByType: UserType; +} + +//TODO Determine if we want to get this from the API or hold it here +export enum UserType { + Associate = "Associate", + System = "System" +} diff --git a/LCU.WebApps.Operations.IncentiveManager/src/shared/models/Commission.ts b/LCU.WebApps.Operations.IncentiveManager/src/shared/models/Commission.ts new file mode 100644 index 0000000..6648765 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/shared/models/Commission.ts @@ -0,0 +1,19 @@ +import { IAppRole } from "./Settings"; +import { IAuditable } from "./Auditable"; + +export interface ICommission { + id: number; + product: string; + ratePerProduct: number; + role: IAppRole; +} + +export interface IAuditableCommission extends ICommission, IAuditable {} + +// used in the commission rate editor +export interface ICommissionRateChange { + id: number; + product: string; + ratePerProduct: number; + role: IAppRole; +} diff --git a/LCU.WebApps.Operations.IncentiveManager/src/shared/models/DeadActivity.ts b/LCU.WebApps.Operations.IncentiveManager/src/shared/models/DeadActivity.ts new file mode 100644 index 0000000..7472da4 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/shared/models/DeadActivity.ts @@ -0,0 +1,101 @@ +import moment, { Moment } from "moment"; +import Product2 from "../enumerations/Product"; +import { + ActivityType, + SystemType, + IActivityDetail, + ActivityState, + IActivitySearchForm, + IManualActivityFormState +} from "./Activity"; +import { IAuditable } from "./Auditable"; + +export interface IDeadActivity { + id: number; + reason: DeadActivityReason; + reasonNote: string; + product: string; + type: ActivityType; + sourceSystem: SystemType; + sourceId?: string; + sourceCreated: Moment; + deadDetail?: IActivityDetail; + departmentId?: string; + employeeId?: string; + employeeEmail?: string; + accepted?: Moment; + state: ActivityState; + livingActivityId?: number; +} + +export interface IDeadActivityDetail { + id?: number; + //personnbr for employee + dnaPersonNbr?: string; + accountNumber?: string; + subAccountNumber?: string; + subjectPersonNbr?: string; + subjectOrgNbr?: string; + memberName?: string; + stationName?: string; + branchOrgNbr?: string; + branchOrgName?: string; + accountStatus?: string; + agreementNbr?: string; + accountBalance?: number; + accountNewBalance?: number; + activityRef?: number; + debtProtectionAddDate?: Moment; + debtProtectionRemoveDate?: Moment; + isDebtProtectionEligible?: boolean; + gapInsuranceAddDate?: Moment; + gapInsuranceRemoveDate?: Moment; + isGapInsuranceEligible?: boolean; + DeadActivity?: IAuditableDeadActivity; +} + +export enum DeadActivityReason { + MissingRequiredField, + FailureToMap, + Other +} + +//extend if add Dead Activity specific fields +export type IDeadActivitySearchForm = IActivitySearchForm; + +export interface IAuditableDeadActivity extends IDeadActivity, IAuditable {} + +export const generateDeadActivityForm = ( + deadActivity: IAuditableDeadActivity +): IManualActivityFormState => { + return { + product: deadActivity.product, + type: deadActivity.type, + sourceSystem: deadActivity.sourceSystem, + sourceId: deadActivity.sourceId, + sourceCreated: moment.utc(deadActivity.sourceCreated), + departmentId: deadActivity.departmentId || "", + employeeId: deadActivity.employeeId || "", + accepted: moment.utc(deadActivity.accepted), + state: deadActivity.state, + detailAccountNumber: deadActivity.deadDetail?.accountNumber, + detailSubjectPersonNbr: deadActivity.deadDetail?.subjectPersonNbr, + detailMemberName: deadActivity.deadDetail?.memberName, + detailAccountStatus: deadActivity.deadDetail?.accountStatus, + detailAccountBalance: deadActivity.deadDetail?.accountBalance, + showValidation: false, + detailIsDebtProtectionEligible: + deadActivity.deadDetail?.isDebtProtectionEligible, + detailDebtProtectionAddDate: + deadActivity.deadDetail?.debtProtectionAddDate, + detailDebtProtectionRemoveDate: + deadActivity.deadDetail?.debtProtectionRemoveDate, + detailIsGapInsuranceEligible: + deadActivity.deadDetail?.isGapInsuranceEligible, + detailGapInsuranceAddDate: deadActivity.deadDetail?.gapInsuranceAddDate, + detailGapInsuranceRemoveDate: + deadActivity.deadDetail?.gapInsuranceRemoveDate, + detailSubjectOrgNbr: deadActivity.deadDetail?.subjectOrgNbr, + deadActivityId: deadActivity.id + }; +}; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/shared/models/Department.ts b/LCU.WebApps.Operations.IncentiveManager/src/shared/models/Department.ts new file mode 100644 index 0000000..b36d935 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/shared/models/Department.ts @@ -0,0 +1,26 @@ +import { IDepartmentAssignmentWithEmployee } from "./DepartmentAssignment"; +import { IEmployee } from "./Employee"; + +export interface IDepartment { + id: number; + departmentId: number; + departmentName: string; + businessLineCode: string; + regionCode?: string; + state: string; //enum from settings + dnaOrgNbr: string; + dayForceGlCode: string; +} + +export interface IDepartmentDto { + departmentName: string; + businessLineCode: string; + regionCode?: string; + dnaOrgNbr: string; + state: string; //enum from settings +} + +export interface IDepartmentDetails { + assignments: IDepartmentAssignmentWithEmployee[]; + regionalManager?: IEmployee; +} diff --git a/LCU.WebApps.Operations.IncentiveManager/src/shared/models/DepartmentAssignment.ts b/LCU.WebApps.Operations.IncentiveManager/src/shared/models/DepartmentAssignment.ts new file mode 100644 index 0000000..b71dcc1 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/shared/models/DepartmentAssignment.ts @@ -0,0 +1,45 @@ +// UserDepartment in API +import { IAuditable } from "./Auditable"; +import { Moment } from "moment"; +import { IAppRole } from "./Settings"; +import { IEmployee } from "./Employee"; + +export interface IBasicDepartmentAssignment { + id?: number; + employeeId: string; + departmentId: number; + startDate: Moment; + endDate?: Moment; + note: string; + isHome: boolean; + role: IAppRole; + slidingPayout: number; + //employee: IProfile + //department: IDepartment +} + +export interface IDepartmentAssignment + extends IBasicDepartmentAssignment, + IAuditable {} + +export interface IDepartmentAssignmentDto { + departmentId: number[]; + startDate: Moment; + endDate?: Moment; + slidingPayout: number; + role: string; + note?: string; +} + +export interface IDepartmentAssignmentUpdateDto { + assignmentId: number; + employeeId: string; + endDate?: Moment; + slidingPayout: number; + role: string; +} + +export interface IDepartmentAssignmentWithEmployee + extends IBasicDepartmentAssignment { + employee: IEmployee; +} diff --git a/LCU.WebApps.Operations.IncentiveManager/src/shared/models/Employee.ts b/LCU.WebApps.Operations.IncentiveManager/src/shared/models/Employee.ts new file mode 100644 index 0000000..696eb82 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/shared/models/Employee.ts @@ -0,0 +1,65 @@ +import { IAppRole, IPermission } from "./Settings"; +import moment, { Moment } from "moment"; +import { IAuditCreate } from "./Auditable"; +import { IUserAssignment } from "./UserAssignment"; +import { IDepartmentAssignment } from "./DepartmentAssignment"; +import { IPayoutLineItem } from "./Payout"; + +export interface IEmployee { + additionalPermissions: IUserPermission[]; + adEmail: string; + assignments: IUserAssignment[]; + baseRole: string; + departments: IDepartmentAssignment[]; + employeeId: string; + fullName: string; + homeDepartment: string; + personId: number; + role: IAppRole; + terminated?: Moment; +} + +export interface IEmployeeDetailsUpdate { + oldEmail: string; + oldEmployeeId: string; + oldFullName: string; + newEmail: string; + newEmployeeId: string; + newFullName: string; +} + +export interface IEmployeeDetailsUpdateResult { + appUser: IEmployee; + updateMessage: string; + updatedSuccessfully: boolean; +} + +export interface IUserPermission extends IAuditCreate { + id: number; + permission: IPermission; +} + +export interface IAdditionalPermissionRequestDto { + addPermCodes: string[]; + removePermCodes: string[]; +} + +export interface IEmployeeSummary { + adEmail: string; + personId: number; + employeeId: string; + fullName: string; + terminated?: Moment; + associatedPayouts: IPayoutLineItem[]; +} + +export function GetCurrentActiveDepartments( + departments: IDepartmentAssignment[] +): IDepartmentAssignment[] { + const currentDate = moment(); + return departments.filter( + (dep) => + currentDate.isAfter(dep.startDate) && + (dep.endDate === null || currentDate.isBefore(dep.endDate)) + ); +} diff --git a/LCU.WebApps.Operations.IncentiveManager/src/shared/models/Goal.ts b/LCU.WebApps.Operations.IncentiveManager/src/shared/models/Goal.ts new file mode 100644 index 0000000..08ae308 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/shared/models/Goal.ts @@ -0,0 +1,102 @@ +import { Moment } from "moment"; +import { Product2 } from "../enumerations/Product"; +import { IAuditable } from "./Auditable"; +import { IEmployee } from "./Employee"; +import { IMonthRange } from "../types/IMonthRange"; +import { IDepartmentAssignment } from "./DepartmentAssignment"; + +interface IBasicGoal { + id: number; + departmentId: number; + employeeId: string; + product: Product2; + startDate: Moment; + endDate: Moment; + value: number; +} + +export interface PerProductGoalType { + [product: string]: number; +} + +export interface PerPersonGoalType { + [person: string]: IPerProductGoal; +} + +export interface PerDepartmentGoalType { + [departmentId: string]: IPerProductGoal; +} + +export interface IPerProductGoal { + productGoals: PerProductGoalType; +} + +export interface IDepartmentGoals { + departmentId: number; + year: number; + monthlyGoals: IPerProductGoal[]; +} + +export interface IPersonnelGoals { + departmentId: number; + year: number; + month: number; + personnel: IEmployee[]; + goals: PerPersonGoalType; +} + +export interface IGoalChangeRequest { + departmentId: number; + personId?: string; + product: Product2; + startDate: Moment; + endDate: Moment; + value: number; +} + +export interface IGoalChangeRequestDto { + departmentId: number; + personId?: string; + product: string; + startDate: Moment; + endDate: Moment; + value: number; +} + +export interface IGoal extends IBasicGoal, IAuditable {} + +export interface IEmployeeGoals { + user: IEmployee; + monthRange: IMonthRange; + departments: IDepartmentAssignment[]; + goals: PerDepartmentGoalType; + goalAudit: IGoal[]; +} + +export interface IDepartmentGoalsInput { + departmentId: string; + year: number; +} + +export interface IPersonnelGoalsInput { + departmentId: string; + year: number; + month: number; + isShowAllPersonnelChecked: boolean; +} + +export interface IGoalEditInput { + type: Product2; + departmentId: number; + personId?: string; + year: number; + month?: number; + value: number; +} + +export interface IDepartmentGoalsForRange { + departmentId: number; + monthRange: IMonthRange; + goals: IPerProductGoal; + goalAudit: IGoal[]; +} diff --git a/LCU.WebApps.Operations.IncentiveManager/src/shared/models/GoalDispersal.ts b/LCU.WebApps.Operations.IncentiveManager/src/shared/models/GoalDispersal.ts new file mode 100644 index 0000000..5206514 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/shared/models/GoalDispersal.ts @@ -0,0 +1,53 @@ +import { Moment } from "moment"; +import Product2 from "../enumerations/Product"; +import { IAppRole } from "./Settings"; + +export interface IGoalDispersalReceipt { + departmentId: number; + goalDispersalErrors: IGoalDispersalReceiptItem[]; +} + +export interface IGoalDispersalReceiptItem { + //technically an enum, keep as string until neecessary. + errorType: string; + message: string; + employeeId: string; + employeeName?: string; +} + +//Do Not get this from the API so we can 'feature flag' different dispersal strategies from the client +export enum AllowedGoalDispersalStrategy { + ExtrapolateFromExistingGoals = "ExtrapolateFromExistingGoals", + CustomWeightedDispersal = "CustomWeightedDispersal" +} + +export interface IGoalDispersalRequest { + departmentId: string; + year: number; + month: number; +} + +export interface ICustomWeightedDispersalFormData { + weightsByRole: IRoleDispersalWeight[]; + dispersalProduct: string; +} +export interface ICustomWeightedDispersalRequest { + departmentId: string; + month: number; + year: number; + percentageByRole: IRoleDispersalWeight[]; + dispersalProduct: string; +} + +export interface IRoleDispersalWeight { + roleName: string; + value: number; +} + +export interface ICustomWeightedDispersalRequestDto { + departmentId: string; + dispersalWeightByRole: { [key: string]: number }; + month: number; + year: number; + dispersalProduct: string; +} diff --git a/LCU.WebApps.Operations.IncentiveManager/src/shared/models/Payout.ts b/LCU.WebApps.Operations.IncentiveManager/src/shared/models/Payout.ts new file mode 100644 index 0000000..6117931 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/shared/models/Payout.ts @@ -0,0 +1,75 @@ +import { IAppRole, IPayoutProfile } from "./Settings"; +import { Moment } from "moment"; +import { IActivity, IAuditableActivity } from "./Activity"; +import { IEmployee } from "./Employee"; +import { ICommission } from "./Commission"; +import { IGoal } from "./Goal"; +import { IMonthRange } from "../types/IMonthRange"; +import { IDepartmentAssignment } from "./DepartmentAssignment"; + +export interface IEmployeePayoutsInput { + employeeId: string; + year: number; + month: number; +} + +export interface IDepartmentPayout { + employee: IEmployee; + monthRange: IMonthRange; + department: IDepartmentAssignment; + eligibleCommissions: ICommission[]; + eligiblePayouts: IPayoutProfile[]; + personalActivity: IActivity[]; + personalGoals: IGoal[]; + departmentActivity: IActivity[]; + departmentGoals: IGoal[]; + commissionPayouts: IPayoutLineItem[]; + goalPayouts: IPayoutLineItem[]; +} + +export interface IPayoutLineItem { + id?: number; + employeeId: string; + departmentId: number; + role?: IAppRole; + product: string; + activityPeriod: Moment; + goalId: number; + goalValue: number; + commissionId: number; + commissionValue: number; + payoutProfileId?: number; + activityPayouts?: IActivityPayoutLineItem[]; + activityTotal: number; + payoutTotal: number; + slidingPayout: number; +} + +export interface IActivityPayoutLineItem { + activityId: number; + payoutLineItemId: number; + activity: IAuditableActivity; + payoutLineItem: IPayoutLineItem; +} + +export interface IPayoutEntry { + code: string; + employeeName: string; + employeeId: string; + payoutTotal: number; +} + +export interface IDepartmentLineItem { + id: number; + departmentId: number; + departmentName: string; + payoutTotal: number; + payoutPercentage: number; + payoutMonth: Moment; +} + +export interface IAccountingSummary { + departmentPayouts: IDepartmentLineItem[]; + total: number; + payoutPeriod: Moment; +} diff --git a/LCU.WebApps.Operations.IncentiveManager/src/shared/models/RoleMapping.ts b/LCU.WebApps.Operations.IncentiveManager/src/shared/models/RoleMapping.ts new file mode 100644 index 0000000..eacdb5c --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/shared/models/RoleMapping.ts @@ -0,0 +1,16 @@ +import { IAppRole } from "./Settings"; +import { IAuditable } from "./Auditable"; + +export interface IRoleMapping { + id: number; + applicationRole: IAppRole; + businessRole: string; +} + +export interface IAuditableRoleMapping extends IRoleMapping, IAuditable {} + +export interface IRoleMappingChange { + id: number; + applicationRole: string; + businessRole: string; +} diff --git a/LCU.WebApps.Operations.IncentiveManager/src/shared/models/Settings.ts b/LCU.WebApps.Operations.IncentiveManager/src/shared/models/Settings.ts new file mode 100644 index 0000000..b87a1ee --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/shared/models/Settings.ts @@ -0,0 +1,76 @@ +import Product2, { IProductDetail } from "../enumerations/Product"; +import { IDepartment } from "./Department"; +import { Moment } from "moment"; + +export interface ISettings { + activityStateSet: { [key: string]: string }; + activityTypeSet: { [key: string]: string }; + appRoles: IAppRole[]; + authorizedReports: IReportSettings[]; + businessLines: IBusinessLine[]; + departmentStateSet: { [key: string]: string }; + organizationSet: { [key: string]: string }; + payoutProfiles: IPayoutProfile[]; + permissions: IPermission[]; + productDetails: IProductDetail[]; + productSet: string[]; + regions: IRegion[]; + systemTypeSet: { [key: string]: string }; + userTypeSet: { [key: string]: string }; + departments: IDepartment[]; + employees: IUserSummary[]; + nextBonusPeriod: Moment; +} + +export interface IUserSummary { + adEmail: string; + employeeId: string; + fullName: string; + managerKey: string; + terminated: string; +} + +export interface IPayoutRange { + minimumPercentage: number; + maximumPercentage: number; + payout: number; +} + +export interface IPayoutProfile { + product: Product2; + roles: IAppRole[]; + payouts: Map; + type: string; + payEach: boolean; +} + +export interface IBusinessLine { + name: string; + code: string; + orgCode: string; +} + +export interface IAppRole { + name: string; + description: string; + permissions: IPermission[]; + hasCommissions: boolean; + rank: number; +} + +export interface IRegion { + name: string; + code: string; + businessLineCode: string; +} + +export interface IPermission { + code: string; + description: string; +} + +export interface IReportSettings { + name: string; + requiredPermissionCode: string; + url: string; +} diff --git a/LCU.WebApps.Operations.IncentiveManager/src/shared/models/UserAssignment.ts b/LCU.WebApps.Operations.IncentiveManager/src/shared/models/UserAssignment.ts new file mode 100644 index 0000000..a391c8a --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/shared/models/UserAssignment.ts @@ -0,0 +1,14 @@ +import { IEmployee } from "./Employee"; + +export interface IUserAssignment { + id: number; + type: string; + key: string; + appUser: IEmployee; +} + +export interface IUserAssignmentDto { + employeeId: string; + type?: string; + key: string; +} diff --git a/LCU.WebApps.Operations.IncentiveManager/src/shared/providers/SettingsProvider.tsx b/LCU.WebApps.Operations.IncentiveManager/src/shared/providers/SettingsProvider.tsx new file mode 100644 index 0000000..96ddac7 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/shared/providers/SettingsProvider.tsx @@ -0,0 +1,34 @@ +import React, { useEffect } from "react"; +import { ISettings } from "../models/Settings"; +import AsyncState from "../enumerations/AsyncState"; +import SettingsService from "../services/SettingsService"; +import IAsyncStateContainer from "../types/IAsyncStateContainer"; + +export const SettingsContext = React.createContext( + undefined +); + +export const SettingsProvider: React.FC = ({ children }) => { + const [state, setState] = React.useState>({ + state: AsyncState.Inactive + }); + + useEffect(() => { + (async (): Promise => { + const result = await SettingsService.getSettings(); + setState(result); + })(); + }, []); + + // TODO, some error handling if we like this... no settings? No app. + + return ( + + {state.state === AsyncState.Loading ? ( +
Loading Settings...
+ ) : ( + children + )} +
+ ); +}; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/shared/security/interceptor.ts b/LCU.WebApps.Operations.IncentiveManager/src/shared/security/interceptor.ts new file mode 100644 index 0000000..7d0a7e8 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/shared/security/interceptor.ts @@ -0,0 +1,47 @@ +import { PublicClientApplication, RedirectRequest } from "@azure/msal-browser"; +import Axios from "axios"; + +export const startInterceptor = async ( + msal: PublicClientApplication, + tokenConfig: RedirectRequest +) => { + const account = msal.getActiveAccount(); + if (account) { + const response = await msal.acquireTokenSilent({ + ...tokenConfig, + account: account + }); + Axios.defaults.headers.common["Authorization"] = + "Bearer " + response.accessToken; + } + + Axios.interceptors.response.use(undefined, (err) => { + if ( + err.response && + err.response.status === 401 && + err.response.config && + !err.response.config.__isRetryRequest + ) { + err.response.config.__isRetryRequest = true; + return new Promise(async (resolve, reject) => { + const account = msal.getActiveAccount(); + if (!account) { + throw Error( + "No active account! Verify a appUser has been signed in and setActiveAccount has been called." + ); + } + const response = await msal.acquireTokenSilent({ + ...tokenConfig, + account: account + }); + Axios.defaults.headers.common["Authorization"] = + "Bearer " + response.accessToken; + err.config.headers.Authorization = + "Bearer " + response.accessToken; + + Axios(err.config).then(resolve, reject); + }); + } + throw err; + }); +}; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/shared/services/ActivityService.test.ts b/LCU.WebApps.Operations.IncentiveManager/src/shared/services/ActivityService.test.ts new file mode 100644 index 0000000..b1f655d --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/shared/services/ActivityService.test.ts @@ -0,0 +1,999 @@ +import axios from "axios"; +import moment from "moment"; +import AsyncState from "../enumerations/AsyncState"; +import Product2, { IProductDetail } from "../enumerations/Product"; +import { + ActivityState, + ActivityTransferState, + ActivityType, + generateDefaultActivityForm, + IActivity, + IActivityStateChange, + IActivityStateChangeDto, + IActivityTransferAction, + IActivityTransferApprovalRequestDto, + IActivityTransferRequestDto, + IAuditableActivity, + IDepartmentProgress, + IEmployeeProgress, + IManualActivityFormState, + SystemType +} from "../models/Activity"; +import { IMonthRange } from "../types/IMonthRange"; +import ActivityService, { mapActivityFormStateToDTO } from "./ActivityService"; + +jest.mock("axios"); + +describe("A service for handling activities", () => { + describe("ActivityService - submitManualActivityForm", () => { + it("Returns invalid async state when required data is missing", async () => { + (axios.post as jest.Mock).mockResolvedValue({ status: 200 }); + const mockFormData: IManualActivityFormState = generateDefaultActivityForm(); + + const results = await ActivityService.submitManualActivityForm( + mockFormData + ); + + expect(results).toEqual({ + state: AsyncState.Invalid, + data: + "Missing One or More Required Fields: Product, ProductType, SourceSystem, DepartmentId, PersonId" + }); + }); + + it("Returns success state when required data is present", async () => { + (axios.post as jest.Mock).mockResolvedValue({ status: 200 }); + const mockFormData: IManualActivityFormState = { + product: "ATMCard", + type: ActivityType.Sale, + sourceSystem: SystemType.ActivityManager, + departmentId: "Goth", + employeeId: "Batman", + showValidation: false, + state: ActivityState.Accepted, + sourceId: "", + sourceCreated: moment() + }; + + const results = await ActivityService.submitManualActivityForm( + mockFormData + ); + + expect(results).toEqual({ + state: AsyncState.Success, + data: undefined + }); + }); + + it("Returns Error state when axios is bad response", async () => { + (axios.post as jest.Mock).mockImplementation(() => + Promise.reject(new Error("Holy mock return Batman!")) + ); + const mockFormData: IManualActivityFormState = { + product: "ATMCard", + type: ActivityType.Sale, + sourceSystem: SystemType.ActivityManager, + departmentId: "Goth", + employeeId: "Batman", + showValidation: false, + state: ActivityState.Accepted, + sourceId: "", + sourceCreated: moment() + }; + + const results = await ActivityService.submitManualActivityForm( + mockFormData + ); + + expect(results).toEqual({ + state: AsyncState.Failure, + data: "Holy mock return Batman!" + }); + }); + }); + + describe("ActivityService - mapActivityFormStateToDTO", () => { + it("maps date to undefined if they are invalid", () => { + let mockFormData: IManualActivityFormState = { + product: "ATMCard", + type: ActivityType.Sale, + sourceSystem: SystemType.ActivityManager, + departmentId: "Gotham", + employeeId: "Batman", + sourceCreated: moment("🤡"), + accepted: moment("2020-01-10"), + showValidation: false, + state: ActivityState.Accepted, + sourceId: "" + }; + + let results = mapActivityFormStateToDTO(mockFormData); + + expect(results.sourceCreated).toBe(undefined); + expect(results.accepted).toEqual(moment("2020-01-10")); + + mockFormData = { + product: "ATMCard", + type: ActivityType.Sale, + sourceSystem: SystemType.ActivityManager, + departmentId: "Gotham", + employeeId: "Batman", + accepted: moment("🤡"), + sourceCreated: moment("2020-01-10"), + showValidation: false, + state: ActivityState.ManualReview + }; + + results = mapActivityFormStateToDTO(mockFormData); + expect(results.accepted).toBe(undefined); + expect(results.sourceCreated).toEqual(moment("2020-01-10")); + }); + + it("Maps empty string to undefined", () => { + const mockFormData: IManualActivityFormState = { + product: "ATMCard", + type: ActivityType.Sale, + sourceSystem: SystemType.ActivityManager, + departmentId: "Gotham", + employeeId: "Batman", + detailAccountNumber: "", + detailMemberName: "", + detailAccountStatus: "", + detailSubjectPersonNbr: "", + detailSubjectOrgNbr: "", + detailAccountBalance: undefined, + detailDebtProtectionAddDate: undefined, + showValidation: false, + state: ActivityState.ManualReview, + sourceCreated: moment() + }; + + const results = mapActivityFormStateToDTO(mockFormData); + + expect(results.detail.accountNumber).toBe(undefined); + expect(results.detail.memberName).toBe(undefined); + expect(results.detail.accountStatus).toBe(undefined); + expect(results.detail.subjectPersonNbr).toBe(undefined); + expect(results.detail.subjectOrgNbr).toBe(undefined); + expect(results.detail.accountNewBalance).toBe(undefined); + expect(results.detail.accountBalance).toBe(undefined); + expect(results.detail.debtProtectionAddDate).toBe(undefined); + expect(results.detail.isDebtProtectionEligible).toBe(undefined); + }); + + it("Maps Passes non empty string values to DTO", () => { + const mockFormData: IManualActivityFormState = { + product: "ATMCard", + type: ActivityType.Sale, + sourceSystem: SystemType.ActivityManager, + departmentId: "Gotham", + employeeId: "Batman", + detailAccountNumber: "B", + detailMemberName: "C", + detailAccountStatus: "D", + detailSubjectPersonNbr: "E", + detailSubjectOrgNbr: "F", + detailAccountBalance: 10, + detailDebtProtectionAddDate: moment("2021-01-01T00:00"), + detailIsDebtProtectionEligible: true, + showValidation: false, + state: ActivityState.ManualReview, + sourceCreated: moment() + }; + + const results = mapActivityFormStateToDTO(mockFormData); + + expect(results.detail.accountNumber).toBe("B"); + expect(results.detail.memberName).toBe("C"); + expect(results.detail.accountStatus).toBe("D"); + expect(results.detail.subjectPersonNbr).toBe("E"); + expect(results.detail.subjectOrgNbr).toBe("F"); + expect(results.detail.accountBalance).toBe(10); + expect(results.detail.debtProtectionAddDate).toEqual( + moment("2021-01-01T00:00") + ); + expect(results.detail.isDebtProtectionEligible).toEqual(true); + }); + }); + + describe("ActivityService - getMyProgress", () => { + it("Returns success with data", async () => { + const range: IMonthRange = { + fromMonth: 1, + fromYear: 2021, + toMonth: 2, + toYear: 2022 + }; + + const progress = { + user: { personId: 123 } + } as IEmployeeProgress; + + (axios.get as jest.Mock).mockResolvedValue({ + status: 200, + data: progress + }); + + const result = await ActivityService.getMyProgress(range); + + expect(result).toEqual({ + state: AsyncState.Success, + data: progress + }); + expect(axios.get).toHaveBeenCalledTimes(1); + expect(axios.get).toBeCalledWith(expect.anything(), { + params: range, + withCredentials: true + }); + }); + + it("Returns Not Found state if axios response is 404", async () => { + (axios.get as jest.Mock).mockImplementationOnce(() => + Promise.reject({ + response: { + status: 404 + } + }) + ); + + const result = await ActivityService.getMyProgress( + {} as IMonthRange + ); + + expect(result).toEqual({ + state: AsyncState.NotFound + }); + }); + + it("returns Failure if axios error is not 404", async () => { + (axios.get as jest.Mock).mockImplementation(() => { + Promise.reject({ + response: { + status: 500 + } + }); + }); + + const result = await ActivityService.getMyProgress( + {} as IMonthRange + ); + + expect(result).toEqual({ + state: AsyncState.Failure + }); + }); + }); + + describe("ActivityService - getMyDepartmentProgress", () => { + it("Returns success with data", async () => { + const range: IMonthRange = { + fromMonth: 1, + fromYear: 2021, + toMonth: 2, + toYear: 2022 + }; + + const progress = { + departmentId: 1, + monthRange: range, + progress: { + productActivity: { product: 1 } + }, + activityAudit: [], + goals: { + productGoals: {} + } + } as IDepartmentProgress; + + (axios.get as jest.Mock).mockResolvedValue({ + status: 200, + data: progress + }); + + const result = await ActivityService.getMyDepartmentProgress(range); + + expect(result).toEqual({ + state: AsyncState.Success, + data: progress + }); + expect(axios.get).toHaveBeenCalledTimes(1); + expect(axios.get).toBeCalledWith(expect.anything(), { + params: range, + withCredentials: true + }); + }); + + it("returns Failure if axios error", async () => { + (axios.get as jest.Mock).mockImplementation(() => { + Promise.reject({ + response: { + status: 500 + } + }); + }); + + const result = await ActivityService.getMyDepartmentProgress( + {} as IMonthRange + ); + + expect(result).toEqual({ + state: AsyncState.Failure + }); + }); + }); + + describe("getValidActivityTypesForProduct", () => { + it("returns activity types for undefined product", () => { + const result = ActivityService.getValidActivityTypesForProduct(); + expect(result).toEqual(["Other", "Referral", "Sale"]); + }); + + it("returns activity types for sellable only product", () => { + const product = { + isSellable: true, + isReferrable: false + } as IProductDetail; + const result = ActivityService.getValidActivityTypesForProduct( + product + ); + expect(result).toEqual(["Sale"]); + }); + + it("returns activity types for referrable only product", () => { + const product = { + isSellable: false, + isReferrable: true + } as IProductDetail; + const result = ActivityService.getValidActivityTypesForProduct( + product + ); + expect(result).toEqual(["Referral"]); + }); + + it("returns activity types for sellable and referrable product", () => { + const product = { + isSellable: true, + isReferrable: true + } as IProductDetail; + const result = ActivityService.getValidActivityTypesForProduct( + product + ); + expect(result).toEqual(["Referral", "Sale"]); + }); + }); + + describe("getStateChanges", () => { + it("Returns success with data", async () => { + const stateChanges = [ + { id: 101, activityId: 1 }, + { id: 102, activityId: 1 } + ] as IActivityStateChange[]; + + (axios.get as jest.Mock).mockResolvedValue({ + status: 200, + data: stateChanges + }); + + const result = await ActivityService.getStateChanges(1); + + expect(result).toEqual({ + state: AsyncState.Success, + data: stateChanges + }); + expect(axios.get).toHaveBeenCalledTimes(1); + expect(axios.get).toBeCalledWith(expect.anything(), { + withCredentials: true + }); + }); + + it("returns Failure if axios error", async () => { + (axios.get as jest.Mock).mockImplementation(() => { + throw new Error("Service Error"); + }); + + const result = await ActivityService.getStateChanges(1); + expect(result).toEqual({ + state: AsyncState.Failure, + data: "Service Error" + }); + }); + }); + + describe("getActivity", () => { + it("Returns success with data", async () => { + const activity = { + id: 1, + product: Product2.ATMCard + } as IAuditableActivity; + + (axios.get as jest.Mock).mockResolvedValue({ + status: 200, + data: activity + }); + + const result = await ActivityService.getActivity(1); + + expect(result).toEqual({ + state: AsyncState.Success, + data: activity + }); + expect(axios.get).toHaveBeenCalledTimes(1); + expect(axios.get).toBeCalledWith(expect.anything(), { + withCredentials: true + }); + }); + + it("returns Failure if axios error", async () => { + (axios.get as jest.Mock).mockImplementation(() => { + throw new Error("Service Error"); + }); + + const result = await ActivityService.getActivity(1); + expect(result).toEqual({ + state: AsyncState.Failure, + data: "Service Error" + }); + }); + }); + + describe("postActivityStateChange", () => { + it("Returns success with data", async () => { + const returnedActivity = { + id: 1, + product: Product2.ATMCard + } as IAuditableActivity; + const stateChange: IActivityStateChangeDto = { + activityId: 1, + state: ActivityState.Accepted, + note: "Note" + }; + (axios.post as jest.Mock).mockResolvedValue({ + status: 200, + data: returnedActivity + }); + + const result = await ActivityService.postActivityStateChange( + stateChange + ); + + expect(result).toEqual({ + state: AsyncState.Success, + data: returnedActivity + }); + expect(axios.post).toHaveBeenCalledTimes(1); + expect(axios.post).toBeCalledWith(expect.anything(), stateChange, { + withCredentials: true + }); + }); + + it("returns Failure if axios error", async () => { + const stateChange: IActivityStateChangeDto = { + activityId: 1, + state: ActivityState.Accepted, + note: "Note" + }; + (axios.post as jest.Mock).mockImplementation(() => { + throw new Error("Service Error"); + }); + + const result = await ActivityService.postActivityStateChange( + stateChange + ); + expect(result).toEqual({ + state: AsyncState.Failure, + data: "Service Error" + }); + }); + }); + + describe("postRequestTransfer", () => { + it("Returns success with data", async () => { + const returnedAction: IActivityTransferAction = { + id: 1, + activityStateBeforeTransfer: ActivityState.Accepted, + requestedActivityId: 2, + state: ActivityTransferState.Requested, + newActivityDepartmentId: 3, + newActivityEmployeeId: "C000" + }; + const transferRequest: IActivityTransferRequestDto = { + activityStateBeforeTransfer: ActivityState.Accepted, + requestedActivityId: 2, + newDepartmentId: 3, + newEmployeeId: "C000" + }; + (axios.post as jest.Mock).mockResolvedValue({ + status: 200, + data: returnedAction + }); + + const result = await ActivityService.postRequestTransfer( + transferRequest + ); + + expect(result).toEqual({ + state: AsyncState.Success, + data: returnedAction + }); + expect(axios.post).toHaveBeenCalledTimes(1); + expect(axios.post).toBeCalledWith( + expect.anything(), + transferRequest, + { + withCredentials: true + } + ); + }); + + it("returns Failure if axios error", async () => { + const transferRequest: IActivityTransferRequestDto = { + activityStateBeforeTransfer: ActivityState.Accepted, + requestedActivityId: -2, + newDepartmentId: 3, + newEmployeeId: "C000" + }; + (axios.post as jest.Mock).mockImplementation(() => { + throw new Error("Service Error"); + }); + + const result = await ActivityService.postRequestTransfer( + transferRequest + ); + expect(result).toEqual({ + state: AsyncState.Failure, + data: "Service Error" + }); + }); + }); + + describe("postRequestToOwnDepartmentTransfer", () => { + it("Returns success with data", async () => { + const returnedAction: IActivityTransferAction = { + id: 1, + activityStateBeforeTransfer: ActivityState.Accepted, + requestedActivityId: 2, + state: ActivityTransferState.Requested, + newActivityDepartmentId: 3, + newActivityEmployeeId: "C000" + }; + const transferRequest: IActivityTransferRequestDto = { + activityStateBeforeTransfer: ActivityState.Accepted, + requestedActivityId: 2, + newDepartmentId: 3, + newEmployeeId: "C000" + }; + (axios.post as jest.Mock).mockResolvedValue({ + status: 200, + data: returnedAction + }); + + const result = await ActivityService.postRequestTransferToOwnDepartment( + transferRequest + ); + + expect(result).toEqual({ + state: AsyncState.Success, + data: returnedAction + }); + expect(axios.post).toHaveBeenCalledTimes(1); + expect(axios.post).toBeCalledWith( + expect.anything(), + transferRequest, + { + withCredentials: true + } + ); + }); + + it("returns Failure if axios error", async () => { + const transferRequest: IActivityTransferRequestDto = { + activityStateBeforeTransfer: ActivityState.Accepted, + requestedActivityId: -2, + newDepartmentId: 3, + newEmployeeId: "C000" + }; + (axios.post as jest.Mock).mockImplementation(() => { + throw new Error("Service Error"); + }); + + const result = await ActivityService.postRequestTransferToOwnDepartment( + transferRequest + ); + expect(result).toEqual({ + state: AsyncState.Failure, + data: "Service Error" + }); + }); + }); + + describe("getRequestedTransferAction", () => { + it("should return success with data", async () => { + const returnedAction: IActivityTransferAction = { + id: 1, + activityStateBeforeTransfer: ActivityState.Accepted, + requestedActivityId: 2, + state: ActivityTransferState.Requested, + newActivityDepartmentId: 3, + newActivityEmployeeId: "C000" + }; + (axios.get as jest.Mock).mockResolvedValue({ + status: 200, + data: returnedAction + }); + + const result = await ActivityService.getRequestedTransferAction(1); + + expect(result).toEqual({ + state: AsyncState.Success, + data: returnedAction + }); + expect(axios.get).toHaveBeenCalledTimes(1); + expect(axios.get).toBeCalledWith(expect.anything(), { + withCredentials: true + }); + }); + + it("should return failure if axios error", async () => { + (axios.get as jest.Mock).mockImplementation(() => { + throw new Error("Service Error"); + }); + + const result = await ActivityService.getRequestedTransferAction(1); + + expect(result).toEqual({ + state: AsyncState.Failure, + data: "Service Error" + }); + }); + }); + + describe("postApproveTransfer", () => { + it("should return success with data", async () => { + const approvalRequest: IActivityTransferApprovalRequestDto = { + id: 1, + approvalState: ActivityTransferState.Accepted + }; + const returnedAction: IActivityTransferAction = { + id: 1, + activityStateBeforeTransfer: ActivityState.Accepted, + requestedActivityId: 2, + state: ActivityTransferState.Requested, + newActivityDepartmentId: 3, + newActivityEmployeeId: "C000" + }; + (axios.post as jest.Mock).mockResolvedValue({ + status: 200, + data: returnedAction + }); + + const result = await ActivityService.postApproveTransfer( + approvalRequest + ); + + expect(result).toEqual({ + state: AsyncState.Success, + data: returnedAction + }); + expect(axios.post).toHaveBeenCalledTimes(1); + expect(axios.post).toBeCalledWith( + expect.anything(), + approvalRequest, + { + withCredentials: true + } + ); + }); + + it("should return failure if axios error", async () => { + const approvalRequest: IActivityTransferApprovalRequestDto = { + id: 1, + approvalState: ActivityTransferState.Accepted + }; + (axios.post as jest.Mock).mockImplementation(() => { + throw new Error("Service Error"); + }); + + const result = await ActivityService.postApproveTransfer( + approvalRequest + ); + + expect(result).toEqual({ + state: AsyncState.Failure, + data: "Service Error" + }); + }); + }); + + describe("isPendingReview", () => { + it.each([ + [ActivityState.ManualReview, true], + [ActivityState.PendingReferral, true], + [ActivityState.Accepted, false], + [ActivityState.Created, false], + [ActivityState.PendingTransfer, false], + [ActivityState.Rejected, false], + [ActivityState.Transferred, false] + ])( + "should return true if activity is in manual review or pending referral state", + (activityState: ActivityState, expected: boolean) => { + const activity = { + state: activityState + } as IActivity; + + expect(ActivityService.isPendingReview(activity)).toBe( + expected + ); + } + ); + }); + + describe("approvalHasBeenDecided", () => { + it.each([ + [ActivityState.ManualReview, false], + [ActivityState.PendingReferral, false], + [ActivityState.Accepted, true], + [ActivityState.Created, false], + [ActivityState.PendingTransfer, false], + [ActivityState.Rejected, true], + [ActivityState.Transferred, false] + ])( + "should return true if activity is accepted or rejected", + (activityState: ActivityState, expected: boolean) => { + const activity = { + state: activityState + } as IActivity; + + expect(ActivityService.approvalHasBeenDecided(activity)).toBe( + expected + ); + } + ); + }); + + describe("referralApproversCanDecide", () => { + it.each([ + [ActivityState.ManualReview, true], + [ActivityState.PendingReferral, true], + [ActivityState.Accepted, true], + [ActivityState.Created, false], + [ActivityState.PendingTransfer, false], + [ActivityState.Rejected, true], + [ActivityState.Transferred, false] + ])( + "should be true if activity is pending referral or sale", + (activityState: ActivityState, expected: boolean) => { + const referralActivity = { + type: ActivityType.Referral, + state: activityState + } as IActivity; + + expect( + ActivityService.referralApproversCanDecide(referralActivity) + ).toBe(expected); + } + ); + + it.each([ + ActivityState.ManualReview, + ActivityState.PendingReferral, + ActivityState.Accepted, + ActivityState.Created, + ActivityState.PendingTransfer, + ActivityState.Rejected, + ActivityState.Transferred + ])( + "should return false if activity is other or sale type", + (activityState: ActivityState) => { + const saleActivity = { + type: ActivityType.Sale, + state: activityState + } as IActivity; + + expect( + ActivityService.referralApproversCanDecide(saleActivity) + ).toBe(false); + } + ); + }); + + describe("saleApproversCanDecide", () => { + it.each([ + [ActivityState.ManualReview, true], + [ActivityState.PendingReferral, true], + [ActivityState.Accepted, false], + [ActivityState.Created, false], + [ActivityState.PendingTransfer, false], + [ActivityState.Rejected, false], + [ActivityState.Transferred, false] + ])( + "should return true if referral or sale is pending review", + (activityState: ActivityState, expected: boolean) => { + const referralActivity = { + type: ActivityType.Referral, + state: activityState + } as IActivity; + + expect( + ActivityService.saleApproversCanDecide(referralActivity) + ).toBe(expected); + + const saleActivity = { + type: ActivityType.Sale, + state: activityState + } as IActivity; + + expect( + ActivityService.saleApproversCanDecide(saleActivity) + ).toBe(expected); + } + ); + }); + + describe("isEligibleForTransfer", () => { + it.each([ + [ActivityState.ManualReview, false], + [ActivityState.PendingReferral, false], + [ActivityState.Accepted, true], + [ActivityState.Created, false], + [ActivityState.PendingTransfer, false], + [ActivityState.Rejected, false], + [ActivityState.Transferred, false] + ])( + "should return true if state matches", + (activityState: ActivityState, expected: boolean) => { + const activity: IActivity = { + activityPayouts: [], + departmentId: "3", + employeeEmail: "employee@landmarkcu.com", + employeeId: "C0000", + detail: {}, + id: 1, + product: Product2.Vehicle, + sourceSystem: SystemType.Dna, + type: ActivityType.Sale, + state: activityState + }; + + expect(ActivityService.isEligbleForTransfer(activity)).toBe( + expected + ); + } + ); + + it.each([ + ActivityState.ManualReview, + ActivityState.PendingReferral, + ActivityState.Accepted, + ActivityState.Created, + ActivityState.PendingTransfer, + ActivityState.Rejected, + ActivityState.Transferred + ])( + "should return true if state matches", + (activityState: ActivityState) => { + const activity: IActivity = { + activityPayouts: [ + { + payoutLineItem: { + employeeId: "C0000", + departmentId: 3, + activityPeriod: moment(), + activityTotal: 1, + commissionId: 2, + commissionValue: 3, + goalId: 4, + goalValue: 5, + payoutTotal: 6, + product: "ATMCard", + slidingPayout: 1 + } + } + ], + departmentId: "3", + employeeEmail: "employee@landmarkcu.com", + employeeId: "C0000", + detail: {}, + id: 1, + product: Product2.Vehicle, + sourceSystem: SystemType.Dna, + type: ActivityType.Sale, + state: activityState + }; + + expect(ActivityService.isEligbleForTransfer(activity)).toBe( + false + ); + } + ); + }); +}); + +describe("getPaginatedActivitiesFromUrl", () => { + //This test just verifies it can properly access the /api/ ending from the parameter url. + it("Formats url properly", () => { + (axios.post as jest.Mock).mockResolvedValue({ status: 200 }); + ActivityService.getPaginatedActivitiesFromUrl( + "http:///localhost:20001/api/Activity/paged?Page=1&Limit=10" + ); + expect( + axios.get as jest.Mock + ).toBeCalledWith( + "https://localhost:20001/api/Activity/paged?Page=1&Limit=10", + { withCredentials: true } + ); + }); + + describe("ActivityService - isSourceIdFieldVisible", () => { + let formState: IManualActivityFormState = { + product: Product2.LIC.code, + type: ActivityType.Referral + }; + + it("returns true for LIC Referrals", () => { + expect(ActivityService.isSourceIdFieldVisible(formState)).toBe( + true + ); + }); + it("returns false for LIC - Sale and LIC - Other", () => { + expect( + ActivityService.isSourceIdFieldVisible({ + ...formState, + type: ActivityType.Other + }) + ).toBe(false); + expect( + ActivityService.isSourceIdFieldVisible({ + ...formState, + type: ActivityType.Sale + }) + ).toBe(false); + }); + it("returns false for all other referrals", () => { + Product2.Products.filter( + (x) => x.code != Product2.LIC.code + ).forEach((p) => { + expect( + ActivityService.isSourceIdFieldVisible({ + ...formState, + product: p.code + }) + ).toBe(false); + }); + }); + }); + + describe("ActivityService - isValidManualActivity", () => { + let formState: IManualActivityFormState = { + product: Product2.LIC.code, + type: ActivityType.Referral, + sourceId: "", + state: ActivityState.Accepted, + sourceSystem: SystemType.Dna, + departmentId: "10", + employeeId: "100", + accepted: moment(), + detailSubjectPersonNbr: "12345", + sourceCreated: moment(), + showValidation: false + }; + it("Is not valid if its a LIC - Referral and SourceId is missing", () => { + expect(ActivityService.isValidManualActivity(formState)).toBe( + false + ); + expect( + ActivityService.isValidManualActivity({ + ...formState, + sourceId: undefined + }) + ).toBe(false); + }); + it("Is valid if its a LIC - Referral and SourceId is populated", () => { + expect( + ActivityService.isValidManualActivity({ + ...formState, + sourceId: "123456" + }) + ).toBe(true); + }); + }); +}); diff --git a/LCU.WebApps.Operations.IncentiveManager/src/shared/services/ActivityService.ts b/LCU.WebApps.Operations.IncentiveManager/src/shared/services/ActivityService.ts new file mode 100644 index 0000000..4a327e0 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/shared/services/ActivityService.ts @@ -0,0 +1,852 @@ +import Axios from "axios"; +import AsyncState from "../enumerations/AsyncState"; +import IAsyncStateContainer, { + IAsyncStateContainerStrict +} from "../types/IAsyncStateContainer"; +import Configuration from "./../configuration/Configuration"; +import { + ActivityState, + ActivityType, + IActivity, + IActivityDetailUpdate, + IActivityDTO, + IActivitySearchForm, + IActivityStateChange, + IActivityStateChangeDto, + IActivityTransferAction, + IActivityTransferApprovalRequestDto, + IActivityTransferRequestDto, + IAuditableActivity, + IAuthorizedActivityCommands, + IBusinessLineProgress, + IDepartmentProgress, + IEmployeeProgress, + IManualActivityFormState, + IPerProductActivity, + IProgressTotal, + IRegionProgress, + IViewableActivityDetail +} from "../models/Activity"; +import IPagedResponse from "./../types/IPagedResponse"; +import { IMonthRange } from "../types/IMonthRange"; +import { IProductDetail, Product2 } from "../enumerations/Product"; +import { IPerProductGoal } from "../models/Goal"; + +//TODO: This should be temporary until validation is added to the form +const MISSING_REQUIRED_FIELD_MESSAGE = + "Missing One or More Required Fields: Product, ProductType, SourceSystem, DepartmentId, PersonId"; + +export default class ActivityService { + static joinTotalProgress = ( + allTotals: IProgressTotal[] + ): IProgressTotal => { + const goalTotal: IPerProductGoal = { + productGoals: {} + }; + const activityTotal: IPerProductActivity = { + productActivity: {}, + productOther: {} + }; + for (let index = 0; index < allTotals.length; index++) { + const eachProgress = allTotals[index].activityTotal; + const eachGoals = allTotals[index].goalTotal; + Product2.Products.forEach((product) => { + if (goalTotal.productGoals[product.code] === undefined) { + goalTotal.productGoals[product.code] = 0; + activityTotal.productActivity[product.code] = 0; + activityTotal.productOther[product.code] = 0; + } + // No need to check for Debt Protection, this is totaling totals. + goalTotal.productGoals[product.code] += + eachGoals.productGoals[product.code]; + activityTotal.productActivity[product.code] += + eachProgress.productActivity[product.code]; + activityTotal.productOther[product.code] += + eachProgress.productOther[product.code]; + }); + } + return { + goalTotal: goalTotal, + activityTotal: activityTotal + }; + }; + + static calculateTotalProgress = ( + data: { progress: IPerProductActivity; goals: IPerProductGoal }[] + ): IProgressTotal => { + const goalTotal: IPerProductGoal = { + productGoals: {} + }; + const activityTotal: IPerProductActivity = { + productActivity: {}, + productOther: {} + }; + for (let index = 0; index < data.length; index++) { + const eachProgress = data[index].progress; + const eachGoals = data[index].goals; + Product2.Products.forEach((product) => { + if (goalTotal.productGoals[product.code] === undefined) { + goalTotal.productGoals[product.code] = 0; + activityTotal.productActivity[product.code] = 0; + activityTotal.productOther[product.code] = 0; + } + + if ( + product.hasPersonnelGoals || + product.code === Product2.DebtProtectionEligibleLoan.code + ) { + if ( + product.code === + Product2.DebtProtectionEligibleLoan.code + ) { + activityTotal.productOther[product.code] += + eachProgress.productOther[product.code]; + } else { + goalTotal.productGoals[product.code] += + eachGoals.productGoals[product.code]; + } + activityTotal.productActivity[product.code] += + eachProgress.productActivity[product.code]; + } else { + activityTotal.productActivity[product.code] += + eachProgress.productActivity[product.code]; + } + }); + } + + return { + goalTotal: goalTotal, + activityTotal: activityTotal + }; + }; + + static submitDeleteDeadActivity = async ( + deadActivityId: number, + matchedActivityId?: string + ): Promise> => { + try { + const url = + Configuration.PrefixIncentiveUrl( + Configuration.Get.urls.incentiveApi.deleteDeadActivity + ) + + "/" + + deadActivityId; + let params = {}; + if (matchedActivityId) { + params = { + activityRef: matchedActivityId + }; + } + const res = await Axios.delete(url, { + params: params, + withCredentials: true + }); + + return { + state: AsyncState.Success, + data: res.data + }; + } catch (err) { + if (err.message === MISSING_REQUIRED_FIELD_MESSAGE) { + return { + state: AsyncState.Invalid, + data: err.message + }; + } + return { + state: AsyncState.Failure, + data: err.message + }; + } + }; + + static submitManualActivityForm = async ( + formData: IManualActivityFormState + ): Promise> => { + try { + const url = Configuration.PrefixIncentiveUrl( + Configuration.Get.urls.incentiveApi.submitActivityForm + ); + const body = mapActivityFormStateToDTO(formData); + const res = await Axios.post(url, body, { withCredentials: true }); + + return { + state: AsyncState.Success, + data: res.data + }; + } catch (err) { + if (err.message === MISSING_REQUIRED_FIELD_MESSAGE) { + return { + state: AsyncState.Invalid, + data: err.message + }; + } + return { + state: AsyncState.Failure, + data: err.message + }; + } + }; + + static getPaginatedActivities = async ( + page: number, + limit: number, + filters: Partial + ): Promise>> => { + try { + const url = Configuration.PrefixIncentiveUrl( + Configuration.Get.urls.incentiveApi.getAllActivitiesPaged + ); + + // I wanted to just do ...filters, but the moment.tostring adds quotes! + const params = new URLSearchParams(); + params.append("Limit", limit?.toString()); + params.append("Page", page?.toString()); + + if (filters.state) { + filters.state.forEach((activityState) => { + params.append("state", activityState); + }); + } + + if (filters.employeeId) { + params.append("employeeId", filters.employeeId); + } + + if (filters.departmentId) { + params.append("departmentId", filters.departmentId.toString()); + } + + if (filters.query) { + params.append("query", filters.query); + } + + if (filters.product) { + params.append("product", filters.product); + } + + if (filters.type) { + params.append("type", filters.type); + } + + if (filters.sortDescending) { + params.append("sortDescending", String(filters.sortDescending)); + } + + if (filters.sortBy) { + params.append("sortBy", filters.sortBy); + } + + if (filters.dateLow && filters.dateLow.isValid()) { + params.append("dateLow", filters.dateLow.format("MM/DD/YYYY")); + } + + if (filters.dateHigh && filters.dateHigh.isValid()) { + params.append( + "dateHigh", + filters.dateHigh.format("MM/DD/YYYY") + ); + } + + const res = await Axios.get(url, { + withCredentials: true, + params: params + }); + + return { + state: AsyncState.Success, + data: res.data + }; + } catch (err) { + if (err.response?.status == 404) { + return { + state: AsyncState.NotFound + }; + } + return { + state: AsyncState.Failure + }; + } + }; + + static getPaginatedActivitiesFromUrl = async ( + url: string + ): Promise>> => { + // For some reason API is not returning URL with basename (/incentive/). + // This could be similar to the issue we had with Swagger. + // We can just take the query and append it to the correct url for now + // This should always work, even if the url is fixed. + // Note for prod "/api" appears twice, you must split on "/api/" + const newUrl = Configuration.PrefixIncentiveUrl( + "/" + url.split("/api/")[1] + ); + try { + const res = await Axios.get(newUrl, { withCredentials: true }); + + return { + state: AsyncState.Success, + data: res.data + }; + } catch (err) { + if (err.response?.status == 404) { + return { + state: AsyncState.NotFound + }; + } + return { + state: AsyncState.Failure + }; + } + }; + + static getMyProgress = async ( + monthRange: IMonthRange, + employeeId?: string + ): Promise> => { + try { + const url = Configuration.PrefixIncentiveUrl( + Configuration.Get.urls.incentiveApi.getMyActivities + ); + + const res = await Axios.get(url, { + withCredentials: true, + params: { ...monthRange, employeeId: employeeId } + }); + + return { + state: AsyncState.Success, + data: res.data + }; + } catch (err) { + if (err.response?.status == 404) { + return { + state: AsyncState.NotFound + }; + } + return { + state: AsyncState.Failure + }; + } + }; + + static getMyDepartmentProgress = async ( + monthRange: IMonthRange, + departmentId?: string + ): Promise> => { + try { + const url = Configuration.PrefixIncentiveUrl( + Configuration.Get.urls.incentiveApi.getMyDepartmentActivities + ); + + const res = await Axios.get(url, { + withCredentials: true, + params: { ...monthRange, departmentId: departmentId } + }); + + return { + state: AsyncState.Success, + data: res.data + }; + } catch (err) { + return { + state: AsyncState.Failure + }; + } + }; + + static getRegionProgress = async ( + monthRange: IMonthRange, + regionCode: string + ): Promise> => { + try { + const url = Configuration.PrefixIncentiveUrl( + Configuration.Get.urls.incentiveApi.getMyRegionProgress + ); + + const res = await Axios.get(url, { + withCredentials: true, + params: { ...monthRange, regionCode: regionCode } + }); + + return { + state: AsyncState.Success, + data: res.data + }; + } catch (err) { + return { + state: AsyncState.Failure + }; + } + }; + + static getBusinessProgress = async ( + monthRange: IMonthRange, + blCode: string + ): Promise> => { + try { + const url = Configuration.PrefixIncentiveUrl( + Configuration.Get.urls.incentiveApi.getBusinessLineProgress + ); + + const res = await Axios.get(url, { + withCredentials: true, + params: { ...monthRange, businessLineCode: blCode } + }); + + return { + state: AsyncState.Success, + data: res.data + }; + } catch (err) { + return { + state: AsyncState.Failure + }; + } + }; + + static getStateChanges = async ( + activityId: number + ): Promise> => { + try { + const url = Configuration.PrefixIncentiveUrl( + Configuration.Get.urls.incentiveApi.getActivityStateChanges.replace( + "{activityId}", + `${activityId}` + ) + ); + + const response = await Axios.get(url, { + withCredentials: true + }); + + return { + state: AsyncState.Success, + data: response.data + }; + } catch (err) { + return { + state: AsyncState.Failure, + data: err.message + }; + } + }; + + static getDetailUpdates = async ( + activityId: number + ): Promise> => { + try { + const url = Configuration.PrefixIncentiveUrl( + Configuration.Get.urls.incentiveApi.getActivityDetailUpdates.replace( + "{activityId}", + `${activityId}` + ) + ); + + const response = await Axios.get(url, { + withCredentials: true + }); + + return { + state: AsyncState.Success, + data: response.data + }; + } catch (err) { + return { + state: AsyncState.Failure, + data: err.message + }; + } + }; + + static getViewableActivity = async ( + activityId: number + ): Promise> => { + try { + const activity = await ActivityService.getActivity(activityId); + const stateChanges = await ActivityService.getStateChanges( + activityId + ); + const detailUpdates = await ActivityService.getDetailUpdates( + activityId + ); + const transferAction = await ActivityService.getRequestedTransferAction( + activityId + ); + const allowed = await ActivityService.getActivityAllowedCommands( + activityId + ); + + return { + state: AsyncState.Success, + data: { + activity: activity.data, + authorized: allowed.data, + stateChanges: stateChanges.data, + detailUpdates: detailUpdates.data, + transferAction: transferAction.data + } + }; + } catch (err) { + return { + state: AsyncState.Failure, + data: err.message + }; + } + }; + + static getActivity = async ( + activityId: number + ): Promise> => { + try { + const url = Configuration.PrefixIncentiveUrl( + `${Configuration.Get.urls.incentiveApi.getActivity}/${activityId}` + ); + + const response = await Axios.get(url, { + withCredentials: true + }); + + return { + state: AsyncState.Success, + data: response.data + }; + } catch (err) { + return { + state: AsyncState.Failure, + data: err.message + }; + } + }; + + static getActivityAllowedCommands = async ( + activityId: number + ): Promise> => { + try { + const url = Configuration.PrefixIncentiveUrl( + `${Configuration.Get.urls.incentiveApi.getActivity}/${activityId}/AvailableCommands` + ); + + const response = await Axios.get(url, { + withCredentials: true + }); + + return { + state: AsyncState.Success, + data: response.data || { + allowedChanges: [] + } + }; + } catch (err) { + return { + state: AsyncState.Failure, + data: err.message + }; + } + }; + + static postActivityStateChange = async ( + activityStateChange: IActivityStateChangeDto + ): Promise> => { + try { + const url = Configuration.PrefixIncentiveUrl( + `${Configuration.Get.urls.incentiveApi.postActivityStateChange}` + ); + + const response = await Axios.post(url, activityStateChange, { + withCredentials: true + }); + + return { + state: AsyncState.Success, + data: response.data + }; + } catch (err) { + return { + state: AsyncState.Failure, + data: err.message + }; + } + }; + + static postRequestTransfer = async ( + transferRequest: IActivityTransferRequestDto + ): Promise> => { + try { + const url = Configuration.PrefixIncentiveUrl( + Configuration.Get.urls.incentiveApi.postActivityTransferRequest + ); + + const response = await Axios.post(url, transferRequest, { + withCredentials: true + }); + + return { state: AsyncState.Success, data: response.data }; + } catch (err) { + return { + state: AsyncState.Failure, + data: err.message + }; + } + }; + + static postRequestTransferToOwnDepartment = async ( + transferRequest: IActivityTransferRequestDto + ): Promise> => { + try { + const url = Configuration.PrefixIncentiveUrl( + Configuration.Get.urls.incentiveApi + .postActivityTransferToOwnDepartmentRequest + ); + + const response = await Axios.post(url, transferRequest, { + withCredentials: true + }); + + return { state: AsyncState.Success, data: response.data }; + } catch (err) { + return { + state: AsyncState.Failure, + data: err.message + }; + } + }; + + static getRequestedTransferAction = async ( + requestedActivityId: number + ): Promise> => { + try { + const url = Configuration.PrefixIncentiveUrl( + Configuration.Get.urls.incentiveApi.getRequestedTransferAction + ).replace("{activityId}", requestedActivityId.toString()); + + const response = await Axios.get(url, { withCredentials: true }); + + return { state: AsyncState.Success, data: response.data }; + } catch (err) { + return { + state: AsyncState.Failure, + data: err.message + }; + } + }; + + static getInputCSSClassList = ( + originalValue: Product2, + newValue: Product2 + ) => { + if (originalValue == newValue) return "form-control"; + + if (newValue === undefined) { + return "form-control is-invalid"; + } + return "form-control is-valid"; + }; + + static postApproveTransfer = async ( + approveRequest: IActivityTransferApprovalRequestDto + ): Promise> => { + try { + const url = Configuration.PrefixIncentiveUrl( + Configuration.Get.urls.incentiveApi.postApproveTransfer + ); + + const response = await Axios.post(url, approveRequest, { + withCredentials: true + }); + + return { state: AsyncState.Success, data: response.data }; + } catch (err) { + return { + state: AsyncState.Failure, + data: err.message + }; + } + }; + + static getValidActivityTypesForProduct( + product?: IProductDetail + ): ActivityType[] { + return Object.values(ActivityType).filter( + (activityType) => + product === undefined || + (product.isSellable && activityType === ActivityType.Sale) || + (product.isReferrable && + activityType === ActivityType.Referral) || + (product.isNonPaying && activityType === ActivityType.Other) + ); + } + + static isPendingReview = (activity: IActivity) => { + return ( + activity.state === ActivityState.ManualReview || + activity.state === ActivityState.PendingReferral + ); + }; + + static approvalHasBeenDecided = (activity: IActivity) => { + return ( + activity.state === ActivityState.Accepted || + activity.state === ActivityState.Rejected + ); + }; + + static referralApproversCanDecide = (activity: IActivity) => { + return ( + activity.type === ActivityType.Referral && + (ActivityService.isPendingReview(activity) || + ActivityService.approvalHasBeenDecided(activity)) + ); + }; + + static saleApproversCanDecide = (activity: IActivity) => { + return ( + (activity.type === ActivityType.Referral || + activity.type === ActivityType.Sale) && + ActivityService.isPendingReview(activity) + ); + }; + + static isEligbleForTransfer(activity: IActivity) { + return ( + activity.activityPayouts.length === 0 && + activity.state && + [ActivityState.Accepted].includes(activity.state) + ); + } + + static isAccountStatusFieldRequired(formState: IManualActivityFormState) { + return ( + (formState.product === Product2.CreditCard.code && + formState.type === ActivityType.Referral) || + formState.product === Product2.Mortgage.code || + (formState.product === Product2.NewChecking.code && + formState.type === ActivityType.Sale) || + formState.product === Product2.Vehicle.code + ); + } + + // Account new balance field was deprecated, but this is what was in the + // spreadsheet... GJF, #26637 + static isAccountNewBalanceFieldRequired( + formState: IManualActivityFormState + ) { + return ( + (formState.product === Product2.CreditCard.code && + formState.type === ActivityType.Referral) || + (formState.product === Product2.CreditCardBalanceTransfer.code && + formState.type === ActivityType.Referral) || + formState.product === Product2.Mortgage.code || + (formState.product === Product2.NewChecking.code && + formState.type === ActivityType.Sale) + ); + } + + static isAccountBalanceFieldRequired(formState: IManualActivityFormState) { + return ( + formState.product === Product2.Mortgage.code || + (formState.product === Product2.NewChecking.code && + formState.type === ActivityType.Sale) || + formState.product === Product2.Vehicle.code + ); + } + + // #26496 - Account Number shouldn't be required for referrals + static isAccountNumberFieldRequired(formState: IManualActivityFormState) { + return formState.type === ActivityType.Sale; + } + + static isSourceIdFieldVisible(formState: IManualActivityFormState) { + return ( + formState.product === Product2.LIC.code && + formState.type === ActivityType.Referral + ); + } + + static isValidManualActivity(formState: IManualActivityFormState) { + const basicFieldsPresent = !!( + formState.state && + formState.sourceSystem && + formState.sourceCreated && + formState.product && + formState.type && + formState.departmentId && + formState.employeeId && + formState.accepted && + (formState.detailSubjectPersonNbr || formState.detailSubjectOrgNbr) + ); + const accountStatusValid = !!( + formState.detailAccountStatus || + !this.isAccountStatusFieldRequired(formState) + ); + const accountBalanceValid = !!( + formState.detailAccountBalance || + !this.isAccountBalanceFieldRequired(formState) + ); + const isSourceIdFieldValid = !!( + formState.sourceId || !this.isSourceIdFieldVisible(formState) + ); + return ( + basicFieldsPresent && + accountStatusValid && + accountBalanceValid && + isSourceIdFieldValid + ); + } +} + +export const mapActivityFormStateToDTO = ( + formData: IManualActivityFormState +): IActivityDTO => { + if ( + !formData.product || + !formData.type || + !formData.sourceSystem || + !formData.departmentId || + !formData.employeeId + ) { + throw new Error(MISSING_REQUIRED_FIELD_MESSAGE); + } + + return { + product: formData.product, + type: formData.type, + sourceSystem: formData.sourceSystem, + sourceId: formData.sourceId, + sourceCreated: formData.sourceCreated?.isValid() + ? formData.sourceCreated + : undefined, + accepted: formData.accepted?.isValid() ? formData.accepted : undefined, + state: formData.state, + departmentId: formData.departmentId, + employeeId: formData.employeeId, + detail: { + accountBalance: formData.detailAccountBalance, + accountNumber: + formData.detailAccountNumber === "" + ? undefined + : formData.detailAccountNumber, + subAccountNumber: + formData.detailSubAccountNumber === "" + ? undefined + : formData.detailSubAccountNumber, + memberName: + formData.detailMemberName === "" + ? undefined + : formData.detailMemberName, + subjectPersonNbr: + formData.detailSubjectPersonNbr === "" + ? undefined + : formData.detailSubjectPersonNbr, + accountStatus: + formData.detailAccountStatus === "" + ? undefined + : formData.detailAccountStatus, + isDebtProtectionEligible: formData.detailIsDebtProtectionEligible, + debtProtectionAddDate: formData.detailDebtProtectionAddDate, + subjectOrgNbr: + formData.detailSubjectOrgNbr === "" + ? undefined + : formData.detailSubjectOrgNbr + } + }; +}; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/shared/services/CommissionService.test.ts b/LCU.WebApps.Operations.IncentiveManager/src/shared/services/CommissionService.test.ts new file mode 100644 index 0000000..d115ed4 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/shared/services/CommissionService.test.ts @@ -0,0 +1,81 @@ +import axios from "axios"; +import moment from "moment"; +import AsyncState from "../enumerations/AsyncState"; +import Product2 from "../enumerations/Product"; +import { UserType } from "../models/Auditable"; +import { IAuditableCommission } from "../models/Commission"; +import CommissionService from "./CommissionService"; + +jest.mock("axios"); + +describe("CommissionService", () => { + it("Returns data from the get response", async () => { + const mockReturnData: IAuditableCommission[] = [ + { + id: 1, + created: moment("2020-07-12"), + createdBy: "Dev", + createdByType: UserType.Associate, + lastUpdated: moment("2020-07-13"), + lastUpdatedBy: "Dev2", + lastUpdatedByType: UserType.Associate, + product: Product2.ATMCard, + ratePerProduct: 2, + role: { + name: "Teller", + description: "Customer service", + permissions: [] + } + } + ]; + + (axios.get as jest.Mock).mockResolvedValue({ + status: 200, + data: mockReturnData + }); + + const results = await CommissionService.getAllCommissions(); + + expect(results).toEqual({ + state: AsyncState.Success, + data: mockReturnData + }); + }); + + it("Returns failure state on get reponse error", async () => { + (axios.get as jest.Mock).mockImplementation(() => { + throw new Error(); + }); + + const results = await CommissionService.getAllCommissions(); + + expect(results).toEqual({ + state: AsyncState.Failure + }); + }); + + it("Returns success from the put call", async () => { + (axios.put as jest.Mock).mockResolvedValue({ + status: 204 + }); + + const results = await CommissionService.setCommissions([]); + + expect(results).toEqual({ + state: AsyncState.Success + }); + }); + + it("Returns failure from the put call", async () => { + (axios.put as jest.Mock).mockImplementation(() => { + throw new Error("Service Error"); + }); + + const results = await CommissionService.setCommissions([]); + + expect(results).toEqual({ + state: AsyncState.Failure, + data: "Service Error" + }); + }); +}); diff --git a/LCU.WebApps.Operations.IncentiveManager/src/shared/services/CommissionService.ts b/LCU.WebApps.Operations.IncentiveManager/src/shared/services/CommissionService.ts new file mode 100644 index 0000000..d65fade --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/shared/services/CommissionService.ts @@ -0,0 +1,80 @@ +import Axios from "axios"; +import AsyncState from "../enumerations/AsyncState"; +import { + IAuditableCommission, + ICommissionRateChange +} from "../models/Commission"; +import IAsyncStateContainer from "../types/IAsyncStateContainer"; +import Configuration from "./../configuration/Configuration"; + +export default class CommissionService { + static getAllCommissions = async (): Promise< + IAsyncStateContainer + > => { + try { + const url = Configuration.PrefixIncentiveUrl( + Configuration.Get.urls.incentiveApi.getAllCommissions + ); + + const res = await Axios.get(url, { withCredentials: true }); + + return { + state: AsyncState.Success, + data: res.data + }; + } catch (err) { + return { + state: AsyncState.Failure + }; + } + }; + + static setCommissions = async ( + commissions: ICommissionRateChange[] + ): Promise> => { + try { + const url = Configuration.PrefixIncentiveUrl( + Configuration.Get.urls.incentiveApi.getAllCommissions + ); + + await Axios.put(url, commissions, { + withCredentials: true + }); + + return { + state: AsyncState.Success + }; + } catch (err) { + if (err.response?.status === 409) { + return { + state: AsyncState.Invalid, + data: err.message + }; + } + return { + state: AsyncState.Failure, + data: err.message + }; + } + }; + + static mapToChange = ( + commissions: IAuditableCommission[] + ): ICommissionRateChange[] => { + return commissions.map((commission) => ({ + id: commission.id, + product: commission.product, + ratePerProduct: commission.ratePerProduct, + role: commission.role + })); + }; + + static isValidRateChange = (change: ICommissionRateChange) => { + return ( + change.ratePerProduct >= 0 && + change.id >= 0 && + change.product && + change.role + ); + }; +} diff --git a/LCU.WebApps.Operations.IncentiveManager/src/shared/services/CookieService.ts b/LCU.WebApps.Operations.IncentiveManager/src/shared/services/CookieService.ts new file mode 100644 index 0000000..ebddddd --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/shared/services/CookieService.ts @@ -0,0 +1,47 @@ +import { IMemberSecurityMeta } from "../types/IMemberSecurityMeta"; + +class CookieService { + public static readonly PUBLIC_COOKIE_KEY = "TokenAuthorization"; + + public static hasAuthCookie(): boolean { + return document.cookie.indexOf(this.PUBLIC_COOKIE_KEY) > -1; + } + + public static getCookieValue(cookieName: string): string { + const name = cookieName + "="; + const decodedCookie = decodeURIComponent(document.cookie); + const cookieArray = decodedCookie.split(";"); + for (let i = 0; i < cookieArray.length; i++) { + let cookie = cookieArray[i]; + while (cookie.charAt(0) === " ") { + cookie = cookie.substring(1); + } + if (cookie.indexOf(name) === 0) { + return cookie.substring(name.length, cookie.length); + } + } + return ""; + } + + public static getUserAuth(): IMemberSecurityMeta { + const rawCookie = this.getCookieValue(this.PUBLIC_COOKIE_KEY); + + if (rawCookie) { + const unencoded = atob(rawCookie); + const parsed = JSON.parse(unencoded); + const MemberSecurityMeta = { + isTwoFactor: parsed.IsTwoFactor, + isMember: parsed.IsMember, + isIndividual: parsed.IsIndividual + }; + return MemberSecurityMeta; + } + return { + isTwoFactor: false, + isMember: false, + isIndividual: false + }; + } +} + +export default CookieService; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/shared/services/DateTimeService.ts b/LCU.WebApps.Operations.IncentiveManager/src/shared/services/DateTimeService.ts new file mode 100644 index 0000000..e425783 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/shared/services/DateTimeService.ts @@ -0,0 +1,23 @@ +import moment, { Moment } from "moment"; +class DateTimeService { + private static CastClientTimeToServerTime = (dateTime: Moment): Moment => { + //Is Local Flag is set when moment is initiated. We will probably have to get into the habit + //of using moment("string").local() + if (dateTime.isLocal()) { + return dateTime.utc(); + } + return dateTime; + }; + + private static CastServerTimeToClientTime = (dateTime: Moment): Moment => { + return moment(dateTime).utc().local(); + }; + + public static CastServerTimeToClientTimeString = ( + dateTime?: Moment + ): string => { + return moment.utc(dateTime).local().format("ll"); + }; +} + +export default DateTimeService; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/shared/services/DeadActivityService.ts b/LCU.WebApps.Operations.IncentiveManager/src/shared/services/DeadActivityService.ts new file mode 100644 index 0000000..9005266 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/shared/services/DeadActivityService.ts @@ -0,0 +1,146 @@ +import Configuration from "../configuration/Configuration"; +import AsyncState from "../enumerations/AsyncState"; +import { + IAuditableDeadActivity, + IDeadActivitySearchForm +} from "../models/DeadActivity"; +import IAsyncStateContainer from "../types/IAsyncStateContainer"; +import IPagedResponse from "../types/IPagedResponse"; +import Axios from "axios"; + +export default class DeadActivityService { + static getPaginatedDeadActivities = async ( + page: number, + limit: number, + filters: Partial + ): Promise< + IAsyncStateContainer> + > => { + try { + const url = Configuration.PrefixIncentiveUrl( + Configuration.Get.urls.incentiveApi.getAllDeadActivitiesPaged + ); + + // I wanted to just do ...filters, but the moment.tostring adds quotes! + const params = { + Limit: limit, + Page: page + } as any; + + if (filters.state) { + params.state = filters.state; + } + + if (filters.employeeId) { + params.employeeId = filters.employeeId; + } + + if (filters.departmentId) { + params.departmentId = filters.departmentId; + } + + if (filters.query) { + params.query = filters.query; + } + + if (filters.product) { + params.product = filters.product; + } + + if (filters.type) { + params.type = filters.type; + } + + if (filters.sortDescending) { + params.sortDescending = filters.sortDescending; + } + + if (filters.sortBy) { + params.sortBy = filters.sortBy; + } + + if (filters.dateLow && filters.dateLow.isValid()) { + params.dateLow = filters.dateLow.format("MM/DD/YYYY"); + } + + if (filters.dateHigh && filters.dateHigh.isValid()) { + params.dateHigh = filters.dateHigh.format("MM/DD/YYYY"); + } + + const res = await Axios.get(url, { + withCredentials: true, + params: params + }); + + return { + state: AsyncState.Success, + data: res.data + }; + } catch (err) { + if (err.response?.status == 404) { + return { + state: AsyncState.NotFound + }; + } + return { + state: AsyncState.Failure + }; + } + }; + + static getPaginatedDeadActivitiesFromUrl = async ( + url: string + ): Promise< + IAsyncStateContainer> + > => { + // For some reason API is not returning URL with basename (/incentive/). + // This could be similar to the issue we had with Swagger. + // We can just take the query and append it to the correct url for now + // This should always work, even if the url is fixed. + // Note for prod "/api" appears twice, you must split on "/api/" + const newUrl = Configuration.PrefixIncentiveUrl( + "/" + url.split("/api/")[1] + ); + try { + const res = await Axios.get(newUrl, { withCredentials: true }); + + return { + state: AsyncState.Success, + data: res.data + }; + } catch (err) { + if (err.response?.status == 404) { + return { + state: AsyncState.NotFound + }; + } + return { + state: AsyncState.Failure + }; + } + }; + + static getDeadActivity = async ( + activityId: number + ): Promise> => { + try { + const url = Configuration.PrefixIncentiveUrl( + `${Configuration.Get.urls.incentiveApi.getDeadActivity}/${activityId}` + ); + + const response = await Axios.get(url, { + withCredentials: true + }); + + return { + state: AsyncState.Success, + data: response.data + }; + } catch (err) { + return { + state: AsyncState.Failure, + data: err.message + }; + } + }; +} diff --git a/LCU.WebApps.Operations.IncentiveManager/src/shared/services/DepartmentService.test.ts b/LCU.WebApps.Operations.IncentiveManager/src/shared/services/DepartmentService.test.ts new file mode 100644 index 0000000..4ca6e21 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/shared/services/DepartmentService.test.ts @@ -0,0 +1,49 @@ +import axios from "axios"; +import DepartmentService from "./DepartmentService"; +import { IDepartmentDetails } from "../models/Department"; +import AsyncState from "../enumerations/AsyncState"; + +jest.mock("axios"); + +describe("DepartmentService", () => { + it("should", async () => { + const returnedDetails: IDepartmentDetails = { + assignments: [], + department: { + businessLineCode: "MEMSRV", + departmentId: 1, + departmentName: "ACH", + id: 1, + state: "Active", + regionCode: "NORTH" + } + }; + (axios.get as jest.Mock).mockResolvedValue({ + status: 200, + data: returnedDetails + }); + + const result = await DepartmentService.getDepartmentDetails(1); + + expect(result).toEqual({ + state: AsyncState.Success, + data: returnedDetails + }); + expect(axios.get).toHaveBeenCalledTimes(1); + expect(axios.get).toBeCalledWith(expect.anything(), { + withCredentials: true + }); + }); + + it("should return failure if axios error", async () => { + (axios.get as jest.Mock).mockImplementation(() => { + throw new Error("Service Error"); + }); + const result = await DepartmentService.getDepartmentDetails(1); + + expect(result).toEqual({ + state: AsyncState.Failure, + data: "Service Error" + }); + }); +}); diff --git a/LCU.WebApps.Operations.IncentiveManager/src/shared/services/DepartmentService.ts b/LCU.WebApps.Operations.IncentiveManager/src/shared/services/DepartmentService.ts new file mode 100644 index 0000000..13c3375 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/shared/services/DepartmentService.ts @@ -0,0 +1,158 @@ +import Axios from "axios"; +import Configuration from "../configuration/Configuration"; +import AsyncState from "../enumerations/AsyncState"; +import { + IDepartment, + IDepartmentDetails, + IDepartmentDto +} from "../models/Department"; +import { IDepartmentAssignment } from "../models/DepartmentAssignment"; +import IAsyncStateContainer from "../types/IAsyncStateContainer"; +import moment from "moment"; + +export default class DepartmentService { + static isDepartmentNameUnique = ( + departments: IDepartment[], + deptName?: string + ) => { + if (deptName === undefined) return true; + return !departments + .map((dept) => dept.departmentName.toLowerCase()) + .includes(deptName.toLowerCase()); + }; + + static isActiveAssignment(userDept: IDepartmentAssignment): unknown { + const endOfMonth = moment().utc().endOf("month"); + const started = moment + .utc(userDept.startDate) + .isSameOrBefore(moment().utc()); + const hasEndDate = !!userDept.endDate; + const hasEnded = moment + .utc(userDept.endDate) + .isSameOrBefore(endOfMonth); + return started && (!hasEndDate || !hasEnded); + } + + static toTitleCase(str: string) { + // Yep, make LIC a special case..... + if (str && str.toUpperCase().trim() === "LIC") { + return "LIC"; + } + return str.replace(/\w\S*/g, function (txt) { + return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase(); + }); + } + + static fixDeptNameCases = (departments: IDepartment[]): IDepartment[] => { + if (departments && departments.length > 0) { + departments.forEach( + (department) => + (department.departmentName = DepartmentService.toTitleCase( + department.departmentName + )) + ); + } + return departments; + }; + + static getAllDepartments = async ( + canEditDepartmentGoals?: boolean, + canEditPersonnelGoals?: boolean + ): Promise> => { + try { + const url = Configuration.PrefixIncentiveUrl( + Configuration.Get.urls.incentiveApi.getAllDepartments + ); + + const params: any = {}; + if (canEditDepartmentGoals != undefined) { + params.canEditDepartmentGoals = canEditDepartmentGoals; + } + if (canEditPersonnelGoals != undefined) { + params.canEditPersonnelGoals = canEditPersonnelGoals; + } + const res = await Axios.get(url, { + withCredentials: true, + params: params + }); + + return { + state: AsyncState.Success, + data: DepartmentService.fixDeptNameCases(res.data) + }; + } catch (err) { + return { + state: AsyncState.Failure + }; + } + }; + + static updateExistingDepartment = async ( + departmentId: number, + newDepartment: IDepartmentDto + ) => { + try { + const url = Configuration.PrefixIncentiveUrl( + Configuration.Get.urls.incentiveApi.getAllDepartments, + "/" + departmentId + ); + + const res = await Axios.put(url, newDepartment, { + withCredentials: true + }); + + return { + state: AsyncState.Success, + data: res.data + }; + } catch (err) { + return { + state: AsyncState.Failure + }; + } + }; + + static submitNewDepartment = async (newDepartment: IDepartmentDto) => { + try { + const url = Configuration.PrefixIncentiveUrl( + Configuration.Get.urls.incentiveApi.getAllDepartments + ); + + const res = await Axios.post(url, newDepartment, { + withCredentials: true + }); + + return { + state: AsyncState.Success, + data: res.data + }; + } catch (err) { + return { + state: AsyncState.Failure + }; + } + }; + + static getDepartmentDetails = async ( + departmentId: number + ): Promise> => { + try { + let url = Configuration.PrefixIncentiveUrl( + Configuration.Get.urls.incentiveApi.getDepartmentDetails + ); + url = url.replace("{departmentId}", departmentId.toString()); + + const res = await Axios.get(url, { withCredentials: true }); + + return { + state: AsyncState.Success, + data: res.data + }; + } catch (err) { + return { + state: AsyncState.Failure, + data: err.message + }; + } + }; +} diff --git a/LCU.WebApps.Operations.IncentiveManager/src/shared/services/EmployeeService.test.ts b/LCU.WebApps.Operations.IncentiveManager/src/shared/services/EmployeeService.test.ts new file mode 100644 index 0000000..1a20d9a --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/shared/services/EmployeeService.test.ts @@ -0,0 +1,146 @@ +import axios from "axios"; +import moment from "moment"; +import Translate from "../../translate/Translate"; +import AsyncState from "../enumerations/AsyncState"; +import EmployeeService from "./EmployeeService"; +import { + IDepartmentAssignmentDto, + IDepartmentAssignmentUpdateDto +} from "../models/DepartmentAssignment"; + +jest.mock("axios"); + +describe("EmployeeService", () => { + it.each([400, 401, 403, 404])( + "assignNewDepartment returns failure from a %i response", + async (statusCode) => { + (axios.post as jest.Mock).mockRejectedValue({ + response: { + status: statusCode, + data: `${statusCode} Message from server` + } + }); + + const assignment: IDepartmentAssignmentDto = { + departmentId: 1, + role: "Teller", + slidingPayout: 100, + startDate: moment().utc() + }; + + const result = await EmployeeService.assignNewDepartment( + assignment, + "12345" + ); + expect(result).toEqual({ + state: AsyncState.Failure, + data: `${statusCode} Message from server` + }); + } + ); + + it("assignNewDepartment returns failure translate text if unknown error occurs", async () => { + (axios.post as jest.Mock).mockRejectedValue({ + response: { + status: 500 + } + }); + + const assignment: IDepartmentAssignmentDto = { + departmentId: 1, + role: "Teller", + slidingPayout: 100, + startDate: moment().utc() + }; + + const result = await EmployeeService.assignNewDepartment( + assignment, + "12345" + ); + expect(result).toEqual({ + state: AsyncState.Failure, + data: + Translate.Get.containers.employeePage.deptAssignment + .deptAssignmentAlertBar.failure + }); + }); + + it.each([400, 401, 403, 404])( + "endDepartmentAssignment returns failure from a %i response", + async (statusCode) => { + (axios.post as jest.Mock).mockRejectedValue({ + response: { + status: statusCode, + data: `${statusCode} Message from server` + } + }); + + const assignment: IDepartmentAssignmentUpdateDto = { + employeeId: 1, + assignmentId: 1, + role: "Teller", + slidingPayout: 100 + }; + + const result = await EmployeeService.updateDepartmentAssignment( + assignment + ); + + expect(result).toEqual({ + state: AsyncState.Failure, + data: `${statusCode} Message from server` + }); + } + ); + + it("endDepartmentAssignment returns failure translate text if unknown error occurs", async () => { + (axios.post as jest.Mock).mockRejectedValue({ + response: { + status: 500 + } + }); + + const assignment: IDepartmentAssignmentUpdateDto = { + employeeId: 1, + assignmentId: 1, + role: "Teller", + slidingPayout: 100 + }; + + const result = await EmployeeService.updateDepartmentAssignment( + assignment + ); + + expect(result).toEqual({ + state: AsyncState.Failure, + data: + Translate.Get.containers.employeePage.deptAssignment + .deptAssignmentAlertBar.failure + }); + }); + + it("valid department update", () => { + const assignment: IDepartmentAssignmentUpdateDto = { + employeeId: 1, + assignmentId: 1, + role: "Teller", + slidingPayout: 100 + }; + + let result = EmployeeService.isValidDepartmentAssignmentUpdate( + assignment + ); + expect(result).toEqual(true); + + assignment.employeeId = 0; + assignment.assignmentId = 0; + result = EmployeeService.isValidDepartmentAssignmentUpdate(assignment); + expect(result).toEqual(false); + + expect( + EmployeeService.isValidDepartmentAssignmentUpdate( + {} as IDepartmentAssignmentUpdateDto + ) + ).toEqual(false); + }); +}); diff --git a/LCU.WebApps.Operations.IncentiveManager/src/shared/services/EmployeeService.ts b/LCU.WebApps.Operations.IncentiveManager/src/shared/services/EmployeeService.ts new file mode 100644 index 0000000..db2a6c4 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/shared/services/EmployeeService.ts @@ -0,0 +1,189 @@ +import IAsyncStateContainer from "../types/IAsyncStateContainer"; +import { IAdditionalPermissionRequestDto, IEmployee } from "../models/Employee"; +import AsyncState from "./../enumerations/AsyncState"; +import Configuration from "./../configuration/Configuration"; +import Axios from "axios"; +import { IPermission } from "../models/Settings"; +import Translate from "../../translate/Translate"; +import { + IDepartmentAssignmentDto, + IDepartmentAssignmentUpdateDto +} from "../models/DepartmentAssignment"; + +const MISSING_REQUIRED_FIELD_MESAGE = + "StartDate and DepartmentId must be assigned"; + +const containersTranslate = Translate.Get.containers; + +class EmployeeService { + public static getByEmployeeId = async ( + employeeId: string + ): Promise> => { + try { + const url = Configuration.PrefixIncentiveUrl( + Configuration.Get.urls.incentiveApi.getEmployeeByEmployeeId, + employeeId + ); + const res = await Axios.get(url, { withCredentials: true }); + + return { + state: AsyncState.Success, + data: res.data + }; + } catch (err) { + return { + state: AsyncState.Failure + }; + } + }; + + public static assignNewDepartment = async ( + assignmentData: Partial, + employeeId: string + ): Promise> => { + try { + const url = Configuration.PrefixIncentiveUrl( + Configuration.Get.urls.incentiveApi.postNewDepartmentAssignment + ).replace("{employeeId}", employeeId); + const body = mapDepartmentAssignmentDtoToBody(assignmentData); + await Axios.post(url, body, { withCredentials: true }); + return { + state: AsyncState.Success, + data: assignmentData.endDate?.toString() ?? "" + }; + } catch (err) { + if ( + err.response?.status === 400 || + err.response?.status === 401 || + err.response?.status === 403 || + err.response?.status === 404 + ) { + return { + state: AsyncState.Failure, + data: err.response.data + }; + } + return { + state: AsyncState.Failure, + data: + err?.response.data ?? + err?.message ?? + containersTranslate.employeePage.deptAssignment + .deptAssignmentAlertBar.failure + }; + } + }; + + public static isValidDepartmentAssignmentUpdate = ( + assignment: IDepartmentAssignmentUpdateDto + ): boolean => { + if (assignment) { + return !!assignment.assignmentId && !!assignment.employeeId; + } + return false; + }; + + public static updateDepartmentAssignment = async ( + update: IDepartmentAssignmentUpdateDto + ): Promise> => { + try { + const url = Configuration.PrefixIncentiveUrl( + Configuration.Get.urls.incentiveApi.updateDepartmentAssignment + ) + .replace("{employeeId}", update.employeeId.toString()) + .replace("{assignmentId}", update.assignmentId.toString()); + + //Note: Converting update to data object to prevent axios from converting endDate to utc - last minute change + const data = { + assignmentId: update.assignmentId, + employeeId: update.employeeId, + endDate: update.endDate?.isValid() + ? update.endDate.format("YYYY-MM-DD HH:mm:ss") + : null, + slidingPayout: update.slidingPayout, + role: update.role + }; + + const response = await Axios.post(url, data, { + withCredentials: true + }); + return { + state: AsyncState.Success, + data: response.data + }; + } catch (err) { + if ( + err.response?.status === 400 || + err.response?.status === 401 || + err.response?.status === 403 || + err.response?.status === 404 + ) { + return { + state: AsyncState.Failure, + data: err.response.data + }; + } + return { + state: AsyncState.Failure, + data: + err?.response.data ?? + err?.message ?? + containersTranslate.employeePage.deptAssignment + .deptAssignmentAlertBar.failure + }; + } + }; + + static updatePermissions = async ( + permCodesToAdd: string[], + permCodesToRemove: string[], + employeeId: string + ): Promise> => { + try { + let url = Configuration.PrefixIncentiveUrl( + Configuration.Get.urls.incentiveApi.putPermissionsUpdate + ); + url = url.replace("{employeeId}", employeeId); + + const body: IAdditionalPermissionRequestDto = { + addPermCodes: permCodesToAdd, + removePermCodes: permCodesToRemove + }; + + const response = await Axios.put(url, body, { + withCredentials: true + }); + + return { + state: AsyncState.Success, + data: response.data + }; + } catch (err) { + return { + state: AsyncState.Failure, + data: err?.message + }; + } + }; +} + +const mapDepartmentAssignmentDtoToBody = ( + assignment: Partial +): IDepartmentAssignmentDto => { + if ( + assignment.departmentId === undefined || + assignment.startDate === undefined + ) { + throw new Error(MISSING_REQUIRED_FIELD_MESAGE); + } + try { + return { + ...assignment, + endDate: assignment.endDate?.endOf("month") + } as IDepartmentAssignmentDto; + } catch (err) { + throw new Error(MISSING_REQUIRED_FIELD_MESAGE); + } +}; + +export default EmployeeService; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/shared/services/ExportCsvService.test.ts b/LCU.WebApps.Operations.IncentiveManager/src/shared/services/ExportCsvService.test.ts new file mode 100644 index 0000000..2d14e4a --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/shared/services/ExportCsvService.test.ts @@ -0,0 +1,234 @@ +import FileSaver from "file-saver"; +import { PerProductGoalType } from "../models/Goal"; +import ExportCsvService, { + MonthlyPersonnelGoalsCsvData +} from "./ExportCsvService"; + +jest.mock("file-saver", () => ({ saveAs: jest.fn() })); + +describe("ExportCsvService", () => { + it("should call saveAs with annual department goal data", () => { + const blobSpy = jest + .spyOn(global, "Blob") + .mockImplementationOnce(() => ({} as Blob)); + const data = testDepartmentGoals(); + + ExportCsvService.exportAnnualDepartmentGoalsAsCSV(data); + + expect(blobSpy).toHaveBeenCalledWith( + [ + // eslint-disable-next-line @typescript-eslint/quotes + 'Month,New Checking,Credit Card,GAPInsurance,LIC,Mortgage,Vehicle\r\n"Jan",11,12,15,16,17,18\r\n"Feb",21,22,25,26,27,28\r\n"Mar",1,2,5,6,7,8\r\n"Apr",1,2,5,6,7,8\r\n"May",1,2,5,6,7,8\r\n"Jun",1,2,5,6,7,8\r\n"Jul",1,2,5,6,7,8\r\n"Aug",1,2,5,6,7,8\r\n"Sep",1,2,5,6,7,8\r\n"Oct",1,2,5,6,7,8\r\n"Nov",1,2,5,6,7,8\r\n"Dec",1,2,5,6,7,8\r\n"Totals",100,200,500,600,700,800' + ], + { + type: "text/csv" + } + ); + expect(FileSaver.saveAs).toHaveBeenCalledTimes(1); + expect(FileSaver.saveAs).toHaveBeenCalledWith( + {} as Blob, + "AnnualDepartmentGoals.csv" + ); + }); + + it("should call saveAs with monthly personnel goal data", () => { + const blobSpy = jest + .spyOn(global, "Blob") + .mockImplementationOnce(() => ({} as Blob)); + + const data = testMonthlyPersonnelGoalsCsvData(); + + ExportCsvService.exportMonthlyPersonnelGoalsAsCSV(data); + + expect(blobSpy).toHaveBeenCalledWith( + [ + // eslint-disable-next-line @typescript-eslint/quotes + 'Personnel,New Checking,Credit Card,GAPInsurance,LIC,Mortgage,Vehicle\r\n"Department Goals",1,2,5,6,7,8\r\n"testing1@landmarkcu.com",11,12,15,16,17,18\r\n"testing2@landmarkcu.com",21,22,25,26,27,28\r\n"Totals",2,4,10,6,7,8' + ], + { + type: "text/csv" + } + ); + expect(FileSaver.saveAs).toHaveBeenCalledTimes(1); + expect(FileSaver.saveAs).toHaveBeenCalledWith( + {} as Blob, + "MonthlyPersonnelGoals.csv" + ); + }); +}); + +const testDepartmentGoals = (): PerProductGoalType[] => [ + { + NewChecking: 11, + CreditCard: 12, + GAPInsurance: 15, + LIC: 16, + Mortgage: 17, + Vehicle: 18 + }, + { + NewChecking: 21, + CreditCard: 22, + GAPInsurance: 25, + LIC: 26, + Mortgage: 27, + Vehicle: 28 + }, + { + NewChecking: 1, + CreditCard: 2, + GAPInsurance: 5, + LIC: 6, + Mortgage: 7, + Vehicle: 8 + }, + { + NewChecking: 1, + CreditCard: 2, + GAPInsurance: 5, + LIC: 6, + Mortgage: 7, + Vehicle: 8 + }, + { + NewChecking: 1, + CreditCard: 2, + GAPInsurance: 5, + LIC: 6, + Mortgage: 7, + Vehicle: 8 + }, + { + NewChecking: 1, + CreditCard: 2, + GAPInsurance: 5, + LIC: 6, + Mortgage: 7, + Vehicle: 8 + }, + { + NewChecking: 1, + CreditCard: 2, + DebtProtectionLife: 3, + DebtProtectionDisability: 4, + GAPInsurance: 5, + LIC: 6, + Mortgage: 7, + Vehicle: 8 + }, + { + NewChecking: 1, + CreditCard: 2, + DebtProtectionLife: 3, + DebtProtectionDisability: 4, + GAPInsurance: 5, + LIC: 6, + Mortgage: 7, + Vehicle: 8 + }, + { + NewChecking: 1, + CreditCard: 2, + DebtProtectionLife: 3, + DebtProtectionDisability: 4, + GAPInsurance: 5, + LIC: 6, + Mortgage: 7, + Vehicle: 8 + }, + { + NewChecking: 1, + CreditCard: 2, + DebtProtectionLife: 3, + DebtProtectionDisability: 4, + GAPInsurance: 5, + LIC: 6, + Mortgage: 7, + Vehicle: 8 + }, + { + NewChecking: 1, + CreditCard: 2, + DebtProtectionLife: 3, + DebtProtectionDisability: 4, + GAPInsurance: 5, + LIC: 6, + Mortgage: 7, + Vehicle: 8 + }, + { + NewChecking: 1, + CreditCard: 2, + DebtProtectionLife: 3, + DebtProtectionDisability: 4, + GAPInsurance: 5, + LIC: 6, + Mortgage: 7, + Vehicle: 8 + }, + { + NewChecking: 100, + CreditCard: 200, + DebtProtectionLife: 300, + DebtProtectionDisability: 400, + GAPInsurance: 500, + LIC: 600, + Mortgage: 700, + Vehicle: 800 + } +]; + +const testMonthlyPersonnelGoalsCsvData = (): MonthlyPersonnelGoalsCsvData[] => [ + { + rowHeader: "Department Goals", + goals: { + NewChecking: 1, + CreditCard: 2, + DebtProtectionLife: 3, + DebtProtectionDisability: 4, + GAPInsurance: 5, + LIC: 6, + Mortgage: 7, + Vehicle: 8 + } + }, + { + rowHeader: "testing1@landmarkcu.com", + goals: { + NewChecking: 11, + CreditCard: 12, + DebtProtectionLife: 13, + DebtProtectionDisability: 14, + GAPInsurance: 15, + LIC: 16, + Mortgage: 17, + Vehicle: 18 + } + }, + { + rowHeader: "testing2@landmarkcu.com", + goals: { + NewChecking: 21, + CreditCard: 22, + DebtProtectionLife: 23, + DebtProtectionDisability: 24, + GAPInsurance: 25, + LIC: 26, + Mortgage: 27, + Vehicle: 28 + } + }, + { + rowHeader: "Totals", + goals: { + NewChecking: 2, + CreditCard: 4, + DebtProtectionLife: 6, + DebtProtectionDisability: 8, + GAPInsurance: 10, + LIC: 6, + Mortgage: 7, + Vehicle: 8 + } + } +]; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/shared/services/ExportCsvService.ts b/LCU.WebApps.Operations.IncentiveManager/src/shared/services/ExportCsvService.ts new file mode 100644 index 0000000..7ce72dd --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/shared/services/ExportCsvService.ts @@ -0,0 +1,180 @@ +import { saveAs } from "file-saver"; +import Translate from "../../translate/Translate"; +import { PerProductGoalType } from "../models/Goal"; +import GoalService from "./GoalService"; +import { + IAccountingSummary, + IDepartmentLineItem, + IPayoutEntry +} from "../models/Payout"; +import moment from "moment"; + +export default class ExportCsvService { + private static buildCSV( + data: any, + fileName: string, + skipStringify = false + ) { + const csvArray = this.constructCSVData(data, skipStringify); + const blob = new Blob([csvArray], { type: "text/csv" }); + saveAs(blob, fileName + ".csv"); + } + + private static constructCSVData( + data: any, + skipStringify?: boolean + ): string { + const replacer = (key: any, value: any) => + value === null ? "" : value; + const header = Object.keys(data[0]); + let csv; + if (skipStringify === true) { + csv = data.map((row: { [x: string]: any }) => + header.map((fieldName) => row[fieldName]).join(",") + ); + } else { + csv = data.map((row: { [x: string]: any }) => + header + .map((fieldName) => + JSON.stringify(row[fieldName], replacer) + ) + .join(",") + ); + } + csv.unshift(header.join(",")); + return csv.join("\r\n"); + } + + private static formatAnnualDepartmentGoals( + data: PerProductGoalType[] + ): any { + const months = [...GoalService.Months, "Totals"]; + return data.map((goals: PerProductGoalType, index) => ({ + Month: months[index], + "New Checking": goals.NewChecking, + "Credit Card": goals.CreditCard, + GAPInsurance: goals.GAPInsurance, + LIC: goals.LIC, + Mortgage: goals.Mortgage, + Vehicle: goals.Vehicle + })); + } + + private static formatMonthlyPersonnelGoals( + data: MonthlyPersonnelGoalsCsvData[] + ): any { + return data.map((row) => ({ + Personnel: row.rowHeader, + "New Checking": row.goals.NewChecking, + "Credit Card": row.goals.CreditCard, + GAPInsurance: row.goals.GAPInsurance, + LIC: row.goals.LIC, + Mortgage: row.goals.Mortgage, + Vehicle: row.goals.Vehicle + })); + } + + public static exportAnnualDepartmentGoalsAsCSV(data: PerProductGoalType[]) { + if (data.length > 0) { + this.buildCSV( + this.formatAnnualDepartmentGoals(data), + Translate.Get.containers.goals.department + .departmentGoalsControls.exportFileName + ); + } + } + + public static exportDayforcePayoutCSV(data: IPayoutEntry[]) { + if (data.length > 0) { + this.buildCSV(this.formatPayoutLineItems(data), "payouts"); + } + } + + public static exportAccountingReportCSV(data: IAccountingSummary) { + if (data.departmentPayouts && data.departmentPayouts.length > 0) { + this.buildCSV( + this.formatDepartmentSummaryItems(data.departmentPayouts), + "accountingReport_" + moment(data.payoutPeriod).format("L") + ); + } + } + + private static formatDepartmentSummaryItems( + data: IDepartmentLineItem[] + ): any { + let incentiveSum = 0; + let percentageSum = 0; + + const departmentLineItems = data.map((row) => ({ + Branch: row.departmentName, + Incentive: GoalService.formatMonetary(row.payoutTotal), + "Percent Of Total": row.payoutPercentage + "%" + })); + + data.forEach((row) => (incentiveSum += row.payoutTotal)); + data.forEach((row) => (percentageSum += row.payoutPercentage)); + + departmentLineItems.push({ + Branch: "Totals", + Incentive: GoalService.formatMonetary(incentiveSum), + "Percent Of Total": percentageSum + "%" + }); + + return departmentLineItems; + } + + private static formatPayoutLineItems(data: IPayoutEntry[]): any { + return data.map((row) => ({ + "Edit Set": null, + "Employee Name": row.employeeName || "Unknown Unknown", + "Employee No.": row.employeeId, + Replace: null, + Code: row.code || "Incentive", + "Trailing Taxation Period Start Date": null, + "Trailing Taxation Period End Date": null, + "Hrs.": null, + Rate: null, + Amount: row.payoutTotal, + Position: null, + "Work Location": null, + "Labor %": null, + "Business Date": null, + "Check Template": null, + "Apply ROE Mapping": null, + "FLSA Adjust Start Date": null, + "FLSA Adjust End Date": null, + Comment: null, + "Ordered Amount Type": null, + Project: null, + Docket: null, + Percent: null, + "Pay Out Accruals": null, + "Limit Amount": null, + "Workers Comp Account": null, + "Disposable Earning Amount": null, + "Pay Periods for Tax": null, + "Workers Comp Code": null, + PPN: null, + "Do Not Disburse to Payee": null, + "Labor Metrics": null, + "Debit Arrears": null + })); + } + + public static exportMonthlyPersonnelGoalsAsCSV( + data: MonthlyPersonnelGoalsCsvData[] + ) { + if (data.length > 0) { + this.buildCSV( + this.formatMonthlyPersonnelGoals(data), + Translate.Get.containers.goals.personnel.personnelGoalsControls + .exportFileName + ); + } + } +} + +export interface MonthlyPersonnelGoalsCsvData { + rowHeader: string; + goals: PerProductGoalType; +} diff --git a/LCU.WebApps.Operations.IncentiveManager/src/shared/services/GoalDispersalService.ts b/LCU.WebApps.Operations.IncentiveManager/src/shared/services/GoalDispersalService.ts new file mode 100644 index 0000000..23334f9 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/shared/services/GoalDispersalService.ts @@ -0,0 +1,87 @@ +import { + ICustomWeightedDispersalRequest, + ICustomWeightedDispersalRequestDto, + IGoalDispersalReceipt, + IGoalDispersalRequest, + IRoleDispersalWeight +} from "../models/GoalDispersal"; +import IAsyncStateContainer from "./../types/IAsyncStateContainer"; +import AsyncState from "./../enumerations/AsyncState"; +import Configuration from "./../configuration/Configuration"; +import Axios from "axios"; + +export default class GoalDispersalService { + public static async DisperseGoalsViaExtrapolation( + request: IGoalDispersalRequest + ): Promise> { + try { + const url = Configuration.PrefixIncentiveUrl( + Configuration.Get.urls.incentiveApi.disperseGoalsByExtrapolation + ).replace("{departmentId}", request.departmentId); + + const res = await Axios.post( + url, + { month: request.month, year: request.year }, + { withCredentials: true } + ); + return { + state: AsyncState.Success, + data: res.data + }; + } catch (err) { + if (err.response?.status == 404) { + return { + state: AsyncState.NotFound + }; + } + return { + state: AsyncState.Failure + }; + } + } + + public static async DisperseGoalsByCustomWeights( + request: ICustomWeightedDispersalRequest + ) { + try { + const url = Configuration.PrefixIncentiveUrl( + Configuration.Get.urls.incentiveApi.disperseGoalsByCustomWeights + ); + + const body: ICustomWeightedDispersalRequestDto = { + departmentId: request.departmentId, + month: request.month, + year: request.year, + dispersalProduct: request.dispersalProduct, + dispersalWeightByRole: this.BuildCustomWeightDictionary( + request.percentageByRole + ) + }; + + const res = await Axios.post(url, body, { + withCredentials: true + }); + return { + state: AsyncState.Success, + data: res.data + }; + } catch (err) { + if (err.response?.status == 404) { + return { + state: AsyncState.NotFound + }; + } + return { + state: AsyncState.Failure + }; + } + } + + private static BuildCustomWeightDictionary = ( + roleWeightArray: IRoleDispersalWeight[] + ): { [key: string]: number } => { + const obj: { [key: string]: number } = {}; + roleWeightArray.forEach((x) => (obj[x.roleName] = x.value / 100)); + return obj; + }; +} diff --git a/LCU.WebApps.Operations.IncentiveManager/src/shared/services/GoalService.test.ts b/LCU.WebApps.Operations.IncentiveManager/src/shared/services/GoalService.test.ts new file mode 100644 index 0000000..9728f5f --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/shared/services/GoalService.test.ts @@ -0,0 +1,727 @@ +import axios from "axios"; +import AsyncState from "../enumerations/AsyncState"; +import PermissionCode from "../enumerations/PermissionCode"; +import { IEmployee } from "../models/Employee"; +import { + IDepartmentGoals, + IDepartmentGoalsForRange, + IEmployeeGoals, + IGoalChangeRequest, + IPersonnelGoals +} from "../models/Goal"; +import { IPermission } from "../models/Settings"; +import { IMonthRange } from "../types/IMonthRange"; +import GoalService from "./GoalService"; +import Product2 from "../enumerations/Product"; +import moment from "moment"; + +jest.mock("axios"); + +describe("GoalService - getMyGoals", () => { + it("Returns success with data", async () => { + const range: IMonthRange = { + fromMonth: 1, + fromYear: 2021, + toMonth: 2, + toYear: 2022 + }; + + const empGoals = { + user: { personId: 123 } + } as IEmployeeGoals; + + (axios.get as jest.Mock).mockResolvedValue({ + status: 200, + data: empGoals + }); + + const result = await GoalService.getMyGoals(range); + + expect(result).toEqual({ + state: AsyncState.Success, + data: empGoals + }); + expect(axios.get).toHaveBeenCalledTimes(1); + expect(axios.get).toBeCalledWith(expect.anything(), { + params: range, + withCredentials: true + }); + }); + + it("Returns Not Found state if axios response is 404", async () => { + (axios.get as jest.Mock).mockImplementationOnce(() => + Promise.reject({ + response: { + status: 404 + } + }) + ); + + const result = await GoalService.getMyGoals({} as IMonthRange); + + expect(result).toEqual({ + state: AsyncState.NotFound + }); + }); + + it("returns Failure if axios error is not 404", async () => { + (axios.get as jest.Mock).mockImplementation(() => { + Promise.reject({ + response: { + status: 500 + } + }); + }); + + const result = await GoalService.getMyGoals({} as IMonthRange); + + expect(result).toEqual({ + state: AsyncState.Failure + }); + }); +}); + +describe("GoalService - getMyDepartmentsGoals", () => { + it("Returns success with data", async () => { + const range: IMonthRange = { + fromMonth: 1, + fromYear: 2021, + toMonth: 2, + toYear: 2022 + }; + + const deptGoals: IDepartmentGoalsForRange = { + departmentId: 1, + goals: { productGoals: { product: 2 } }, + goalAudit: [], + monthRange: range + }; + + (axios.get as jest.Mock).mockResolvedValue({ + status: 200, + data: deptGoals + }); + + const result = await GoalService.getMyDepartmentsGoals(range); + + expect(result).toEqual({ + state: AsyncState.Success, + data: deptGoals + }); + expect(axios.get).toHaveBeenCalledTimes(1); + expect(axios.get).toBeCalledWith(expect.anything(), { + params: range, + withCredentials: true + }); + }); + + it("Returns Not Found state if axios response is 404", async () => { + (axios.get as jest.Mock).mockImplementationOnce(() => + Promise.reject({ + response: { + status: 404 + } + }) + ); + + const result = await GoalService.getMyDepartmentsGoals( + {} as IMonthRange + ); + + expect(result).toEqual({ + state: AsyncState.NotFound + }); + }); + + it("returns Failure if axios error is not 404", async () => { + (axios.get as jest.Mock).mockImplementation(() => { + Promise.reject({ + response: { + status: 500 + } + }); + }); + + const result = await GoalService.getMyDepartmentsGoals( + {} as IMonthRange + ); + + expect(result).toEqual({ + state: AsyncState.Failure + }); + }); +}); + +describe("filterGoalsByShowAll", () => { + it("should return goals without filtering if passed true", () => { + const personnelGoals = mockPersonnelGoals(); + const result = GoalService.filterPersonnelGoalsByShowAll( + true, + personnelGoals + ); + + expect(result).toEqual(personnelGoals); + }); + + it("should return goals and personnel with permission or goal set if passed false", () => { + const personnelGoals = mockPersonnelGoals(); + const result = GoalService.filterPersonnelGoalsByShowAll( + false, + personnelGoals + ); + + expect(result.personnel.length).toBe(3); + expect(Object.keys(result.goals).length).toBe(3); + expect(result.goals["1"]).toEqual(personnelGoals.goals["1"]); + expect(result.goals["2"]).toEqual(personnelGoals.goals["2"]); + expect(result.goals["3"]).toBeUndefined(); + expect(result.goals["4"]).toEqual(personnelGoals.goals["4"]); + }); +}); + +describe("validPersonnelGoals", () => { + it("Should determine goal changes are bad", () => { + const pendingChanges: IGoalChangeRequest[] = [ + { + departmentId: 92, + personId: "11771", + value: 1, + product: Product2.CheckCard, + startDate: moment("2020-01-01"), + endDate: moment("2020-01-31") + } + ]; + const personnelGoals: IPersonnelGoals = fullMockPersonnelGoals(); + const departmentGoals: IDepartmentGoals = mockDepartmentGoals(); + const result = GoalService.isValidPersonnelChanges( + pendingChanges, + personnelGoals, + departmentGoals + ); + expect(result).toEqual(false); + }); + + it("Should determine no goal changes are good", () => { + const pendingChanges: IGoalChangeRequest[] = []; + const personnelGoals: IPersonnelGoals = fullMockPersonnelGoals(); + const departmentGoals: IDepartmentGoals = mockDepartmentGoals(); + const result = GoalService.isValidPersonnelChanges( + pendingChanges, + personnelGoals, + departmentGoals + ); + expect(result).toEqual(true); + }); + + it("Should determine goal changes are good", () => { + const pendingChanges: IGoalChangeRequest[] = [ + { + departmentId: 92, + personId: "11771", + value: 5, + product: Product2.ATMCard, + startDate: moment("2020-01-01"), + endDate: moment("2020-01-31") + }, + { + departmentId: 92, + personId: "11771", + value: 10, + product: Product2.CreditCardBalanceTransfer, + startDate: moment("2020-01-01"), + endDate: moment("2020-01-31") + }, + { + departmentId: 92, + personId: "11771", + value: 15, + product: Product2.CheckCard, + startDate: moment("2020-01-01"), + endDate: moment("2020-01-31") + }, + { + departmentId: 92, + personId: "11771", + value: 20, + product: Product2.NewChecking, + startDate: moment("2020-01-01"), + endDate: moment("2020-01-31") + }, + { + departmentId: 92, + personId: "11771", + value: 25, + product: Product2.CreditCard, + startDate: moment("2020-01-01"), + endDate: moment("2020-01-31") + }, + { + departmentId: 92, + personId: "11771", + value: 20, + product: Product2.DirectDeposit, + startDate: moment("2020-01-01"), + endDate: moment("2020-01-31") + }, + { + departmentId: 92, + personId: "C00002", + value: 20, + product: Product2.DirectDeposit, + startDate: moment("2020-01-01"), + endDate: moment("2020-01-31") + }, + { + departmentId: 92, + personId: "C00002", + value: 45, + product: Product2.GAPInsurance, + startDate: moment("2020-01-01"), + endDate: moment("2020-01-31") + }, + { + departmentId: 92, + personId: "11771", + value: 50, + product: Product2.LIC, + startDate: moment("2020-01-01"), + endDate: moment("2020-01-31") + }, + { + departmentId: 92, + personId: "11771", + value: 55, + product: Product2.Mortgage, + startDate: moment("2020-01-01"), + endDate: moment("2020-01-31") + }, + { + departmentId: 92, + personId: "C00002", + value: 60, + product: Product2.MechanicalBreakdownProtection, + startDate: moment("2020-01-01"), + endDate: moment("2020-01-31") + }, + { + departmentId: 92, + personId: "11771", + value: 65, + product: Product2.Vehicle, + startDate: moment("2020-01-01"), + endDate: moment("2020-01-31") + } + ]; + const personnelGoals: IPersonnelGoals = fullMockPersonnelGoals(); + const departmentGoals: IDepartmentGoals = mockDepartmentGoals(); + + expect( + GoalService.isValidGoalChange( + pendingChanges[0].product, + pendingChanges[0].value + "" + ) + ).toEqual(true); + const monthExisting = GoalService.getMonthlyGoal( + departmentGoals, + personnelGoals.month, + Product2.CheckCard.code + ); + const monthNew = GoalService.sumPendingPersonnelGoalsForProduct( + personnelGoals, + pendingChanges, + Product2.CheckCard + ); + + expect(monthExisting).toEqual(15); + expect(monthNew).toEqual(15); + const result = GoalService.isValidPersonnelChanges( + pendingChanges, + personnelGoals, + departmentGoals + ); + expect(result).toEqual(true); + }); +}); + +describe("monthlyGoals", () => { + it("should handle a bad month parameter", () => { + const departmentGoals: IDepartmentGoals = { + departmentId: 99, + year: 2099, + monthlyGoals: [] + }; + const productCode = Product2.CheckCard.code; + let result = GoalService.getMonthlyGoal( + departmentGoals, + 0, + productCode + ); + expect(result).toEqual(-1); + result = GoalService.getMonthlyGoal(departmentGoals, 13, productCode); + expect(result).toEqual(-1); + }); + + it("should handle a bad product parameter", () => { + const departmentGoals: IDepartmentGoals = { + departmentId: 99, + year: 2099, + monthlyGoals: [] + }; + const productCode = "bad"; + let result = GoalService.getMonthlyGoal( + departmentGoals, + 1, + productCode + ); + expect(result).toEqual(-1); + }); + + it("should find the goal", () => { + const departmentGoals: IDepartmentGoals = { + departmentId: 99, + year: 2099, + monthlyGoals: [ + { + productGoals: { + ATMCard: 0, + CreditCardBalanceTransfer: 0, + CheckCard: 7, + NewChecking: 5, + CreditCard: 5, + DirectDeposit: 0, + GAPInsurance: 10, + LIC: 10, + Mortgage: 10, + MechanicalBreakdownProtection: 0, + Vehicle: 10 + } + } + ] + }; + const month = 1; + const productCode = Product2.CheckCard.code; + const result = GoalService.getMonthlyGoal( + departmentGoals, + month, + productCode + ); + expect(result).toEqual(7); + }); +}); + +const mockDepartmentGoals = (): IDepartmentGoals => { + return { + departmentId: 92, + year: 2021, + monthlyGoals: [ + { + productGoals: { + ATMCard: 5, + CreditCardBalanceTransfer: 10, + CheckCard: 15, + NewChecking: 20, + CreditCard: 25, + DirectDeposit: 40, + GAPInsurance: 45, + LIC: 50, + Mortgage: 55, + MechanicalBreakdownProtection: 60, + Vehicle: 65 + } + }, + { + productGoals: { + ATMCard: 5, + CreditCardBalanceTransfer: 10, + CheckCard: 15, + NewChecking: 20, + CreditCard: 25, + DirectDeposit: 40, + GAPInsurance: 45, + LIC: 50, + Mortgage: 55, + MechanicalBreakdownProtection: 60, + Vehicle: 65 + } + }, + { + productGoals: { + ATMCard: 5, + CreditCardBalanceTransfer: 10, + CheckCard: 15, + NewChecking: 20, + CreditCard: 25, + DirectDeposit: 40, + GAPInsurance: 45, + LIC: 50, + Mortgage: 55, + MechanicalBreakdownProtection: 60, + Vehicle: 65 + } + }, + { + productGoals: { + ATMCard: 5, + CreditCardBalanceTransfer: 10, + CheckCard: 15, + NewChecking: 20, + CreditCard: 25, + DirectDeposit: 40, + GAPInsurance: 45, + LIC: 50, + Mortgage: 55, + MechanicalBreakdownProtection: 60, + Vehicle: 65 + } + }, + { + productGoals: { + ATMCard: 5, + CreditCardBalanceTransfer: 10, + CheckCard: 15, + NewChecking: 20, + CreditCard: 25, + DirectDeposit: 40, + GAPInsurance: 45, + LIC: 50, + Mortgage: 55, + MechanicalBreakdownProtection: 60, + Vehicle: 65 + } + }, + { + productGoals: { + ATMCard: 5, + CreditCardBalanceTransfer: 10, + CheckCard: 15, + NewChecking: 20, + CreditCard: 25, + DirectDeposit: 40, + GAPInsurance: 45, + LIC: 50, + Mortgage: 55, + MechanicalBreakdownProtection: 60, + Vehicle: 65 + } + }, + { + productGoals: { + ATMCard: 5, + CreditCardBalanceTransfer: 10, + CheckCard: 15, + NewChecking: 20, + CreditCard: 25, + DirectDeposit: 40, + GAPInsurance: 45, + LIC: 50, + Mortgage: 55, + MechanicalBreakdownProtection: 60, + Vehicle: 65 + } + }, + { + productGoals: { + ATMCard: 5, + CreditCardBalanceTransfer: 10, + CheckCard: 15, + NewChecking: 20, + CreditCard: 25, + DirectDeposit: 40, + GAPInsurance: 45, + LIC: 50, + Mortgage: 55, + MechanicalBreakdownProtection: 60, + Vehicle: 65 + } + }, + { + productGoals: { + ATMCard: 5, + CreditCardBalanceTransfer: 10, + CheckCard: 15, + NewChecking: 20, + CreditCard: 25, + DirectDeposit: 40, + GAPInsurance: 45, + LIC: 50, + Mortgage: 55, + MechanicalBreakdownProtection: 60, + Vehicle: 65 + } + }, + { + productGoals: { + ATMCard: 5, + CreditCardBalanceTransfer: 10, + CheckCard: 15, + NewChecking: 20, + CreditCard: 25, + DirectDeposit: 40, + GAPInsurance: 45, + LIC: 50, + Mortgage: 55, + MechanicalBreakdownProtection: 60, + Vehicle: 65 + } + }, + { + productGoals: { + ATMCard: 5, + CreditCardBalanceTransfer: 10, + CheckCard: 15, + NewChecking: 20, + CreditCard: 25, + DirectDeposit: 40, + GAPInsurance: 45, + LIC: 50, + Mortgage: 55, + MechanicalBreakdownProtection: 60, + Vehicle: 65 + } + }, + { + productGoals: { + ATMCard: 5, + CreditCardBalanceTransfer: 10, + CheckCard: 15, + NewChecking: 20, + CreditCard: 25, + DirectDeposit: 40, + GAPInsurance: 45, + LIC: 50, + Mortgage: 55, + MechanicalBreakdownProtection: 60, + Vehicle: 65 + } + } + ] + }; +}; + +const fullMockPersonnelGoals = (): IPersonnelGoals => { + return { + departmentId: 92, + year: 2021, + month: 1, + personnel: [ + { + adEmail: "georgefrick@landmarkcu.com", + personId: 0, + employeeId: "C00002", + homeDepartment: "IT OPERATIONS", + baseRole: "Developer", + role: { + name: "ABM", + rank: 1, + description: "Assistant Branch Manager", + permissions: [], + hasCommissions: true + }, + additionalPermissions: [], + fullName: "George Frick", + departments: [], + assignments: [] + }, + { + adEmail: "priyaduba@landmarkcu.com", + personId: 0, + employeeId: "11771", + homeDepartment: "HEADQUARTERS - INFORMATION TECHNOLOGY", + baseRole: "QUALITY ASSURANCE ANALYST", + role: { + name: "Developer", + rank: 2, + description: "Developer", + permissions: [], + hasCommissions: false + }, + additionalPermissions: [], + fullName: "DUBA, SIRISHA PRIYADARSINI", + departments: [], + assignments: [] + } + ], + goals: { + C00002: { + productGoals: { + ATMCard: 0, + CreditCardBalanceTransfer: 0, + CheckCard: 0, + NewChecking: 0, + CreditCard: 0, + DirectDeposit: 0, + GAPInsurance: 0, + LIC: 0, + Mortgage: 0, + MechanicalBreakdownProtection: 0, + Vehicle: 0 + } + }, + "11771": { + productGoals: { + ATMCard: 0, + CreditCardBalanceTransfer: 0, + CheckCard: 0, + NewChecking: 0, + CreditCard: 0, + DirectDeposit: 0, + GAPInsurance: 0, + LIC: 0, + Mortgage: 0, + MechanicalBreakdownProtection: 0, + Vehicle: 0 + } + } + } + }; +}; + +const mockPersonnelGoals = () => { + return { + departmentId: 1, + month: 1, + year: 2020, + personnel: [ + { + employeeId: "1", + fullName: "Has Permission", + + role: { + permissions: [ + { code: PermissionCode.HAVE_SALES_GOALS } + ] as IPermission[] + } + }, + { + employeeId: "2", + fullName: "Has set goal, no permission", + role: { permissions: [] as IPermission[] } + }, + { + employeeId: "3", + fullName: "Has no set goal or permission", + role: { permissions: [] as IPermission[] } + }, + { + employeeId: "4", + fullName: "Has additional permission", + additionalPermissions: [ + { + id: 1, + permission: { + code: PermissionCode.HAVE_SALES_GOALS, + description: "testing yo" + } + } + ], + role: { permissions: [] as IPermission[] } + } + ] as IEmployee[], + goals: { + "1": { productGoals: { goal1: 0, goal2: 0 } }, + "2": { productGoals: { goal1: 1, goal2: 0 } }, + "3": { productGoals: { goal1: 0, goal2: 0 } }, + "4": { productGoals: { goal1: 3, goal2: 5 } } + } + } as IPersonnelGoals; +}; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/shared/services/GoalService.ts b/LCU.WebApps.Operations.IncentiveManager/src/shared/services/GoalService.ts new file mode 100644 index 0000000..30662cb --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/shared/services/GoalService.ts @@ -0,0 +1,773 @@ +import Axios from "axios"; +import AsyncState from "../enumerations/AsyncState"; +import { + IDepartmentGoals, + IDepartmentGoalsForRange, + IDepartmentGoalsInput, + IEmployeeGoals, + IGoal, + IGoalChangeRequest, + IGoalChangeRequestDto, + IPersonnelGoals, + IPersonnelGoalsInput +} from "../models/Goal"; +import IAsyncStateContainer from "../types/IAsyncStateContainer"; +import Configuration from "./../configuration/Configuration"; +import Product, { Product2, ProductValue } from "../enumerations/Product"; +import moment from "moment"; +import { IMonthRange } from "../types/IMonthRange"; +import PermissionCode from "../enumerations/PermissionCode"; +import pick from "lodash.pick"; +import { IDepartment } from "../models/Department"; +import XLSX from "xlsx"; + +const moneyFormatter = new Intl.NumberFormat("en-US", { + style: "currency", + currency: "USD" + + // These options are needed to round to whole numbers if that's what you want. + //minimumFractionDigits: 0, // (this suffices for whole numbers, but will print 2500.10 as $2,500.1) + //maximumFractionDigits: 0, // (causes 2500.99 to be printed as $2,501) +}); + +const numberFormatter = new Intl.NumberFormat("en-US"); + +export default class GoalService { + static getAllGoals = async ( + range: IMonthRange, + includePersonnel: boolean, + departmentId?: number + ): Promise> => { + try { + let url = Configuration.PrefixIncentiveUrl( + Configuration.Get.urls.incentiveApi.getAllGoals + ); + url += "?p1=1"; + if (range && range.fromMonth) { + url += "&fromMonth=" + range.fromMonth; + } + if (range && range.fromYear) { + url += "&fromYear=" + range.fromYear; + } + if (range && range.toMonth) { + url += "&toMonth=" + range.toMonth; + } + if (range && range.toYear) { + url += "&toYear=" + range.toYear; + } + if (departmentId) { + url += "&departmentId=" + departmentId; + } + if (includePersonnel != null) { + url += "&includePersonnel=" + includePersonnel; + } + + const res = await Axios.get(url, { withCredentials: true }); + + return { + state: AsyncState.Success, + data: res.data + }; + } catch (err) { + if (err.response?.status == 404) { + return { + state: AsyncState.NotFound + }; + } + return { + state: AsyncState.Failure + }; + } + }; + + static getMyGoals = async ( + monthRange: IMonthRange + ): Promise> => { + try { + const url = Configuration.PrefixIncentiveUrl( + Configuration.Get.urls.incentiveApi.getMyGoals + ); + const res = await Axios.get(url, { + withCredentials: true, + params: { + ...monthRange + } + }); + + return { + state: AsyncState.Success, + data: res.data + }; + } catch (err) { + if (err.response?.status == 404) { + return { + state: AsyncState.NotFound + }; + } + return { + state: AsyncState.Failure + }; + } + }; + + static getMyDepartmentsGoals = async ( + monthRange: IMonthRange + ): Promise> => { + try { + const url = Configuration.PrefixIncentiveUrl( + Configuration.Get.urls.incentiveApi.getMyDepartmentsGoals + ); + + const res = await Axios.get(url, { + withCredentials: true, + params: { + ...monthRange + } + }); + return { + state: AsyncState.Success, + data: res.data + }; + } catch (err) { + if (err.response?.status == 404) { + return { + state: AsyncState.NotFound + }; + } + return { + state: AsyncState.Failure + }; + } + }; + + /** + * Compare a goal change request to the actual department goals to determine + * if it actually changed. + * @param dataExisting + * @param request + * @constructor + */ + static IsChangedGoal = ( + dataExisting: IDepartmentGoals[], + request: IGoalChangeRequest + ): boolean => { + const departmentGoals = dataExisting.find( + (deptGoals) => deptGoals.departmentId === request.departmentId + ); + if (departmentGoals) { + const monthlyGoal = + departmentGoals.monthlyGoals[request.startDate.month()]; + if ( + monthlyGoal.productGoals[request.product.code] === request.value + ) { + return false; + } + } + return true; + }; + + /** + * This goes through an excel sheet (single sheet), as a series of goal + * change requests. It checks them against freshly loaded goals so that + * it only creates changes that are different. You can optionally not + * pass in existing data, and all changes are products (~3,000) + * @param productCode - which product the sheet is for + * @param year - year + * @param dataNew - the new sheet data that is importing + * @param dataExisting - the current state of the goals + */ + static ImportGoalSheet = ( + productCode: string, + year: number, + dataNew: any[], + dataExisting?: IDepartmentGoals[] + ): IGoalChangeRequest[] => { + const requests: IGoalChangeRequest[] = []; + const product = Product2.Products.find((x) => x.code === productCode); + if (product) { + dataNew.forEach((row: any[], rowIdx: number) => { + // Skip the headers. + if (rowIdx === 0) return; + row.forEach((col: number, colIdx: number) => { + // Skip the department columns. + if (colIdx <= 2 || !col) return; + const request: IGoalChangeRequest = { + departmentId: row[0], + value: col, + startDate: moment + .utc(`${colIdx - 2}/01/${year}`, "MM/dd/yyyy") + .startOf("month"), + endDate: moment + .utc(`${colIdx - 2}/01/${year}`, "MM/dd/yyyy") + .endOf("month"), + product: product + }; + if ( + !dataExisting || + GoalService.IsChangedGoal(dataExisting, request) + ) { + requests.push(request); + } + }); + }); + } + return requests; + }; + + /** + * First version, Ben didn't like it. We agreed to do by Product, but this is here + * if it is ever needed. + * @param year + * @param departments + */ + static exportAllGoalsForYearByMonth = ( + year: number, + departments: IDepartment[] + ) => { + const productsWithDepartmentGoals = Product2.Products.filter( + (product) => product.hasDepartmentGoals + ); + GoalService.getAllDepartmentGoals(year).then( + (allDepartmentsAllGoals) => { + if (allDepartmentsAllGoals.data) { + const workBook = XLSX.utils.book_new(); + const goalDepartments = allDepartmentsAllGoals.data; + GoalService.Months.forEach( + (month: string, mIdx: number) => { + const monthData: any[] = []; + monthData[0] = ["Dept ID", "Department"]; + productsWithDepartmentGoals.forEach( + (product: Product2, pIdx: number) => { + monthData[0].push(product.code); + goalDepartments.forEach( + (departmentGoals, dIdx) => { + const department = departments.find( + (dept) => { + return ( + dept.id === + departmentGoals.departmentId + ); + } + ); + if (department) { + monthData[dIdx + 1] = [ + department.id, + department.departmentName + ]; + const goalsForMonth = + departmentGoals + .monthlyGoals[mIdx]; + productsWithDepartmentGoals.forEach( + ( + productInner: Product2, + pIdxInner: number + ) => { + monthData[ + dIdx + 1 + ].push( + goalsForMonth + .productGoals[ + productInner + .code + ] + ); + } + ); + } + } + ); + } + ); + const workSheet = XLSX.utils.aoa_to_sheet( + monthData + ); + XLSX.utils.book_append_sheet( + workBook, + workSheet, + month + ); + } + ); + XLSX.writeFile(workBook, `all_goals_${year}.xlsx`); + } + } + ); + }; + + static exportAllGoalsForYearByProduct = ( + year: number, + departments: IDepartment[] + ) => { + const productsWithDepartmentGoals = Product2.Products.filter( + (product) => product.hasDepartmentGoals + ); + GoalService.getAllDepartmentGoals(year).then( + (allDepartmentsAllGoals) => { + if (allDepartmentsAllGoals.data) { + const workBook = XLSX.utils.book_new(); + const goalDepartments = allDepartmentsAllGoals.data; + productsWithDepartmentGoals.forEach( + (product: Product2, pIdx: number) => { + const productSheet: any[] = []; + // Create Row 0, [DeptId,Dept,Bl,Jan,Feb,...] + productSheet[0] = ["Dept ID", "Department", "BL"]; + GoalService.Months.forEach( + (month: string, mIdx: number) => { + productSheet[0].push(month); + } + ); + // Create Department rows. + goalDepartments.forEach((departmentGoals, dIdx) => { + const department = departments.find((dept) => { + return ( + dept.id === departmentGoals.departmentId + ); + }); + if (department) { + // Row: [id,name,bl,jan,feb, + productSheet[dIdx + 1] = [ + department.id, + department.departmentName, + department.businessLineCode + ]; + // months... + GoalService.Months.forEach( + ( + monthInner: string, + mIdxInner: number + ) => { + productSheet[dIdx + 1].push( + departmentGoals.monthlyGoals[ + mIdxInner + ].productGoals[product.code] + ); + } + ); + } + }); + const workSheet = XLSX.utils.aoa_to_sheet( + productSheet + ); + XLSX.utils.book_append_sheet( + workBook, + workSheet, + product.code + ); + } + ); + XLSX.writeFile(workBook, `all_goals_${year}.xlsx`); + } + } + ); + }; + + static getAllDepartmentGoals = async ( + year: number + ): Promise> => { + try { + const url = Configuration.PrefixIncentiveUrl( + Configuration.Get.urls.incentiveApi.getAllGoals, + "/department" + "?year=" + year + ); + const res = await Axios.get(url, { withCredentials: true }); + + return { + state: AsyncState.Success, + data: res.data + }; + } catch (err) { + if (err.response?.status == 404) { + return { + state: AsyncState.NotFound + }; + } + return { + state: AsyncState.Failure + }; + } + }; + + static getDepartmentGoals = async ( + input: IDepartmentGoalsInput + ): Promise> => { + try { + const url = Configuration.PrefixIncentiveUrl( + Configuration.Get.urls.incentiveApi.getAllGoals, + "/department/" + input.departmentId + "?year=" + input.year + ); + const res = await Axios.get(url, { withCredentials: true }); + + return { + state: AsyncState.Success, + data: res.data + }; + } catch (err) { + if (err.response?.status == 404) { + return { + state: AsyncState.NotFound + }; + } + return { + state: AsyncState.Failure + }; + } + }; + + static getPersonnelGoals = async ( + input: IPersonnelGoalsInput + ): Promise> => { + try { + const url = Configuration.PrefixIncentiveUrl( + Configuration.Get.urls.incentiveApi.getAllGoals, + "/personnel/" + + input.departmentId + + "?year=" + + input.year + + "&month=" + + input.month + ); + const res = await Axios.get(url, { + withCredentials: true, + params: { includeAllPersonnel: true } + }); + + return { + state: AsyncState.Success, + data: res.data + }; + } catch (err) { + if (err.response?.status == 404) { + return { + state: AsyncState.NotFound + }; + } + return { + state: AsyncState.Failure + }; + } + }; + + static savePendingChanges = async ( + changes: IGoalChangeRequest[] + ): Promise> => { + try { + const url = Configuration.PrefixIncentiveUrl( + Configuration.Get.urls.incentiveApi.getAllGoals, + "/multi" + ); + + const requestedChanges = GoalService.convertToRequestDto(changes); + const res = await Axios.post(url, requestedChanges, { + withCredentials: true + }); + + return { + state: AsyncState.Success, + data: res.data + }; + } catch (err) { + if (err.response?.status == 404) { + return { + state: AsyncState.NotFound + }; + } + return { + state: AsyncState.Failure + }; + } + }; + + static getInputCSSClassList = ( + type: Product2, + originalValue: string, + newValue: string + ) => { + if (originalValue == newValue) return "form-control"; + const newVal = parseInt(newValue, 10); + if (newVal < type.minValue || newVal > type.maxValue) { + return "form-control is-invalid"; + } + return "form-control is-valid"; + }; + + // Give the department goals, a month and a product code, provide the department goal. + static getMonthlyGoal( + departmentGoals: IDepartmentGoals, + month: number, + productCode: string + ): number { + if (month <= 0 || month > 12) { + // Not on the server, so we just don't want to crash. + return -1; + } + + if ( + !productCode || + Product.Products.findIndex((x) => x.code === productCode) === -1 + ) { + return -1; + } + + return departmentGoals.monthlyGoals[month - 1].productGoals[ + productCode + ]; + } + + static formatMonthlyGoal(value: number, product: Product2): string { + if (product.valueType === ProductValue.Monetary) { + return moneyFormatter.format(value); + } + return numberFormatter.format(value); + } + + static formatMonetary(value: number | undefined): string { + return value ? moneyFormatter.format(value) : ""; + } + + static convertToPercentage(actual: number, goal: number): string { + if (!actual) { + return "0%"; + } + if (!goal && actual) { + return "Infinity%"; + } + //NOTE: Have to multiple by 1000 and divide by 10, because of typescript 57/100 = .569999999 + const result = ((actual / goal) * 1000) / 10; + + return Math.floor(result) + "%"; + } + + static isValidDepartmentChanges( + pendingChanges: IGoalChangeRequest[] + ): boolean { + let valid = true; + pendingChanges.forEach((pending) => { + if (!this.isValidGoalChange(pending.product, pending.value + "")) { + valid = false; + } + }); + + return valid; + } + + static productsWithPersonnelGoals(): Array { + return Product2.Products.filter((product) => product.hasPersonnelGoals); + } + + static isValidPersonnelChanges( + pendingChanges: IGoalChangeRequest[], + filteredPersonnelGoals: IPersonnelGoals | undefined, + departmentGoals: IDepartmentGoals | undefined + ): boolean { + if (!pendingChanges || pendingChanges.length === 0) { + return true; + } + + let valid = true; + pendingChanges.forEach((pending) => { + if (!this.isValidGoalChange(pending.product, pending.value + "")) { + valid = false; + } + }); + + if (valid && departmentGoals && filteredPersonnelGoals) { + this.productsWithPersonnelGoals().forEach((product: Product2) => { + const monthExisting = this.getMonthlyGoal( + departmentGoals, + filteredPersonnelGoals.month, + product.code + ); + const monthNew = this.sumPendingPersonnelGoalsForProduct( + filteredPersonnelGoals, + pendingChanges, + product + ); + if (monthNew < monthExisting) { + valid = false; + } + }); + } + + return valid; + } + + static isValidGoalChange = (type: Product2, newValue: string) => { + const newVal = parseInt(newValue, 10); + return !(newVal < type.minValue || newVal > type.maxValue); + }; + + static Months = [ + "Jan", + "Feb", + "Mar", + "Apr", + "May", + "Jun", + "Jul", + "Aug", + "Sep", + "Oct", + "Nov", + "Dec" + ]; + + static getMonthIndex = (month: string): number => { + return GoalService.Months.indexOf(month); + }; + + static convertToGoalChanges = ( + monthStr: string, + type: Product2, + value: number, + departmentId: number, + year: number + ): IGoalChangeRequest => { + let startDate = moment().utc().set("year", year); + startDate = startDate.set("month", GoalService.getMonthIndex(monthStr)); + startDate = startDate.clone().startOf("month"); + const endDate = startDate.clone(); // TODO: .endOf("month"); ? + endDate.add(1, "month"); + endDate.subtract(1, "second"); + + return { + departmentId: departmentId, + product: type, + value: value, + startDate: startDate, + endDate: endDate + }; + }; + + static sumPersonnelGoalsForProduct = ( + goals: IPersonnelGoals, + product: Product2 + ) => { + let total = 0; + goals.personnel.forEach((person) => { + total += goals.goals[person.employeeId].productGoals[product.code]; + }); + return total; + }; + + static sumPendingPersonnelGoalsForProduct = ( + goals: IPersonnelGoals, + pendingChanges: IGoalChangeRequest[], + product: Product2 + ) => { + let totalPending = 0; + let found = false; + goals.personnel.forEach((person) => { + found = false; + pendingChanges.forEach((pending: IGoalChangeRequest) => { + if ( + pending.product === product && + pending.departmentId === goals.departmentId && + pending.personId === person.employeeId + ) { + totalPending += pending.value; + found = true; + } + }); + if (!found) { + totalPending += + goals.goals[person.employeeId].productGoals[product.code]; + } + }); + return totalPending; + }; + + static sumDepartmentGoalsForProduct = ( + goals: IDepartmentGoals, + product: Product2 + ) => { + let total = 0; + goals.monthlyGoals.forEach((eachGoal) => { + total += eachGoal.productGoals[product.code]; + }); + return total; + }; + + static sumPendingDepartmentGoalsForProduct = ( + goals: IDepartmentGoals, + pendingChanges: IGoalChangeRequest[], + product: Product2 + ) => { + let totalPending = 0; + let found = false; + + goals.monthlyGoals.forEach((eachGoal, index: number) => { + found = false; + + pendingChanges.forEach((pending: IGoalChangeRequest) => { + if ( + pending.product === product && + pending.departmentId === goals.departmentId && + pending.startDate.month() === index + ) { + totalPending += pending.value; + found = true; + } + }); + if (!found) { + totalPending += eachGoal.productGoals[product.code]; + } + }); + return totalPending; + }; + + static filterPersonnelGoalsByShowAll = ( + includeAllPersonnel: boolean, + personnelGoals: IPersonnelGoals + ) => { + if (includeAllPersonnel) { + return personnelGoals; + } + + const { personnel, goals } = personnelGoals; + // filter personnel with sales goal permission or at least 1 goal set + const filteredPersonnel = personnel.filter( + (person) => + person.role.permissions.some( + (p) => p.code === PermissionCode.HAVE_SALES_GOALS + ) || + (person.additionalPermissions && + person.additionalPermissions.some( + (p) => + p.permission.code === + PermissionCode.HAVE_SALES_GOALS + )) || + Object.values(goals[person.employeeId].productGoals).some( + (count) => count > 0 + ) + ); + + // filter goals to include only filtered personnel + const filteredGoals = pick( + goals, + filteredPersonnel.map((person) => person.employeeId) + ); + + return { + ...personnelGoals, + personnel: filteredPersonnel, + goals: filteredGoals + }; + }; + + private static convertToRequestDto( + changes: IGoalChangeRequest[] + ): IGoalChangeRequestDto[] { + return changes.map( + (value: IGoalChangeRequest) => { + return { + departmentId: value.departmentId, + personId: value.personId, + product: value.product.code, + startDate: value.startDate, + endDate: value.endDate, + value: value.value + }; + } + ); + } +} diff --git a/LCU.WebApps.Operations.IncentiveManager/src/shared/services/InputService.test.ts b/LCU.WebApps.Operations.IncentiveManager/src/shared/services/InputService.test.ts new file mode 100644 index 0000000..5261e5a --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/shared/services/InputService.test.ts @@ -0,0 +1,62 @@ +import moment, { Moment } from "moment"; +import { tryUpdateStateWithInput } from "./InputService"; + +describe("Input Service", () => { + interface ITestState { + Color: string; + Number: number; + Date: Moment; + } + + let TestState: ITestState; + + beforeEach(() => { + TestState = { + Color: "red", + Number: 3, + Date: moment("04/04/2020") + }; + }); + + it("returns updated state", () => { + let res = tryUpdateStateWithInput( + TestState, + "Color", + "blue" + ); + + expect(res.Color).toBe("blue"); + expect(res.Number).toBe(3); + expect(res.Date.format("MM/DD/YYYY")).toBe("04/04/2020"); + + res = tryUpdateStateWithInput( + TestState, + "Date", + moment("07/07/2020") + ); + + expect(res.Color).toBe("red"); + expect(res.Number).toBe(3); + expect(res.Date.format("MM/DD/YYYY")).toBe("07/07/2020"); + + res = tryUpdateStateWithInput(TestState, "Number", 5); + + expect(res.Color).toBe("red"); + expect(res.Number).toBe(5); + expect(res.Date.format("MM/DD/YYYY")).toBe("04/04/2020"); + }); + + it("errors out if property is not found", () => { + let didThrow = false; + + try { + expect( + tryUpdateStateWithInput(TestState, "clr", "blue") + ).toThrow(); + } catch { + didThrow = true; + } + + expect(didThrow).toBe(true); + }); +}); diff --git a/LCU.WebApps.Operations.IncentiveManager/src/shared/services/InputService.ts b/LCU.WebApps.Operations.IncentiveManager/src/shared/services/InputService.ts new file mode 100644 index 0000000..7b3c740 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/shared/services/InputService.ts @@ -0,0 +1,39 @@ +/* eslint-disable @typescript-eslint/ban-types */ +/* eslint-disable no-console */ +import cloneDeep from "lodash.clonedeep"; + +/** + * @template T the interface for the state you are modifying + * @param state The Component State that has the property you want to modify. + * @param propertyName The name of the property in the state you wan't to update. + * @param newValue The new value of the state property you are updating. + * @returns {T} A new copy of state with the properties updated. You must Set the State in your component for change to take effect. + * @throws {Property Not found} when property is not present in state. This is a fatal error. + * + * @example + * handleInputChange(name: string, newValue: any) { + this.setState( + tryUpdateStateWithInput(this.state, name, newValue) + ) + } + */ + +export function tryUpdateStateWithInput( + state: T, + propertyName: string, + newValue: any +): T { + if (state.hasOwnProperty(propertyName)) { + const newState: any = cloneDeep(state); + newState[propertyName] = newValue; + return newState; + } else { + console.warn( + "Input changing property: " + + propertyName + + ". But It's not found in state." + ); + } + + throw new Error("Property Name Not Found"); +} diff --git a/LCU.WebApps.Operations.IncentiveManager/src/shared/services/LocalStorageService.ts b/LCU.WebApps.Operations.IncentiveManager/src/shared/services/LocalStorageService.ts new file mode 100644 index 0000000..64d8c7c --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/shared/services/LocalStorageService.ts @@ -0,0 +1,34 @@ +import {DeepPartial} from "redux"; +import {IAppState} from "../../store/Store"; +import pick from "lodash.pick"; + + +class LocalStorageService { + + private static StorageKey = "ApplicationState"; + + public static loadAppSavedState(paths?: string[]): DeepPartial { + try { + const serializedState = localStorage.getItem(LocalStorageService.StorageKey); + if (serializedState === null) { + return {}; + } + const parsed = JSON.parse(serializedState); + return paths ? pick(parsed, paths) : parsed; + } catch (err) { + return {}; + } + } + + public static saveAppState(state: IAppState, paths?: string[]) { + try { + const targetObj = paths ? pick(state, paths) : state; + const serializedState = JSON.stringify(targetObj); + localStorage.setItem(LocalStorageService.StorageKey, serializedState); + } catch { + // Not logging because this can happen for normal reasons. + } + } +} + +export default LocalStorageService; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/shared/services/PayoutService.ts b/LCU.WebApps.Operations.IncentiveManager/src/shared/services/PayoutService.ts new file mode 100644 index 0000000..c880f8c --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/shared/services/PayoutService.ts @@ -0,0 +1,164 @@ +import Axios from "axios"; +import AsyncState from "../enumerations/AsyncState"; +import IAsyncStateContainer from "../types/IAsyncStateContainer"; +import Configuration from "./../configuration/Configuration"; +import { + IAccountingSummary, + IEmployeePayoutsInput, + IPayoutEntry, + IPayoutLineItem +} from "../models/Payout"; +import { IEmployeeSummary } from "../models/Employee"; +import { Moment } from "moment"; + +export default class PayoutService { + static getMyPayouts = async ( + month: number, + year: number + ): Promise> => { + try { + const url = Configuration.PrefixIncentiveUrl( + Configuration.Get.urls.incentiveApi.getMyPayouts + ); + const res = await Axios.get(url, { + params: { + year: year, + month: month + }, + withCredentials: true + }); + + return { + state: AsyncState.Success, + data: res.data + }; + } catch (err) { + if (err.response?.status == 404) { + return { + state: AsyncState.NotFound + }; + } + return { + state: AsyncState.Failure + }; + } + }; + + static getEmployeePayouts = async ( + input: IEmployeePayoutsInput + ): Promise> => { + try { + const url = Configuration.PrefixIncentiveUrl( + Configuration.Get.urls.incentiveApi.getEmployeePayouts + ).replace("{employeeId}", input.employeeId); + + const res = await Axios.get(url, { + params: { + year: input.year, + month: input.month + }, + withCredentials: true + }); + + return { + state: AsyncState.Success, + data: res.data + }; + } catch (err) { + if (err.response?.status == 404) { + return { + state: AsyncState.NotFound + }; + } + return { + state: AsyncState.Failure + }; + } + }; + + public static CreateEndOfEmploymentPayouts( + data: IEmployeeSummary[] + ): IPayoutEntry[] { + let results: IPayoutEntry[] = []; + if (data && data.length > 0) { + data.forEach((summary: IEmployeeSummary) => { + const entry: IPayoutEntry = { + employeeName: summary.fullName, + employeeId: summary.employeeId, + code: "Incentive", + payoutTotal: 0 + }; + + summary.associatedPayouts.forEach((payout: IPayoutLineItem) => { + entry.payoutTotal += payout.payoutTotal; + }); + results.push(entry); + }); + } + return results; + } + + static GetExportPayouts = async ( + year: number, + month: number + ): Promise> => { + try { + const url = Configuration.PrefixIncentiveUrl( + Configuration.Get.urls.incentiveApi.exportPayouts + ); + + const res = await Axios.get(url, { + params: { + year: year, + month: month + }, + withCredentials: true + }); + + return { + state: AsyncState.Success, + data: res.data + }; + } catch (err) { + if (err.response?.status == 404) { + return { + state: AsyncState.NotFound + }; + } + return { + state: AsyncState.Failure + }; + } + }; + + static GetAccountingReport = async ( + date: Moment + ): Promise> => { + try { + const url = Configuration.PrefixIncentiveUrl( + Configuration.Get.urls.incentiveApi.exportAccountReport + ); + + const res = await Axios.get(url, { + params: { + period: date.format("MM/DD/YYYY") + }, + withCredentials: true + }); + + return { + state: AsyncState.Success, + data: res.data + }; + } catch (err) { + if (err.response?.status == 404) { + return { + state: AsyncState.NotFound + }; + } + return { + state: AsyncState.Failure + }; + } + }; +} diff --git a/LCU.WebApps.Operations.IncentiveManager/src/shared/services/ProductService.test.ts b/LCU.WebApps.Operations.IncentiveManager/src/shared/services/ProductService.test.ts new file mode 100644 index 0000000..d9107c7 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/shared/services/ProductService.test.ts @@ -0,0 +1,114 @@ +import { IProductDetail } from "../enumerations/Product"; +import { ActivityType } from "../models/Activity"; +import ProductService from "./ProductService"; + +describe("Product service", () => { + it("should return products for undefined activity type", () => { + const results = ProductService.getValidProductsForActivityType( + productDetails() + ); + expect(results).toEqual(productDetails()); + }); + + it("should return products for sale activity type", () => { + const results = ProductService.getValidProductsForActivityType( + productDetails(), + ActivityType.Sale + ); + expect(results).toEqual([ + { + product: "NewChecking", + title: "New Checking", + isSellable: true, + isReferrable: true, + isNonPaying: false + } + ]); + }); + + it("should return products for other activity type", () => { + const results = ProductService.getValidProductsForActivityType( + productDetails(), + ActivityType.Other + ); + expect(results).toEqual([ + { + product: "ATMCard", + title: "ATM Card", + isSellable: false, + isReferrable: true, + isNonPaying: true + } + ]); + }); + + it("should return products for referral activity type", () => { + const results = ProductService.getValidProductsForActivityType( + productDetails(), + ActivityType.Referral + ); + expect(results).toEqual([ + { + product: "ATMCard", + title: "ATM Card", + isSellable: false, + isReferrable: true, + isNonPaying: true + }, + { + product: "CreditCardBalanceTransfer", + title: "CCBT", + isSellable: false, + isReferrable: true, + isNonPaying: false + }, + { + product: "NewChecking", + title: "New Checking", + isSellable: true, + isReferrable: true, + isNonPaying: false + }, + { + product: "CreditCard", + title: "Credit Card", + isSellable: false, + isReferrable: true, + isNonPaying: false + } + ]); + }); +}); + +const productDetails = () => { + return [ + { + product: "ATMCard", + title: "ATM Card", + isSellable: false, + isReferrable: true, + isNonPaying: true + }, + { + product: "CreditCardBalanceTransfer", + title: "CCBT", + isSellable: false, + isReferrable: true, + isNonPaying: false + }, + { + product: "NewChecking", + title: "New Checking", + isSellable: true, + isReferrable: true, + isNonPaying: false + }, + { + product: "CreditCard", + title: "Credit Card", + isSellable: false, + isReferrable: true, + isNonPaying: false + } + ] as IProductDetail[]; +}; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/shared/services/ProductService.ts b/LCU.WebApps.Operations.IncentiveManager/src/shared/services/ProductService.ts new file mode 100644 index 0000000..7414e9c --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/shared/services/ProductService.ts @@ -0,0 +1,18 @@ +import { IProductDetail } from "../enumerations/Product"; +import { ActivityType } from "../../shared/models/Activity"; + +export default class ProductService { + static getValidProductsForActivityType = ( + products: IProductDetail[], + activityType?: ActivityType + ): IProductDetail[] => { + return products.filter( + (product) => + activityType === undefined || + (product.isSellable && activityType === ActivityType.Sale) || + (product.isReferrable && + activityType === ActivityType.Referral) || + (product.isNonPaying && activityType === ActivityType.Other) + ); + }; +} diff --git a/LCU.WebApps.Operations.IncentiveManager/src/shared/services/ProfileService.test.ts b/LCU.WebApps.Operations.IncentiveManager/src/shared/services/ProfileService.test.ts new file mode 100644 index 0000000..c1e615a --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/shared/services/ProfileService.test.ts @@ -0,0 +1,274 @@ +import ProfileService from "./ProfileService"; +import { IEmployee } from "../models/Employee"; +import PermissionCode from "../enumerations/PermissionCode"; +import moment from "moment"; +import { UserType } from "../models/Auditable"; + +describe("Profile Service", () => { + describe("isRegionalManager", () => { + it("should return true if profile role name is RegionalManager", () => { + const profile = { + departments: [ + { + id: 5, + departmentId: 55, + role: { + name: "RegionalManager", + permissions: [ + { + code: PermissionCode.APPROVE_REFERRALS, + description: "" + }, + { + code: + PermissionCode.TRANSFER_ACTIVITIES_BETWEEN_DEPARTMENTS, + description: "" + } + ], + description: "Regional Manager", + hasCommissions: true + }, + note: "testing", + employeeId: "12345", + slidingPayout: 100, + isHome: true, + created: moment(), + endDate: moment(), + startDate: moment(), + lastUpdatedBy: "test", + createdBy: "test", + lastUpdatedByType: UserType.System, + createdByType: UserType.System, + lastUpdated: moment() + } + ], + role: { name: "RegionalManager" } + } as IEmployee; + expect(ProfileService.isRegionalManager(profile)).toBe(true); + }); + + it("should return false if profile role name is RegionalManager", () => { + const profile = { role: { name: "Teller" } } as IEmployee; + expect(ProfileService.isRegionalManager(profile)).toBe(false); + }); + }); + + describe("hasRegionalManagerAssignment", () => { + it("should return true if profile has an assignment with the type RegionalManager", () => { + const profile = { + assignments: [{ type: "RegionalManager" }] + } as IEmployee; + expect(ProfileService.hasRegionalManagerAssignment(profile)).toBe( + true + ); + }); + + it("should return false if profile does not have any assignments", () => { + const profile = { + assignments: [], + departments: [], + additionalPermissions: [], + adEmail: "", + employeeId: "", + fullName: "", + baseRole: "", + homeDepartment: "", + personId: 100, + role: { + name: "", + permissions: [], + hasCommissions: true, + description: "", + rank: 99 + } + } as IEmployee; + expect(ProfileService.hasRegionalManagerAssignment(profile)).toBe( + false + ); + }); + + it("should return false if profile does not have an assignment with the type RegionalManager", () => { + const profile = { assignments: [{ type: "Other" }] } as IEmployee; + expect(ProfileService.hasRegionalManagerAssignment(profile)).toBe( + false + ); + }); + }); + + describe("filters for active departments", () => { + const employee: IEmployee = { + adEmail: "", + additionalPermissions: [], + assignments: [], + baseRole: "App Developer", + departments: [ + { + employeeId: "", + departmentId: 1, + startDate: moment(), + endDate: undefined, + note: "", + isHome: true, + slidingPayout: 100, + role: { + name: "Admin", + description: "Manager", + hasCommissions: true, + permissions: [], + rank: 99 + }, + created: moment(), + createdBy: "", + createdByType: UserType.Associate, + lastUpdated: moment(), + lastUpdatedBy: "", + lastUpdatedByType: UserType.Associate + }, + { + employeeId: "", + departmentId: 2, + startDate: moment().subtract(1, "days"), + endDate: moment().subtract(1, "days"), + note: "", + isHome: true, + slidingPayout: 100, + role: { + name: "Admin", + description: "Manager", + hasCommissions: true, + permissions: [], + rank: 99 + }, + created: moment(), + createdBy: "", + createdByType: UserType.Associate, + lastUpdated: moment(), + lastUpdatedBy: "", + lastUpdatedByType: UserType.Associate + }, + { + employeeId: "", + departmentId: 3, + startDate: moment().add(1, "days"), + endDate: moment().add(1, "days"), + note: "", + isHome: true, + slidingPayout: 100, + role: { + name: "Admin", + description: "Manager", + hasCommissions: true, + permissions: [], + rank: 99 + }, + created: moment(), + createdBy: "", + createdByType: UserType.Associate, + lastUpdated: moment(), + lastUpdatedBy: "", + lastUpdatedByType: UserType.Associate + }, + { + employeeId: "", + departmentId: 4, + startDate: moment().subtract(1, "days"), + endDate: moment(), + note: "", + isHome: true, + slidingPayout: 100, + role: { + name: "Admin", + description: "Manager", + hasCommissions: true, + permissions: [], + rank: 99 + }, + created: moment(), + createdBy: "", + createdByType: UserType.Associate, + lastUpdated: moment(), + lastUpdatedBy: "", + lastUpdatedByType: UserType.Associate + } + ], + employeeId: "C0000", + fullName: "Full Name", + homeDepartment: "1", + personId: 0, + role: { + description: "", + hasCommissions: false, + name: "UNKNOWN", + permissions: [], + rank: 99 + } + }; + const results = ProfileService.getActiveDepartments(employee); + expect(results).toBeTruthy(); + expect(results.length).toEqual(2); + expect(results[0].departmentId).toEqual(1); + expect(results[1].departmentId).toEqual(4); + }); + + describe("isDepartmentManager", () => { + it.each([ + ["BM", 1, true], + ["BOM", 1, true], + ["Manager", 1, true], + ["Developer", 1, false], + ["Teller", 1, false], + ["BM", 2, false], + ["BOM", 2, false], + ["Manager", 2, false], + ["Developer", 2, false], + ["Teller", 2, false] + ])( + "should return true if appUser is manager for given department role and ID", + (roleName: string, departmentId: number, expected: boolean) => { + const employee: IEmployee = { + adEmail: "", + additionalPermissions: [], + assignments: [], + baseRole: "App Developer", + departments: [ + { + employeeId: "", + departmentId: departmentId, + startDate: moment(), + note: "", + isHome: true, + slidingPayout: 100, + role: { + name: roleName, + description: "Manager", + hasCommissions: true, + permissions: [], + rank: 99 + }, + created: moment(), + createdBy: "", + createdByType: UserType.Associate, + lastUpdated: moment(), + lastUpdatedBy: "", + lastUpdatedByType: UserType.Associate + } + ], + employeeId: "C0000", + fullName: "Full Name", + homeDepartment: "1", + personId: 0, + role: { + description: "", + hasCommissions: false, + name: "UNKNOWN", + permissions: [], + rank: 99 + } + }; + expect(ProfileService.isDepartmentManager(employee, 1)).toBe( + expected + ); + } + ); + }); +}); diff --git a/LCU.WebApps.Operations.IncentiveManager/src/shared/services/ProfileService.ts b/LCU.WebApps.Operations.IncentiveManager/src/shared/services/ProfileService.ts new file mode 100644 index 0000000..521b3e6 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/shared/services/ProfileService.ts @@ -0,0 +1,135 @@ +import PermissionCode, { + RankedPermissionCodes +} from "../enumerations/PermissionCode"; +import { IEmployee } from "../models/Employee"; +import { IAppRole } from "../models/Settings"; +import { IDepartmentAssignment } from "../models/DepartmentAssignment"; +import moment from "moment"; + +export default class ProfileService { + static getActiveDepartments = ( + profile: IEmployee + ): IDepartmentAssignment[] => { + let departments = profile.departments; + if (departments && departments.length > 0) { + departments = departments.filter((department) => { + const start = moment(department.startDate); + const end = department.endDate + ? moment(department.endDate) + : null; + const today = moment(); + return ( + start.isSameOrBefore(today, "day") && + (end === null || end.isSameOrAfter(today, "day")) + ); + }); + } + return departments ?? []; + }; + + static findHighestRank = (profile: IEmployee): number => { + const departments = ProfileService.getActiveDepartments(profile); + const ranks = departments + .map((x) => x.role.rank) + .sort() + .reverse(); + return ranks && ranks.length > 0 ? ranks[0] : 1; + }; + + static highestRole = (profile: IEmployee): PermissionCode => { + const permission = RankedPermissionCodes.find((permissionCode) => + ProfileService.hasPermission(profile, permissionCode) + ); + return permission ? permission : PermissionCode.UNKNOWN; + }; + + static roleHasPermission = ( + role: IAppRole, + permissionCode: PermissionCode + ): boolean => { + const rolePermissions = role?.permissions ?? []; + return rolePermissions.some( + (rolePermission) => rolePermission.code === permissionCode + ); + }; + + static hasAnyPermission = ( + profile: IEmployee, + permissionCodes: string[] + ): boolean => { + return permissionCodes.some((permissionCode) => + ProfileService.hasPermission( + profile, + permissionCode as PermissionCode + ) + ); + }; + + static hasPermission = ( + profile: IEmployee, + permissionCode: PermissionCode + ): boolean => { + const departments = ProfileService.getActiveDepartments(profile); + let hasPermission = false; + if (departments && departments.length) { + const found = departments.find((department) => + ProfileService.roleHasPermission( + department.role, + permissionCode + ) + ); + hasPermission = !!found; + } + return ( + hasPermission || + ProfileService.hasAdditionalPermission(profile, permissionCode) + ); + }; + + static hasAdditionalPermission = ( + profile: IEmployee, + permissionCode: PermissionCode + ): boolean => { + const additionalPermissions = + profile.additionalPermissions?.map( + (userPermission) => userPermission.permission + ) ?? []; + return additionalPermissions.some( + (rolePermission) => rolePermission.code === permissionCode + ); + }; + + static isRegionalManager = (profile: IEmployee): boolean => { + return ProfileService.profileHasRole(profile, "RegionalManager"); + }; + + static profileHasRole = (profile: IEmployee, roleName: string): boolean => { + let hasRole = false; + const departments = ProfileService.getActiveDepartments(profile); + if (departments && departments.length) { + const found = departments.find( + (department) => department.role.name === roleName + ); + hasRole = !!found; + } + return hasRole; + }; + + static hasRegionalManagerAssignment = (profile: IEmployee): boolean => { + return profile.assignments.some( + (assignment) => assignment.type === "RegionalManager" + ); + }; + + static isDepartmentManager = ( + profile: IEmployee, + departmentId: number + ): boolean => { + const departments = ProfileService.getActiveDepartments(profile); + return departments.some( + (department) => + department.departmentId === departmentId && + ["BOM", "BM", "Manager"].includes(department.role.name) + ); + }; +} diff --git a/LCU.WebApps.Operations.IncentiveManager/src/shared/services/RoleMappingService.ts b/LCU.WebApps.Operations.IncentiveManager/src/shared/services/RoleMappingService.ts new file mode 100644 index 0000000..f897437 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/shared/services/RoleMappingService.ts @@ -0,0 +1,64 @@ +import Axios from "axios"; +import AsyncState from "../enumerations/AsyncState"; +import { + IAuditableRoleMapping, + IRoleMappingChange +} from "../models/RoleMapping"; +import IAsyncStateContainer from "../types/IAsyncStateContainer"; +import Configuration from "./../configuration/Configuration"; + +export default class RoleMappingService { + static getAllRoleMappings = async (): Promise< + IAsyncStateContainer + > => { + try { + const url = Configuration.PrefixIncentiveUrl( + Configuration.Get.urls.incentiveApi.getAllRoleMappings + ); + + const res = await Axios.get(url, { withCredentials: true }); + + return { + state: AsyncState.Success, + data: res.data + }; + } catch (err) { + return { + state: AsyncState.Failure + }; + } + }; + + public static assignNewRoleMapping = async ( + roleMappingData: IRoleMappingChange + ): Promise> => { + try { + const url = Configuration.PrefixIncentiveUrl( + Configuration.Get.urls.incentiveApi.updateRoleMapping + ); + const response = await Axios.post(url, roleMappingData, { + withCredentials: true + }); + return { + state: AsyncState.Success, + data: response.data + }; + } catch (err) { + if ( + err.response?.status === 400 || + err.response?.status === 401 || + err.response?.status === 403 || + err.response?.status === 404 + ) { + return { + state: AsyncState.Failure, + data: err.response.data + }; + } + return { + state: AsyncState.Failure, + data: err?.response.data ?? err?.message ?? "" + }; + } + }; +} diff --git a/LCU.WebApps.Operations.IncentiveManager/src/shared/services/SettingsService.test.ts b/LCU.WebApps.Operations.IncentiveManager/src/shared/services/SettingsService.test.ts new file mode 100644 index 0000000..b0b75b1 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/shared/services/SettingsService.test.ts @@ -0,0 +1,16 @@ +import { flattenRecord } from "./SettingsService"; + +describe("SettingsService - flattenRecords", () => { + it("Flattens Records", () => { + const testObject: { [key: string]: string } = { + Key1: "Value1", + Key2: "Value2" + }; + + let res = flattenRecord(testObject); + expect(res).toEqual([ + ["Key1", "Value1"], + ["Key2", "Value2"] + ]); + }); +}); diff --git a/LCU.WebApps.Operations.IncentiveManager/src/shared/services/SettingsService.ts b/LCU.WebApps.Operations.IncentiveManager/src/shared/services/SettingsService.ts new file mode 100644 index 0000000..3ddcdd5 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/shared/services/SettingsService.ts @@ -0,0 +1,86 @@ +import { IAppRole, ISettings } from "../models/Settings"; +import IAsyncStateContainer from "../types/IAsyncStateContainer"; +import Configuration from "./../configuration/Configuration"; +import Axios from "axios"; +import AsyncState from "./../enumerations/AsyncState"; +import DepartmentService from "./DepartmentService"; + +export default class SettingsService { + static getSettings = async (): Promise> => { + try { + const url = Configuration.PrefixIncentiveUrl( + Configuration.Get.urls.incentiveApi.getAllSettings + ); + + const res = await Axios.get(url, { withCredentials: true }); + + if (res.data && res.data.departments) { + res.data.departments = DepartmentService.fixDeptNameCases( + res.data.departments + ); + } + return { + state: AsyncState.Success, + data: res.data + }; + } catch (err) { + return { + state: AsyncState.Failure + }; + } + }; + + static runEmployeeUpdateSync = async (): Promise< + IAsyncStateContainer + > => { + try { + const url = Configuration.PrefixIncentiveUrl( + Configuration.Get.urls.incentiveApi.runEmployeeUpdateSync + ); + + await Axios.post(url, { withCredentials: true }); + + return { + state: AsyncState.Success + }; + } catch (err) { + if (err.response?.status === 504) { + return { + state: AsyncState.Success + }; + } + return { + state: AsyncState.Failure + }; + } + }; + + static getAssignableRoles( + appRoles: IAppRole[] | undefined, + maxRank: number, + minRank = 0 + ): IAppRole[] { + if (!appRoles || appRoles.length === 0) { + return [] as IAppRole[]; + } + const requiredRank = maxRank ? maxRank : 1; + return appRoles + .filter( + (appRole: IAppRole) => + appRole.rank <= requiredRank && appRole.rank > minRank + ) + .sort((a, b) => a.name.localeCompare(b.name)); + } +} + +export const flattenRecord = (record?: { + [key: string]: string; +}): string[][] => { + if (record === undefined) { + return []; + } + + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + //@ts-ignore + return Object.keys(record).map((key) => [key, record[key]]); +}; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/shared/services/UserAssignmentService.test.ts b/LCU.WebApps.Operations.IncentiveManager/src/shared/services/UserAssignmentService.test.ts new file mode 100644 index 0000000..f3b9334 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/shared/services/UserAssignmentService.test.ts @@ -0,0 +1,117 @@ +import axios from "axios"; +import AsyncState from "../enumerations/AsyncState"; +import { IUserAssignment } from "../models/UserAssignment"; +import UserAssignmentService from "./UserAssignmentService"; +import { IEmployee } from "../models/Employee"; +jest.mock("axios"); + +describe("UserAssignmentService", () => { + describe("getAllUserAssignments", () => { + it("Returns data from the get response", async () => { + const mockReturnData: IUserAssignment[] = [ + { + id: 1, + type: "RegionalManager", + key: "North", + appUser: {} as IEmployee + }, + { + id: 2, + type: "RegionalManager", + key: "South", + appUser: {} as IEmployee + } + ]; + + (axios.get as jest.Mock).mockResolvedValue({ + status: 200, + data: mockReturnData + }); + + const results = await UserAssignmentService.getAllUserAssignments(); + + expect(results).toEqual({ + state: AsyncState.Success, + data: mockReturnData + }); + }); + + it("Returns failure state on get reponse error", async () => { + (axios.get as jest.Mock).mockImplementation(() => { + throw new Error(); + }); + + const results = await UserAssignmentService.getAllUserAssignments(); + + expect(results).toEqual({ + state: AsyncState.Failure + }); + }); + }); + + describe("setRegionalManager", () => { + it("Returns success from the post call", async () => { + (axios.post as jest.Mock).mockResolvedValue({ + status: 204 + }); + + const results = await UserAssignmentService.setRegionalManager({ + employeeId: "123", + key: "SOUTH" + }); + + expect(results).toEqual({ + state: AsyncState.Success + }); + }); + + it("Returns failure from the post call", async () => { + (axios.post as jest.Mock).mockImplementation(() => { + throw new Error(); + }); + + const results = await UserAssignmentService.setRegionalManager({ + employeeId: "123", + key: "SOUTH" + }); + + expect(results).toEqual({ + state: AsyncState.Failure + }); + }); + }); + + describe("filterUserAssignmentByType", () => { + it("returns assignments of a given type from assignments passed in", () => { + const assignments: IUserAssignment[] = [ + { id: 1, key: "one", type: "RegionalManager", appUser: null }, + { id: 2, key: "two", type: "other", appUser: null }, + { id: 3, key: "three", type: "RegionalManager", appUser: null } + ]; + + const results = UserAssignmentService.filterUserAssignmentsByType( + assignments, + "RegionalManager" + ); + + expect(results.length).toBe(2); + expect(results[0].id).toBe(1); + expect(results[1].id).toBe(3); + }); + + it("returns assignments of a given type from assignments passed in", () => { + const assignments: IUserAssignment[] = [ + { id: 1, key: "one", type: "guest", appUser: null }, + { id: 2, key: "two", type: "other", appUser: null }, + { id: 3, key: "three", type: "guest", appUser: null } + ]; + + const results = UserAssignmentService.filterUserAssignmentsByType( + assignments, + "RegionalManager" + ); + + expect(results.length).toBe(0); + }); + }); +}); diff --git a/LCU.WebApps.Operations.IncentiveManager/src/shared/services/UserAssignmentService.ts b/LCU.WebApps.Operations.IncentiveManager/src/shared/services/UserAssignmentService.ts new file mode 100644 index 0000000..67f799c --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/shared/services/UserAssignmentService.ts @@ -0,0 +1,57 @@ +import axios from "axios"; +import Configuration from "../configuration/Configuration"; +import AsyncState from "../enumerations/AsyncState"; +import { IUserAssignment, IUserAssignmentDto } from "../models/UserAssignment"; +import IAsyncStateContainer from "../types/IAsyncStateContainer"; + +export default class UserAssignmentService { + static getAllUserAssignments = async (): Promise< + IAsyncStateContainer + > => { + try { + const url = Configuration.PrefixIncentiveUrl( + Configuration.Get.urls.incentiveApi.getAllUserAssignments + ); + + const res = await axios.get(url, { withCredentials: true }); + + return { state: AsyncState.Success, data: res.data }; + } catch (err) { + return { + state: AsyncState.Failure + }; + } + }; + + static setRegionalManager = async ( + userAssignmentDto: IUserAssignmentDto + ): Promise> => { + try { + const url = Configuration.PrefixIncentiveUrl( + Configuration.Get.urls.incentiveApi.setRegionalManager + ); + + const res = await axios.post(url, userAssignmentDto, { + withCredentials: true + }); + + return { + state: AsyncState.Success, + data: res.data + }; + } catch (err) { + return { + state: AsyncState.Failure + }; + } + }; + + static filterUserAssignmentsByType = ( + userAssignments: IUserAssignment[], + type: string + ) => { + return userAssignments.filter( + (userAssignment) => userAssignment.type === type + ); + }; +} diff --git a/LCU.WebApps.Operations.IncentiveManager/src/shared/testing/mocks/MockAppSettings.ts b/LCU.WebApps.Operations.IncentiveManager/src/shared/testing/mocks/MockAppSettings.ts new file mode 100644 index 0000000..137322d --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/shared/testing/mocks/MockAppSettings.ts @@ -0,0 +1,985 @@ +import { ISettings } from "../../models/Settings"; +import moment from "moment"; + +export const getMockAppSettings = (): ISettings => { + return { + nextBonusPeriod: moment(), + activityStateSet: { + "0": "Created", + "1": "ManualReview", + "2": "PendingReferral", + "3": "Rejected", + "4": "Accepted", + "5": "PendingTransfer", + "6": "Transferred" + }, + activityTypeSet: { "0": "Sale", "1": "Referral", "2": "Other" }, + employees: [], + appRoles: [ + { + name: "Admin", + description: "Admin", + permissions: [ + { + code: "APPROVE_REFERRALS", + description: "User can approve referrals" + }, + { + code: "VIEW_PERFORMANCE_FOR_DEPARTMENT", + description: + "User can see performance for their department" + }, + { + code: "VIEW_PERFORMANCE_FOR_REGION", + description: "User can see performance for their region" + }, + { + code: "VIEW_PERFORMANCE_FOR_BUSINESS_LINE", + description: + "User can see performance for their business line" + }, + { + code: "MANAGE_DEPARTMENT_GOALS", + description: "User can manange their department goals" + }, + { + code: "ASSIGN_PERMISSIONS_TO_ASSOCIATES", + description: + "User can add permissions to an associate's appUser role" + }, + { + code: "ASSIGN_ASSOCIATES_TO_DEPARTMENT", + description: + "User can assign associates to additional departments" + }, + { + code: "TRANSFER_ACTIVITIES_BETWEEN_DEPARTMENTS", + description: + "User can transfer activities between deparments" + }, + { + code: "TRANSFER_ACTIVITIES_BETWEEN_ASSOCIATES", + description: + "User can transfer activities between associates" + }, + { + code: "GENERATE_PAYOUT_FILES", + description: "User can generate payout files for HR" + }, + { + code: "APPROVE_SALES", + description: "User can approve sales" + } + ], + hasCommissions: false, + rank: 1 + }, + { + name: "Mortgage", + description: "Mortgage", + permissions: [ + { + code: "ELIGIBLE_FOR_REFERRALS", + description: "User is eligible for referrals" + }, + { + code: "VIEW_DASHBOARD", + description: + "User can see a dashboard showing their referral totals and their sales totals compared to goal" + } + ], + hasCommissions: true, + rank: 1 + }, + { + name: "Manager", + description: "Manager", + permissions: [ + { + code: "VIEW_PERFORMANCE_FOR_DEPARTMENT", + description: + "User can see performance for their department" + }, + { + code: "MANAGE_DEPARTMENT_GOALS", + description: "User can manange their department goals" + }, + { + code: "ASSIGN_ASSOCIATES_TO_DEPARTMENT", + description: + "User can assign associates to additional departments" + }, + { + code: "TRANSFER_ACTIVITIES_BETWEEN_DEPARTMENTS", + description: + "User can transfer activities between deparments" + }, + { + code: "TRANSFER_ACTIVITIES_BETWEEN_ASSOCIATES", + description: + "User can transfer activities between associates" + } + ], + hasCommissions: true, + rank: 1 + }, + { + name: "LIC", + description: "Landmark Investment Center", + permissions: [ + { + code: "APPROVE_REFERRALS", + description: "User can approve referrals" + }, + { + code: "VIEW_PERFORMANCE_FOR_DEPARTMENT", + description: + "User can see performance for their department" + }, + { + code: "VIEW_PERFORMANCE_FOR_BUSINESS_LINE", + description: + "User can see performance for their business line" + }, + { + code: "APPROVE_SALES", + description: "User can approve sales" + } + ], + hasCommissions: true, + rank: 1 + }, + { + name: "RegionalManager", + description: "Regional Manager", + permissions: [ + { + code: "APPROVE_REFERRALS", + description: "User can approve referrals" + }, + { + code: "VIEW_PERFORMANCE_FOR_DEPARTMENT", + description: + "User can see performance for their department" + }, + { + code: "VIEW_PERFORMANCE_FOR_REGION", + description: "User can see performance for their region" + }, + { + code: "MANAGE_DEPARTMENT_GOALS", + description: "User can manange their department goals" + }, + { + code: "ASSIGN_PERMISSIONS_TO_ASSOCIATES", + description: + "User can add permissions to an associate's appUser role" + }, + { + code: "ASSIGN_ASSOCIATES_TO_DEPARTMENT", + description: + "User can assign associates to additional departments" + }, + { + code: "TRANSFER_ACTIVITIES_BETWEEN_DEPARTMENTS", + description: + "User can transfer activities between deparments" + }, + { + code: "TRANSFER_ACTIVITIES_BETWEEN_ASSOCIATES", + description: + "User can transfer activities between associates" + }, + { + code: "APPROVE_SALES", + description: "User can approve sales" + } + ], + hasCommissions: true, + rank: 1 + }, + { + name: "BM", + description: "Branch Manager", + permissions: [ + { + code: "ELIGIBLE_FOR_REFERRALS", + description: "User is eligible for referrals" + }, + { + code: "HAVE_SALES_GOALS", + description: "User can have sales goals" + }, + { + code: "APPROVE_REFERRALS", + description: "User can approve referrals" + }, + { + code: "VIEW_DASHBOARD", + description: + "User can see a dashboard showing their referral totals and their sales totals compared to goal" + }, + { + code: "VIEW_PERFORMANCE_FOR_DEPARTMENT", + description: + "User can see performance for their department" + }, + { + code: "MANAGE_DEPARTMENT_GOALS", + description: "User can manange their department goals" + }, + { + code: "ASSIGN_ASSOCIATES_TO_DEPARTMENT", + description: + "User can assign associates to additional departments" + }, + { + code: "TRANSFER_ACTIVITIES_BETWEEN_DEPARTMENTS", + description: + "User can transfer activities between deparments" + }, + { + code: "TRANSFER_ACTIVITIES_BETWEEN_ASSOCIATES", + description: + "User can transfer activities between associates" + }, + { + code: "APPROVE_SALES", + description: "User can approve sales" + } + ], + hasCommissions: true, + rank: 1 + }, + { + name: "ABM", + description: "Assistant Branch Manager", + permissions: [ + { + code: "ELIGIBLE_FOR_REFERRALS", + description: "User is eligible for referrals" + }, + { + code: "HAVE_SALES_GOALS", + description: "User can have sales goals" + }, + { + code: "APPROVE_REFERRALS", + description: "User can approve referrals" + }, + { + code: "VIEW_DASHBOARD", + description: + "User can see a dashboard showing their referral totals and their sales totals compared to goal" + }, + { + code: "VIEW_PERFORMANCE_FOR_DEPARTMENT", + description: + "User can see performance for their department" + }, + { + code: "MANAGE_DEPARTMENT_GOALS", + description: "User can manange their department goals" + }, + { + code: "ASSIGN_ASSOCIATES_TO_DEPARTMENT", + description: + "User can assign associates to additional departments" + }, + { + code: "TRANSFER_ACTIVITIES_BETWEEN_DEPARTMENTS", + description: + "User can transfer activities between deparments" + }, + { + code: "TRANSFER_ACTIVITIES_BETWEEN_ASSOCIATES", + description: + "User can transfer activities between associates" + }, + { + code: "APPROVE_SALES", + description: "User can approve sales" + } + ], + hasCommissions: true, + rank: 1 + }, + { + name: "BOM", + description: "Branch Operations Manager", + permissions: [ + { + code: "ELIGIBLE_FOR_REFERRALS", + description: "User is eligible for referrals" + }, + { + code: "HAVE_SALES_GOALS", + description: "User can have sales goals" + }, + { + code: "VIEW_DASHBOARD", + description: + "User can see a dashboard showing their referral totals and their sales totals compared to goal" + }, + { + code: "VIEW_PERFORMANCE_FOR_DEPARTMENT", + description: + "User can see performance for their department" + }, + { + code: "ASSIGN_ASSOCIATES_TO_DEPARTMENT", + description: + "User can assign associates to additional departments" + } + ], + hasCommissions: true, + rank: 1 + }, + { + name: "PFO", + description: "PERS FIN OFFICER", + permissions: [ + { + code: "ELIGIBLE_FOR_REFERRALS", + description: "User is eligible for referrals" + }, + { + code: "HAVE_SALES_GOALS", + description: "User can have sales goals" + }, + { + code: "VIEW_DASHBOARD", + description: + "User can see a dashboard showing their referral totals and their sales totals compared to goal" + } + ], + hasCommissions: true, + rank: 1 + }, + { + name: "Teller", + description: "Teller", + permissions: [ + { + code: "ELIGIBLE_FOR_REFERRALS", + description: "User is eligible for referrals" + }, + { + code: "VIEW_DASHBOARD", + description: + "User can see a dashboard showing their referral totals and their sales totals compared to goal" + } + ], + hasCommissions: true, + rank: 1 + }, + { + name: "MSCTeamLead", + description: "Member Service Center Team Lead", + permissions: [ + { + code: "ELIGIBLE_FOR_REFERRALS", + description: "User is eligible for referrals" + }, + { + code: "HAVE_SALES_GOALS", + description: "User can have sales goals" + }, + { + code: "APPROVE_REFERRALS", + description: "User can approve referrals" + }, + { + code: "VIEW_DASHBOARD", + description: + "User can see a dashboard showing their referral totals and their sales totals compared to goal" + }, + { + code: "VIEW_PERFORMANCE_FOR_DEPARTMENT", + description: + "User can see performance for their department" + }, + { + code: "ASSIGN_ASSOCIATES_TO_DEPARTMENT", + description: + "User can assign associates to additional departments" + }, + { + code: "APPROVE_SALES", + description: "User can approve sales" + } + ], + hasCommissions: true, + rank: 1 + }, + { + name: "MSLO", + description: "Member Services Loan Officer", + permissions: [ + { + code: "ELIGIBLE_FOR_REFERRALS", + description: "User is eligible for referrals" + }, + { + code: "HAVE_SALES_GOALS", + description: "User can have sales goals" + }, + { + code: "VIEW_DASHBOARD", + description: + "User can see a dashboard showing their referral totals and their sales totals compared to goal" + } + ], + hasCommissions: true, + rank: 1 + }, + { + name: "MSR", + description: "Member Services Representative", + permissions: [ + { + code: "ELIGIBLE_FOR_REFERRALS", + description: "User is eligible for referrals" + }, + { + code: "VIEW_DASHBOARD", + description: + "User can see a dashboard showing their referral totals and their sales totals compared to goal" + } + ], + hasCommissions: true, + rank: 1 + }, + { + name: "OpsHelpDesk", + description: "Ops Help Desk", + permissions: [ + { + code: "ELIGIBLE_FOR_REFERRALS", + description: "User is eligible for referrals" + }, + { + code: "VIEW_DASHBOARD", + description: + "User can see a dashboard showing their referral totals and their sales totals compared to goal" + } + ], + hasCommissions: false, + rank: 1 + }, + { + name: "UniversalSpecialist-PFO", + description: "Universal Specialist - PFO", + permissions: [ + { + code: "ELIGIBLE_FOR_REFERRALS", + description: "User is eligible for referrals" + }, + { + code: "HAVE_SALES_GOALS", + description: "User can have sales goals" + }, + { + code: "VIEW_DASHBOARD", + description: + "User can see a dashboard showing their referral totals and their sales totals compared to goal" + } + ], + hasCommissions: true, + rank: 1 + }, + { + name: "UniversalSpecialist-Teller", + description: "Universal Specialist - Teller", + permissions: [ + { + code: "ELIGIBLE_FOR_REFERRALS", + description: "User is eligible for referrals" + }, + { + code: "VIEW_DASHBOARD", + description: + "User can see a dashboard showing their referral totals and their sales totals compared to goal" + } + ], + hasCommissions: true, + rank: 1 + }, + { + name: "InStoreBranchManager", + description: "In-Store Branch Manager", + permissions: [ + { + code: "ELIGIBLE_FOR_REFERRALS", + description: "User is eligible for referrals" + }, + { + code: "HAVE_SALES_GOALS", + description: "User can have sales goals" + }, + { + code: "APPROVE_REFERRALS", + description: "User can approve referrals" + }, + { + code: "VIEW_DASHBOARD", + description: + "User can see a dashboard showing their referral totals and their sales totals compared to goal" + }, + { + code: "VIEW_PERFORMANCE_FOR_DEPARTMENT", + description: + "User can see performance for their department" + }, + { + code: "MANAGE_DEPARTMENT_GOALS", + description: "User can manange their department goals" + }, + { + code: "ASSIGN_ASSOCIATES_TO_DEPARTMENT", + description: + "User can assign associates to additional departments" + }, + { + code: "TRANSFER_ACTIVITIES_BETWEEN_DEPARTMENTS", + description: + "User can transfer activities between deparments" + }, + { + code: "TRANSFER_ACTIVITIES_BETWEEN_ASSOCIATES", + description: + "User can transfer activities between associates" + }, + { + code: "APPROVE_SALES", + description: "User can approve sales" + } + ], + hasCommissions: true, + rank: 1 + }, + { + name: "InStoreAssistantBranchManager", + description: "In-Store Assistant Branch Manager", + permissions: [ + { + code: "ELIGIBLE_FOR_REFERRALS", + description: "User is eligible for referrals" + }, + { + code: "HAVE_SALES_GOALS", + description: "User can have sales goals" + }, + { + code: "APPROVE_REFERRALS", + description: "User can approve referrals" + }, + { + code: "VIEW_DASHBOARD", + description: + "User can see a dashboard showing their referral totals and their sales totals compared to goal" + }, + { + code: "VIEW_PERFORMANCE_FOR_DEPARTMENT", + description: + "User can see performance for their department" + }, + { + code: "MANAGE_DEPARTMENT_GOALS", + description: "User can manange their department goals" + }, + { + code: "ASSIGN_ASSOCIATES_TO_DEPARTMENT", + description: + "User can assign associates to additional departments" + }, + { + code: "TRANSFER_ACTIVITIES_BETWEEN_DEPARTMENTS", + description: + "User can transfer activities between deparments" + }, + { + code: "TRANSFER_ACTIVITIES_BETWEEN_ASSOCIATES", + description: + "User can transfer activities between associates" + }, + { + code: "APPROVE_SALES", + description: "User can approve sales" + } + ], + hasCommissions: true, + rank: 1 + }, + { + name: "ManagementTrainee-Teller", + description: "Management Trainee - Teller", + permissions: [ + { + code: "ELIGIBLE_FOR_REFERRALS", + description: "User is eligible for referrals" + }, + { + code: "VIEW_DASHBOARD", + description: + "User can see a dashboard showing their referral totals and their sales totals compared to goal" + } + ], + hasCommissions: true, + rank: 1 + }, + { + name: "ManagementTrainee-PFO", + description: "Management Trainee - PFO", + permissions: [ + { + code: "ELIGIBLE_FOR_REFERRALS", + description: "User is eligible for referrals" + }, + { + code: "HAVE_SALES_GOALS", + description: "User can have sales goals" + }, + { + code: "VIEW_DASHBOARD", + description: + "User can see a dashboard showing their referral totals and their sales totals compared to goal" + } + ], + hasCommissions: true, + rank: 1 + }, + { + name: "BusinessLineManager", + description: "Business Line Manager", + permissions: [ + { + code: "VIEW_PERFORMANCE_FOR_DEPARTMENT", + description: + "User can see performance for their department" + }, + { + code: "VIEW_PERFORMANCE_FOR_REGION", + description: "User can see performance for their region" + }, + { + code: "VIEW_PERFORMANCE_FOR_BUSINESS_LINE", + description: + "User can see performance for their business line" + }, + { + code: "MANAGE_DEPARTMENT_GOALS", + description: "User can manange their department goals" + }, + { + code: "ASSIGN_PERMISSIONS_TO_ASSOCIATES", + description: + "User can add permissions to an associate's appUser role" + }, + { + code: "ASSIGN_ASSOCIATES_TO_DEPARTMENT", + description: + "User can assign associates to additional departments" + }, + { + code: "TRANSFER_ACTIVITIES_BETWEEN_DEPARTMENTS", + description: + "User can transfer activities between deparments" + }, + { + code: "TRANSFER_ACTIVITIES_BETWEEN_ASSOCIATES", + description: + "User can transfer activities between associates" + } + ], + hasCommissions: true, + rank: 1 + }, + { + name: "UNKNOWN", + description: "Base role has no access", + permissions: [], + hasCommissions: false, + rank: 0 + }, + { + name: "MISSING", + description: "User base role unknown.", + permissions: [], + hasCommissions: false, + rank: 0 + }, + { + name: "Developer", + description: "Developer", + permissions: [ + { + code: "VIEW_DASHBOARD", + description: + "User can see a dashboard showing their referral totals and their sales totals compared to goal" + } + ], + hasCommissions: false, + rank: 0 + } + ], + authorizedReports: [ + { + name: "", + requiredPermissionCode: "", + url: "" + } + ], + businessLines: [ + { + name: "Branch Operations", + code: "BCHOPS", + orgCode: "MEMEXP" + }, + { name: "Member Services", code: "MEMSRV", orgCode: "MEMEXP" }, + { + name: "Operations", + code: "OPS", + orgCode: "MEMEXP" + } + ], + departmentStateSet: { "0": "Active", "1": "Inactive" }, + organizationSet: { MEMEXP: "MemberExperience" }, + payoutProfiles: [], + permissions: [ + { + code: "ELIGIBLE_FOR_REFERRALS", + description: "User is eligible for referrals" + }, + { + code: "HAVE_SALES_GOALS", + description: "User can have sales goals" + }, + { + code: "APPROVE_REFERRALS", + description: "User can approve referrals" + }, + { + code: "VIEW_DASHBOARD", + description: + "User can see a dashboard showing their referral totals and their sales totals compared to goal" + }, + { + code: "VIEW_PERFORMANCE_FOR_DEPARTMENT", + description: "User can see performance for their department" + }, + { + code: "VIEW_PERFORMANCE_FOR_REGION", + description: "User can see performance for their region" + }, + { + code: "VIEW_PERFORMANCE_FOR_BUSINESS_LINE", + description: "User can see performance for their business line" + }, + { + code: "MANAGE_DEPARTMENT_GOALS", + description: "User can manange their department goals" + }, + { + code: "ASSIGN_PERMISSIONS_TO_ASSOCIATES", + description: + "User can add permissions to an associate's appUser role" + }, + { + code: "ASSIGN_ASSOCIATES_TO_DEPARTMENT", + description: + "User can assign associates to additional departments" + }, + { + code: "TRANSFER_ACTIVITIES_BETWEEN_DEPARTMENTS", + description: "User can transfer activities between deparments" + }, + { + code: "TRANSFER_ACTIVITIES_BETWEEN_ASSOCIATES", + description: "User can transfer activities between associates" + }, + { + code: "GENERATE_PAYOUT_FILES", + description: "User can generate payout files for HR" + }, + { + code: "ASSIGN_REGIONAL_MANAGER", + description: "User can set the regional manager for a region" + }, + { code: "APPROVE_SALES", description: "User can approve sales" }, + { + code: "UNKNOWN", + description: + "A Permission Could Not Be Parsed By The System. Possibly Removed." + } + ], + productDetails: [ + { + product: "ATMCard", + title: "ATM Card", + valueType: "Numeric", + minValue: 1, + maxValue: 99, + hasGoals: false, + hasCommissions: true, + isSellable: false, + isReferrable: true, + isNonPaying: true + }, + { + product: "CreditCardBalanceTransfer", + title: "CCBT", + valueType: "Numeric", + minValue: 1, + maxValue: 99, + hasGoals: false, + hasCommissions: true, + isSellable: false, + isReferrable: true, + isNonPaying: false + }, + { + product: "CheckCard", + title: "Check Card", + valueType: "Numeric", + minValue: 1, + maxValue: 99, + hasGoals: false, + hasCommissions: true, + isSellable: false, + isReferrable: true, + isNonPaying: true + }, + { + product: "NewChecking", + title: "New Checking", + valueType: "Numeric", + minValue: 1, + maxValue: 99, + hasGoals: true, + hasCommissions: true, + isSellable: true, + isReferrable: true, + isNonPaying: false + }, + { + product: "CreditCard", + title: "Credit Card", + valueType: "Numeric", + minValue: 1, + maxValue: 99, + hasGoals: true, + hasCommissions: true, + isSellable: false, + isReferrable: true, + isNonPaying: false + }, + { + product: "DirectDeposit", + title: "Direct Deposit", + valueType: "Numeric", + minValue: 1, + maxValue: 99, + hasGoals: false, + hasCommissions: true, + isSellable: false, + isReferrable: true, + isNonPaying: false + }, + { + product: "GAPInsurance", + title: "GAPInsurance", + valueType: "Numeric", + minValue: 1, + maxValue: 99, + hasGoals: true, + hasCommissions: true, + isSellable: true, + isReferrable: false, + isNonPaying: false + }, + { + product: "LIC", + title: "LIC", + valueType: "Numeric", + minValue: 1, + maxValue: 99, + hasGoals: true, + hasCommissions: true, + isSellable: false, + isReferrable: true, + isNonPaying: false + }, + { + product: "Mortgage", + title: "Mortgage", + valueType: "Monetary", + minValue: 1, + maxValue: 99999999, + hasGoals: true, + hasCommissions: true, + isSellable: true, + isReferrable: true, + isNonPaying: false + }, + { + product: "MechanicalBreakdownProtection", + title: "MBP", + valueType: "Numeric", + minValue: 1, + maxValue: 99, + hasGoals: false, + hasCommissions: true, + isSellable: true, + isReferrable: false, + isNonPaying: false + }, + { + product: "Vehicle", + title: "Vehicle", + valueType: "Numeric", + minValue: 1, + maxValue: 99, + hasGoals: true, + hasCommissions: true, + isSellable: true, + isReferrable: true, + isNonPaying: false + } + ], + productSet: [ + "ATMCard", + "CreditCardBalanceTransfer", + "CheckCard", + "NewChecking", + "CreditCard", + "DirectDeposit", + "GAPInsurance", + "LIC", + "Mortgage", + "MechanicalBreakdownProtection", + "Vehicle" + ], + regions: [ + { name: "North", code: "NORTH", businessLineCode: "BCHOPS" }, + { + name: "South", + code: "SOUTH", + businessLineCode: "BCHOPS" + }, + { name: "Central", code: "CENTRAL", businessLineCode: "BCHOPS" } + ], + systemTypeSet: { + "0": "Dna", + "1": "ActivityManager", + "2": "Action", + "3": "Allied", + "4": "ClientCentral", + "5": "Manual", + "6": "Redtail", + "7": "IncentiveManager" + }, + userTypeSet: { "0": "Associate", "1": "System" }, + departments: [] + }; +}; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/shared/testing/mocks/MockDepartmentAssignments.ts b/LCU.WebApps.Operations.IncentiveManager/src/shared/testing/mocks/MockDepartmentAssignments.ts new file mode 100644 index 0000000..9e48dbc --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/shared/testing/mocks/MockDepartmentAssignments.ts @@ -0,0 +1,101 @@ +import { IDepartmentAssignment } from "../../models/DepartmentAssignment"; +import moment from "moment"; +import { UserType } from "../../models/Auditable"; + +export const mockDeptAssignments: IDepartmentAssignment[] = [ + { + id: 0, + employeeId: "id", + departmentId: 1, + startDate: moment("2020-01-01"), + endDate: moment("2021-01-01"), + note: "note", + isHome: true, + createdByType: UserType.Associate, + createdBy: "me", + created: moment("2021-01-02"), + lastUpdated: moment("2021-01-2"), + lastUpdatedByType: UserType.Associate, + lastUpdatedBy: "me", + role: { + name: "ABM", + description: "ABM", + permissions: [], + hasCommissions: true, + rank: 1 + }, + slidingPayout: 100 + }, + { + id: 1, + employeeId: "id1", + departmentId: 100, + startDate: moment("2020-01-01"), + /*hey, you should update this test in the year 10000. + * Hello from 2021, I can't believe react is still a thing. + */ + endDate: moment("10000-01-01"), + note: "note", + isHome: true, + createdByType: UserType.Associate, + createdBy: "me", + created: moment("2021-01-03"), + lastUpdated: moment("2021-01-03"), + lastUpdatedByType: UserType.Associate, + lastUpdatedBy: "me", + role: { + name: "ABM", + description: "ABM", + permissions: [], + hasCommissions: true, + rank: 1 + }, + slidingPayout: 100 + }, + { + id: 2, + employeeId: "id2", + departmentId: 200, + startDate: moment("2020-01-01"), + endDate: undefined, + note: "note", + isHome: true, + createdByType: UserType.Associate, + createdBy: "me", + created: moment("2021-01-04"), + lastUpdated: moment("2021-01-04"), + lastUpdatedByType: UserType.Associate, + lastUpdatedBy: "me", + role: { + name: "ABM", + description: "ABM", + permissions: [], + hasCommissions: true, + rank: 1 + }, + slidingPayout: 100 + }, + { + id: 3, + employeeId: "id2", + departmentId: 200, + startDate: moment("1995-01-01"), + endDate: moment(moment.now()), + note: "note", + isHome: true, + createdByType: UserType.Associate, + createdBy: "me", + created: moment("2021-01-04"), + lastUpdated: moment("2021-01-04"), + lastUpdatedByType: UserType.Associate, + lastUpdatedBy: "me", + role: { + name: "ABM", + description: "ABM", + permissions: [], + hasCommissions: true, + rank: 1 + }, + slidingPayout: 100 + } +]; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/shared/testing/mocks/MockDepartments.ts b/LCU.WebApps.Operations.IncentiveManager/src/shared/testing/mocks/MockDepartments.ts new file mode 100644 index 0000000..86ce561 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/shared/testing/mocks/MockDepartments.ts @@ -0,0 +1,24 @@ +import { IDepartment } from "../../models/Department"; + +export const getMockDepartments = (): IDepartment[] => { + return [ + { + departmentId: 0, + id: 55, + departmentName: "WAUWATOSA", + businessLineCode: "OPS", + state: "Active", + dnaOrgNbr: "1135", + dayForceGlCode: "135" + }, + { + departmentId: 1, + id: 56, + departmentName: "HEADQUARTERS", + businessLineCode: "OPS", + state: "Active", + dnaOrgNbr: "1136", + dayForceGlCode: "136" + } + ]; +}; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/shared/testing/mocks/MockProfileState.ts b/LCU.WebApps.Operations.IncentiveManager/src/shared/testing/mocks/MockProfileState.ts new file mode 100644 index 0000000..4d8c601 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/shared/testing/mocks/MockProfileState.ts @@ -0,0 +1,36 @@ +import { IProfileState } from "../../../reducers/ProfileReducer"; +import PermissionCode from "../../enumerations/PermissionCode"; +import AsyncState from "../../enumerations/AsyncState"; + +export const mockProfileState: IProfileState = { + profile: { + data: { + adEmail: "test@test.com", + personId: 11, + employeeId: "11771", + homeDepartment: "HOME", + baseRole: "QA", + fullName: "John Tester", + role: { + name: "Admin", + description: "admin", + permissions: [ + { + code: PermissionCode.ASSIGN_ASSOCIATES_TO_DEPARTMENT, + description: "ASSIGN_ASSOCIATES_TO_DEPARTMENT" + } + ], + hasCommissions: false, + rank: 1 + }, + additionalPermissions: [], + departments: [], + assignments: [] + }, + state: AsyncState.Success + }, + searchResults: { + state: AsyncState.Success, + data: [] + } +}; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/shared/testing/mocks/MockRoles.ts b/LCU.WebApps.Operations.IncentiveManager/src/shared/testing/mocks/MockRoles.ts new file mode 100644 index 0000000..22a04ae --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/shared/testing/mocks/MockRoles.ts @@ -0,0 +1,94 @@ +import { IAppRole } from "../../models/Settings"; + +export const mockRoles: IAppRole[] = [ + { + name: "Admin", + description: "Admin", + permissions: [ + { + code: "APPROVE_REFERRALS", + description: "User can approve referrals" + }, + { + code: "VIEW_PERFORMANCE_FOR_DEPARTMENT", + description: "User can see performance for their department" + }, + { + code: "VIEW_PERFORMANCE_FOR_REGION", + description: "User can see performance for their region" + }, + { + code: "VIEW_PERFORMANCE_FOR_BUSINESS_LINE", + description: "User can see performance for their business line" + }, + { + code: "MANAGE_DEPARTMENT_GOALS", + description: "User can manange their department goals" + }, + { + code: "ASSIGN_PERMISSIONS_TO_ASSOCIATES", + description: + "User can add permissions to an associate's appUser role" + }, + { + code: "ASSIGN_ASSOCIATES_TO_DEPARTMENT", + description: + "User can assign associates to additional departments" + }, + { + code: "TRANSFER_ACTIVITIES_BETWEEN_DEPARTMENTS", + description: "User can transfer activities between deparments" + }, + { + code: "TRANSFER_ACTIVITIES_BETWEEN_ASSOCIATES", + description: "User can transfer activities between associates" + }, + { + code: "GENERATE_PAYOUT_FILES", + description: "User can generate payout files for HR" + }, + { + code: "APPROVE_SALES", + description: "User can approve sales" + } + ], + hasCommissions: false, + rank: 1 + }, + { + name: "Manager", + description: "Manager", + permissions: [ + { + code: "VIEW_PERFORMANCE_FOR_DEPARTMENT", + description: "User can see performance for their department" + }, + { + code: "MANAGE_DEPARTMENT_GOALS", + description: "User can manange their department goals" + }, + { + code: "ASSIGN_ASSOCIATES_TO_DEPARTMENT", + description: + "User can assign associates to additional departments" + }, + { + code: "TRANSFER_ACTIVITIES_BETWEEN_DEPARTMENTS", + description: "User can transfer activities between deparments" + }, + { + code: "TRANSFER_ACTIVITIES_BETWEEN_ASSOCIATES", + description: "User can transfer activities between associates" + } + ], + hasCommissions: true, + rank: 1 + }, + { + name: "MISSING", + description: "User base role unknown.", + permissions: [], + hasCommissions: false, + rank: 0 + } +]; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/shared/types/IAdalRoleActions.ts b/LCU.WebApps.Operations.IncentiveManager/src/shared/types/IAdalRoleActions.ts new file mode 100644 index 0000000..8d2dd64 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/shared/types/IAdalRoleActions.ts @@ -0,0 +1,4 @@ +export interface IAdalRoleActions { + role: string; + actions: string[]; +} diff --git a/LCU.WebApps.Operations.IncentiveManager/src/shared/types/IAsyncStateContainer.ts b/LCU.WebApps.Operations.IncentiveManager/src/shared/types/IAsyncStateContainer.ts new file mode 100644 index 0000000..113dc8a --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/shared/types/IAsyncStateContainer.ts @@ -0,0 +1,13 @@ +import AsyncState from "../enumerations/AsyncState"; + +interface IAsyncStateContainer { + data?: T; + state: AsyncState; +} + +export interface IAsyncStateContainerStrict extends IAsyncStateContainer { + data: T; +} + +export default IAsyncStateContainer; + diff --git a/LCU.WebApps.Operations.IncentiveManager/src/shared/types/IMemberSecurityMeta.ts b/LCU.WebApps.Operations.IncentiveManager/src/shared/types/IMemberSecurityMeta.ts new file mode 100644 index 0000000..4ff730e --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/shared/types/IMemberSecurityMeta.ts @@ -0,0 +1,5 @@ +export interface IMemberSecurityMeta { + isTwoFactor: boolean; + isMember: boolean; + isIndividual: boolean; +} diff --git a/LCU.WebApps.Operations.IncentiveManager/src/shared/types/IMonthRange.ts b/LCU.WebApps.Operations.IncentiveManager/src/shared/types/IMonthRange.ts new file mode 100644 index 0000000..f174807 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/shared/types/IMonthRange.ts @@ -0,0 +1,6 @@ +export interface IMonthRange { + fromMonth: number; + fromYear: number; + toMonth: number; + toYear: number; +} diff --git a/LCU.WebApps.Operations.IncentiveManager/src/shared/types/IMultiSelectOption.ts b/LCU.WebApps.Operations.IncentiveManager/src/shared/types/IMultiSelectOption.ts new file mode 100644 index 0000000..695b017 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/shared/types/IMultiSelectOption.ts @@ -0,0 +1,6 @@ +export interface MultiSelectOption { + disabled?: boolean; + key?: string; + label: string; + value: any; +} diff --git a/LCU.WebApps.Operations.IncentiveManager/src/shared/types/IPagedResponse.ts b/LCU.WebApps.Operations.IncentiveManager/src/shared/types/IPagedResponse.ts new file mode 100644 index 0000000..2451c29 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/shared/types/IPagedResponse.ts @@ -0,0 +1,13 @@ +export default interface IPagedResponse { + items: T[]; + totalItems: number; + currentPage: number; + totalPages: number; + nextPage?: string; + previousPage?: string; +} + +export interface IPagedResponseCache { + pagedItems: Record>; + cachedLimit?: number; +} diff --git a/LCU.WebApps.Operations.IncentiveManager/src/store/Store.tsx b/LCU.WebApps.Operations.IncentiveManager/src/store/Store.tsx new file mode 100644 index 0000000..01c7490 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/store/Store.tsx @@ -0,0 +1,95 @@ +/* eslint-disable sort-imports */ + +/* Imports from Redux: + applyMiddleware: Applies middleware to the dispatch method of the Redux store + combineReducers: Merges reducers into one + createStore: Creates a Redux store that holds the state tree + Store: The TS Type used for the store, or state tree + */ +import throttle from "lodash.throttle"; +import { applyMiddleware, combineReducers, createStore, Store } from "redux"; +/* + Dev-Tools for debugging and visualizing Redux State in browser +*/ +import { composeWithDevTools } from "redux-devtools-extension"; +/* Thunk + Redux Thunk middleware allows you to write action creators that return a function instead of an action. + The thunk can be used to delay the dispatch of an action, or to dispatch only if a certain condition is met. + The inner function receives the store methods dispatch and getState as parameters. +*/ +import thunk from "redux-thunk"; +import Configuration from "../shared/configuration/Configuration"; +import LocalStorageService from "../shared/services/LocalStorageService"; + +// Import reducers and state type +import { IActivityState, activityReducer } from "../reducers/ActivityReducer"; +import { + ICommissionState, + commissionReducer +} from "../reducers/CommissionReducer"; +import { + IDeadActivityState, + deadActivityReducer +} from "../reducers/DeadActivityReducer"; +import { + IDepartmentState, + departmentReducer +} from "../reducers/DepartmentReducer"; +import { IGoalState, goalReducer } from "../reducers/GoalReducer"; +import { IEmployeeState, employeeReducer } from "../reducers/EmployeeReducer"; +import { IPayoutState, payoutReducer } from "../reducers/PayoutReducer"; +import { IProfileState, profileReducer } from "../reducers/ProfileReducer"; +import { + IRoleMappingState, + roleMappingReducer +} from "../reducers/RoleMappingReducer"; +import { + IUserAssignmentState, + userAssignmentReducer +} from "../reducers/UserAssignmentReducer"; + +// Create an interface for the application state +export interface IAppState { + goalState: IGoalState; + activityState: IActivityState; + departmentState: IDepartmentState; + profileState: IProfileState; + employeeState: IEmployeeState; + commissionState: ICommissionState; + roleMappingState: IRoleMappingState; + payoutState: IPayoutState; + userAssignmentState: IUserAssignmentState; + deadActivityState: IDeadActivityState; +} + +// Create the root reducer +export const rootReducer = () => + combineReducers({ + goalState: goalReducer, + departmentState: departmentReducer, + activityState: activityReducer, + profileState: profileReducer, + employeeState: employeeReducer, + commissionState: commissionReducer, + roleMappingState: roleMappingReducer, + payoutState: payoutReducer, + userAssignmentState: userAssignmentReducer, + deadActivityState: deadActivityReducer + } as any); + +// Create a configure store function of type `IAppState` +export default function configureStore(initState: any): Store { + const enhancer = composeWithDevTools(applyMiddleware(thunk)); + const store = createStore(rootReducer(), initState, enhancer); + + store.subscribe( + throttle(() => { + LocalStorageService.saveAppState( + store.getState(), + Configuration.Get.statesToSave + ); + }, 2000) + ); + + return store; +} diff --git a/LCU.WebApps.Operations.IncentiveManager/src/theme/theme.scss b/LCU.WebApps.Operations.IncentiveManager/src/theme/theme.scss new file mode 100644 index 0000000..eadb79b --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/theme/theme.scss @@ -0,0 +1,4 @@ +//Remap Bootstrap Color Variables Here: +$dark: #0054a4; +$info: #42a0a0; +@import "node_modules/bootstrap/scss/bootstrap.scss"; diff --git a/LCU.WebApps.Operations.IncentiveManager/src/translate/Translate.ts b/LCU.WebApps.Operations.IncentiveManager/src/translate/Translate.ts new file mode 100644 index 0000000..9fb4f01 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/translate/Translate.ts @@ -0,0 +1,15 @@ +import translate from "./translate.json"; + +//Add conditional logic for other translate files here +const SelectTranslateFile = () => { + return translate; +}; +/** + * Static class for access application specific translation data. + */ +export default class Translate { + + static get Get() : typeof translate { + return SelectTranslateFile(); + } +} diff --git a/LCU.WebApps.Operations.IncentiveManager/src/translate/translate.json b/LCU.WebApps.Operations.IncentiveManager/src/translate/translate.json new file mode 100644 index 0000000..f098ea2 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/translate/translate.json @@ -0,0 +1,874 @@ +{ + "containers": { + "progressView": { + "progressTabGroup": { + "progress": "Progress", + "departmentProgress": "Department Progress", + "goalAudit": "Goal Details", + "activityAudit": "Activity Audit", + "activitySales": "Sales", + "activityReferrals": "Referrals" + } + }, + "organizationView": { + "pageTitle": "Organization Overview" + }, + "organizationTable": { + "title": "LMCU Organizations", + "headers": { + "orgCode": "OrgCode", + "orgName": "Name" + } + }, + "businessLineTable": { + "title": "LMCU Business Lines", + "headers": { + "code": "Business Line Code", + "name": "Name", + "orgCode": "Parent OrgCode" + } + }, + "profileResultsTable": { + "title": "User Results", + "headers": { + "name": "Name", + "id": "Employee ID", + "email": "Email" + } + }, + "regionTable": { + "title": "LMCU Regions", + "headers": { + "code": "Region Code", + "name": "Name", + "businessLineCode": "Business Line Code", + "manager": "Regional Manager" + }, + "row": { + "saveButton": "Save", + "editButton": "Edit", + "managerPlaceholder": "Regional Manager ..." + } + }, + "roleManagement": { + "pageTitle": "Role Overview", + "roleTable": { + "title": "LMCU Roles", + "headers": { + "name": "Role", + "description": "Description", + "permissions": "Permissions" + } + } + }, + "employeePage": { + "deptAssignment": { + "deptAssignmentTable": { + "headers": { + "departmentId": "Department", + "startDate": "Start Date", + "endDate": "End Date", + "note": "Note", + "role": "Role", + "payout": "Payout", + "isHome": "Home" + }, + "newDeptAssignmentRow": { + "departmentPlaceholder": "Department...", + "rolePlaceholder": "Role...", + "notePlaceholder": "Add Note...", + "submitButton": "Submit", + "endDate": { + "validation": { + "invalid": "End Date Must Be After Start Date" + } + } + }, + "deptAssignmentTableRow": { + "editButton": "Edit", + "noEndDate": "active" + }, + "deptAssignmentTableEdit": { + "submitButton": "Confirm", + "validation": { + "endDate": "End Date Must Be After Start Date" + } + } + }, + "deptAssignmentAlertBar": { + "loading": "Submitting department assignment.", + "error": "Could not submit department assignment at this time.", + "success": "Successfully saved department assignment.", + "failure": "Failed to save Department Change. Please Try Again or Contact IT." + }, + "deptAssignmentTabGroup": { + "deptAssignmentTable": "Active Department Assignments", + "basicDeptAssignmentTable": "Closed Department Assignments", + "deptAssignmentAduitTable": "Department Assignment Audits" + }, + "deptAssignmentAuditTable": { + "headers": { + "departmentId": "Department", + "startDate": "Start Date", + "endDate": "End Date", + "note": "Note", + "isHome": "Home", + "created": "Created Date", + "createdBy": "Created By", + "createdByType": "Created User Type", + "lastUpdated": "Updated Date", + "lastUpdatedBy": "Updated By", + "UpdatedByType": "Updated User Type" + } + } + } + }, + "goals": { + "department": { + "departmentGoalsControls": { + "exportButton": "Export All Goals", + "exportFileName": "AnnualDepartmentGoals" + }, + "states": { + "processing": "Processing goal changes, please wait.", + "loading": "Loading goal changes, please wait." + }, + "tabs": { + "editor": "Editor", + "detail": "Details" + }, + "buttons": { + "confirmSave": "Confirm and Save" + } + }, + "personnel": { + "personnelGoalsControls": { + "exportButton": "Export", + "exportFileName": "MonthlyPersonnelGoals", + "openDispersal": "Disperse", + "closeDispersal": "Close Dispersal" + }, + "tabs": { + "editor": "Editor", + "detail": "Details", + "exportFileName": "MonthlyPersonnelGoals0" + } + } + }, + "commissionPage": { + "title": "Commissions" + }, + "reportPage": { + "title": "Reports" + }, + "roleMappingPage": { + "title": "Role Mappings" + }, + "payoutProfileView": { + "pageTitle": "Payout Profiles" + }, + "exportPayoutPage": { + "downloadButton": "Download", + "exportButton": "Fetch Payouts", + "exportAccountingButton": "Fetch Account Report", + "instructions": "Select a period to export the payouts or account report for. It will only export generated data. Payouts are generated for a month on the first of the second month after. (May is generated July 1st). If exported before then, it will export any generated payouts for terminated employees. Accounting Reporting is generated for a month back. (June is generated July 5th)" + }, + "endOfEmploymentPage": { + "downloadButton": "Download", + "exportButton": "Fetch Employees", + "instructions": "Select a period to view end of employment for. Use the download button to export their payouts in the HR format." + }, + "employeeDashboard": { + "unassignedRegionalManagerAlert": { + "message": "You are a regional manager but are not assigned to any regions." + } + }, + "activityDetailPage": { + "title": "Activity Details", + "stateChangeNote": "Manual State Change" + }, + "departmentDetailPage": { + "title": "Department", + "unavailable": "Details Unavailable", + "details": "Details", + "progress": "Department Progress", + "managers": "Managers", + "personnel": "Personnel" + } + }, + "components": { + "permissions": { + "permissionPage": { + "defaultTitle": "Default Permissions", + "additionalTitle": "Applied Additional Permissions", + "modifyTitle": "Modify Additional Permissions" + }, + "additionalPermissionsDisplay": { + "noPermissions": "No Permissions", + "granted": "Granted By", + "dateDivider": "on" + }, + "additionalPermissionsForm": { + "availablePermissionsTitle": "Available Permissions", + "additionalPermissionsTitle": "Additional Permissions", + "submit": "Submit Changes" + }, + "additionalPermissionsAlertBar": { + "success": "Updated Additional Permissions", + "loading": "Submitting Permissions Modification", + "failure": "COuld Not Submit Permissions Update. Try again later or contact IT." + } + }, + "pagedContentControls": { + "next": "Next", + "back": "Back", + "pageLabel": "Page" + }, + "statusPages": { + "notFound404Page": { + "title": "Page Not Found", + "message": "Please try accessing the page you want using the navigation bar. If this is an error, contact IT Operations." + } + }, + "layout": { + "navigation": { + "currentUserMessage": "Logged in as ", + "navbarBrand": "Incentive Manager", + "adminDropDown": { + "label": "Menu", + "headers": { + "activity": "Manage Activity", + "configuration": "Configuration", + "dashboard": "Dashboards", + "goals": "Manage Goals", + "personnel": "Manage Personnel", + "tools": "Tools" + }, + "links": { + "activityAudit": "Activity Audit", + "commissions": "Commissions", + "dashboardBusiness": "Business", + "dashboardDepartment": "Department", + "dashboardEmployee": "My Progress", + "dashboardRegion": "Region", + "deadActivityAudit": "Dead Activity Queue", + "departmentGoals": "Department Goals", + "employeeMapping": "Employee Mapping", + "eoeList": "EOE List", + "myProfile": "My Profile", + "newActivity": "New Activity", + "payoutFile": "Export Payout File", + "payoutProfiles": "Payout Profiles", + "personnelGoals": "Personnel Goals", + "profileSearch": "Employee Search", + "organizationHierarchy": "Organization Hierarchy", + "reports": "Reports", + "roleManagement": "Roles", + "roleMapping": "Role Mapping" + } + } + } + }, + "goal": { + "goalDisplay": { + "goalListTitle": "Your goals" + }, + "goalTable": { + "noData": "No Activity Data To Display", + "headers": { + "id": "Id", + "product": "Product", + "value": "Goal", + "departmentId": "Department", + "personId": "Associate", + "lastUpdatedBy": "Updated By", + "lastUpdated": "Updated Date", + "UpdatedByType": "Updated User Type", + "createdBy": "Created By", + "created": "Created Date", + "createdByType": "Created User Type" + } + }, + "goalDispersal": { + "dispersalForm": { + "dispersalStrategyLabel": "Dispersal Strategy", + "dispersalStrategyPlaceholder": "Select a Dispersal Strategy...", + "dispersalStrategyDescLabel": "Description", + "confirmButton": "Confirm Dispersal", + "warningText": "*This action is irreversible", + "description": { + "extrapolateFromExisting": "Use the ratio of last months personal to department goals to calculate this months goals.", + "customWeighted": "Disperse percentage of department product goal equally among personnel with a supplied roles. Overrites goals for only employees with the given roles.", + "placeholder": "Auto disperse department goals To personnel with a predetermined strategy. Select a strategy to begin." + } + }, + "dispersalFormAlertBar": { + "success": { + "message": " Successfully Dispersed Personnel Goals.", + "alerts": "Dispersal Alerts" + }, + "failure": "Failed to Disperse Personnel Goals. Try again later or contact IT.", + "loading": "Dispersing Personnel Goals. Please wait..." + }, + "dispersalReceiptErrorDisplay": { + "userWithoutPermission": "Employee received goals, but no longer has 'HasSalesGoal' permission.", + "userRequiresManualEntry": "Employee is eligible but did not receive goals. May require manual goal change.", + "userNoLongerInDepartment": "Employee is no longer assigned to the department. This may result in inaccurate totals.", + "default": "There was an error while dispersing to this employee. Check goals and totals." + }, + "customWeightedDispersal": { + "customWeightedDispersalForm": { + "productTypeInput": { + "label": "Product Type", + "placeholder": "Product...", + "validation": "Please Select a Value" + }, + "addRoleButton": "+ Add Role", + "submitButton": "Confirm Dispersal", + "submitButtonWarning": "*This action is irreversable", + "addTo100Total": "*Percentages must add up to 100%" + }, + "dispersalWeightInputRow": { + "roleInput": { + "label": "Role", + "placeholder": "Role...", + "validation": "Please Select a Value" + }, + "valueInput": { + "label": "Percentage of Department Goal", + "validation": " Value must be between 0 and 100" + } + } + } + } + }, + "activity": { + "filterForm": { + "productType": { + "label": "Product Type", + "default": "Any Type" + }, + "activityType": { + "label": "Activity Type", + "default": "Any Type" + }, + "activityState": { + "label": "Activity State", + "default": "Any State" + }, + "searchQuery": { + "label": "Search", + "placeHolder": "Search here..." + }, + "lowDate": { + "label": "Start Date" + }, + "employeeId": { + "label": "Employee", + "placeHolder": "Any Employee" + }, + "departmentId": { + "label": "Department", + "placeHolder": "Any Department" + }, + "highDate": { + "label": "High Date" + } + }, + "manualActivityWizard": { + "wizardTitle": "Manually Add An Activity", + "submitButton": "Submit Activity", + "activityWizardAlertBar": { + "submitLoadingMessage": "Submitting New Activity...", + "submitErrorHeader": "Error Submitting Manual Activity", + "submitErrorMessage": "Could not submit new activity at this time. Please try again.", + "submitSuccessMessage": "New Activity Submitted" + }, + "activityProductForm": { + "formLabel": "Activity Information", + "activityType": { + "label": "Activity Type", + "placeholder": "Activity Type..." + }, + "productType": { + "label": "Product", + "placeholder": "Product Type..." + }, + "departmentId": { + "label": "Department", + "placeholder": "Department" + }, + "personId": { + "label": "Person", + "placeholder": "Person...", + "select": "Select Person..." + }, + "activityState": { + "label": "Activity State", + "placeholder": "Activity State..." + }, + "acceptedDateTime": { + "label": "Accepted Date", + "placeholder": "MMDDYYYY" + } + }, + "activitySourceForm": { + "formLabel": "Source Information", + "sourceSystemType": { + "label": "Source System*", + "placeholder": "Source System..." + }, + "sourceSystemId": { + "label": "Source System ID", + "placeholder": "Value from Source..." + }, + "sourceCreatedDateTime": { + "label": "Source Entered DateTime", + "placeholder": "MMDDYYYT:HH:MM:SS" + } + }, + "activityDetailsForm": { + "formLabel": "Activity Details", + "accountNumber": { + "label": "Account Number", + "placeholder": "Value from Source" + }, + "detailSubAccountNumber": { + "label": "Sub Account Number", + "placeholder": "Value from Source" + }, + "memberNumber": { + "label": "Subject Person Number", + "placeholder": "Value From Source" + }, + "orgNumber": { + "label": "Subject Organization Number", + "placeholder": "Value from Source" + }, + "memberName": { + "label": "Name", + "placeholder": "Value From Source" + }, + "accountStatus": { + "label": "Account Status", + "placeholder": "Value From Source" + }, + "accountBalance": { + "label": "Account Balance", + "placeholder": "0.00" + }, + "accountNewBalance": { + "label": "New Account Balance", + "placeholder": "0.00" + }, + "debtProtectionEligible": { + "label": "Is Eligible For Debt Protection", + "placeholder": false + }, + "debtProtectionAddDate": { + "label": "Date Debt Protection Added", + "placeholder": "DateTime" + }, + "debtProtectionRemoveDate": { + "label": "Date Debt Protection Removed", + "placeholder": "DateTime" + }, + "gapInsuranceEligible": { + "label": "Is Eligible For GAP Insurance", + "placeholder": false + }, + "gapInsuranceAddDate": { + "label": "Date GAP Insurance Added", + "placeholder": "DateTime" + }, + "gapInsuranceRemoveDate": { + "label": "Date GAP Insurance Removed", + "placeholder": "DateTime" + }, + "sourceSystemId": { + "label": "Source System ID", + "placeholder": "Value from Source..." + } + } + }, + "activityTable": { + "noData": "No Activity Data To Display", + "headers": { + "id": "Id", + "product": "Product", + "type": "Activity Type", + "sourceSystem": "Source System", + "sourceId": "Source Id", + "sourceCreated": "Source Entered DateTime", + "departmentId": "Credited Department", + "personId": "Credited Associate", + "accepted": "Accepted Date", + "state": "Activity State", + "detailAccountNumber": "Account Number", + "detailSubjectPersonNbr": "Subject Person Number", + "detailSubjectOrgNbr": "Subject Org Number", + "detailMemberName": "Member Name", + "detailAccountStatus": "Account Status", + "detailAccountBalance": "Account Balance", + "detailAccountNewBalance": "New Account Balance", + "lastUpdatedBy": "Updated By", + "lastUpdated": "Updated Date", + "UpdatedByType": "Updated User Type", + "createdBy": "Created By", + "created": "Created Date", + "createdByType": "Created User Type" + } + }, + "activityAuditLog": { + "auditTableTitle": "Activity Audit Log", + "loadingMessage": "Loading Activities...", + "errorMessage": "Failed to Load Activities." + } + }, + "activityDetails": { + "activityDisplay": { + "title": "Activity", + "headers": { + "id": "ID", + "product": "Product", + "type": "Type", + "sourceSystem": "Source System", + "sourceId": "Source ID", + "sourceCreated": "Source Created Date", + "departmentId": "Department ID", + "employeeId": "Employee ID", + "accepted": "Accepted", + "state": "Activity State", + "created": "Created Date", + "createdBy": "Created By", + "createdByType": "Created User Type", + "lastUpdated": "Updated Date", + "lastUpdatedBy": "Updated By", + "lastUpdatedByType": "Updated User Type" + } + }, + "activityDetailDisplay": { + "title": "Details", + "headers": { + "id": "ID", + "accountNumber": "Account Number", + "subAccountNumber": "Sub Account Number", + "subjectPersonNbr": "Subject Person Number", + "subjectOrgNbr": "Subject Organization Number", + "dnaPersonNumber": "Employee Person Number", + "memberName": "Member Name", + "stationName": "Activity Manager Station", + "branchOrgNbr": "Branch Organization Number (DNA)", + "branchOrgName": "Branch Organization Name (DNA)", + "accountStatus": "Account Status", + "accountBalance": "Account Balance", + "accountNewBalance": "Account New Balance", + "debtProtectionAddDate": "Date Debt Protection Added", + "debtProtectionRemoveDate": "Date Debt Protection Removed", + "isDebtProtectionEligible": "Debt Protection Eligible", + "gapInsuranceAddDate": "Date GAP Insurance Added", + "gapInsuranceRemoveDate": "Date GAP Insurance Removed", + "isGapInsuranceEligible": "GAP Insurance Eligible" + } + }, + "activityMetaDataDisplay": { + "title": "Meta Data", + "headers": { + "metaData": "Data Value", + "metaDataType": "Data Type" + }, + "noMetaDataMessage": "No meta data found for this activity." + }, + "activityPayoutsDisplay": { + "title": "Payouts", + "headers": { + "employeeId": "Employee ID", + "departmentId": "Department ID", + "role": "Role", + "period": "Activity Period", + "activityTotal": "Activity Total", + "payoutTotal": "Payout Total" + }, + "noPayoutsMessage": "No payouts found for this activity." + }, + "activityStateChangesTable": { + "title": "Changes History", + "headers": { + "state": "Activity State", + "accepted": "Accepted Date", + "note": "Note", + "created": "Created Date", + "createdBy": "Created By" + }, + "noChangesMessage": "No history of activity state changes found for this activity." + }, + "activityDetailUpdatesTable": { + "title": "Activity Detail Changes History", + "headers": { + "note": "Note", + "created": "Created Date", + "createdBy": "Created By" + }, + "noChangesMessage": "No history of activity detail changes found for this activity." + }, + "activityDetailAlertBar": { + "loadingMessage": "Loading..." + }, + "activityTransferRequest": { + "title": "Transfer Request", + "userSearch": { + "label": "Employee *" + }, + "department": { + "label": "Department *", + "placeholder": "Select Department..." + }, + "submitButton": "Submit Request" + }, + "activityTransferToOwnDepartmentRequest": { + "title": "Transfer Request To Own Department", + "department": { + "label": "Department *", + "placeholder": "Select Department..." + }, + "submitButton": "Submit Request" + }, + "activityTransferApproval": { + "title": "Transfer Approval", + "headers": { + "department": "Department", + "employeeId": "Employee ID", + "approvalFrom": "Approval from " + }, + "buttons": { + "approve": "Approve", + "reject": "Reject" + }, + "pending": "Pending" + } + }, + "deadActivity": { + "deadActivityFilterForm": { + "productType": { + "label": "Product Type", + "default": "Any Type" + }, + "activityType": { + "label": "Activity Type", + "default": "Any Type" + }, + "activityState": { + "label": "Activity State", + "default": "Any State" + }, + "searchQuery": { + "label": "Search", + "placeHolder": "Search here..." + }, + "lowDate": { + "label": "Start Date" + }, + "employeeId": { + "label": "Employee", + "placeHolder": "Any Employee" + }, + "departmentId": { + "label": "Department", + "placeHolder": "Any Department" + }, + "highDate": { + "label": "High Date" + } + }, + "deadActivityTable": { + "noData": "No Dead Activity Data To Display", + "noLivingActivity": "No Living Activity", + "headers": { + "accepted": "Accepted", + "id": "Id", + "product": "Product", + "activityType": "Activity Type", + "source": "Source System", + "sourceId": "SourceId", + "reasonCode": "Reason Code", + "reasonNote": "Reason Note", + "associate": "Credited Associate", + "department": "Credited Department", + "accountNbr": "Account Number", + "livingActivity": "Living Activity", + "memberName": "Member", + "dpEligible": "DP Eligible", + "dpAdded": "DP Added" + } + }, + "deadActivityAuditLog": { + "auditTableTitle": "Dead Activity Queue", + "loadingMessage": "Loading Activities...", + "errorMessage": "Failed to Load Activities." + } + }, + "departmentTable": { + "title": "LMCU Departments", + "headers": [ + "Name", + "Business Line Code", + "Region Code", + "DNA Org Code", + "State" + ], + "departmentTableAlertBar": { + "success": "Successfully Saved Department: ", + "loading": "Saving Department Change...", + "failure": "Failed to save Department Change. Please Try Again or Contact IT." + }, + "departmentTableRow": { + "editButton": "Edit", + "saveUpdateButton": "Save", + "namePlaceholder": "Department Name...", + "orgNbrPlaceholder": "OrgNbr", + "validation": { + "departmentNameLength": "Name must be 3 or more Characters", + "departmentNameUnique": "Name must be unique" + }, + "noneRegion": "None", + "noneBusinessLine": "None" + }, + "addNewDepartmentRow": { + "statePlaceholder": "State...", + "businessLinePlaceholder": "Business Line...", + "regionPlaceholder": "Region...", + "orgCodePlaceholder": "OrgCode...", + "addButton": "Add", + "namePlaceholder": "Department Name...", + "validation": { + "departmentNameLength": "Name must be 3 or more Characters", + "departmentNameUnique": "Name must be unique" + }, + "noneRegion": "None", + "noneBusinessLine": "None" + } + }, + "employee": { + "employeeAlertBar": { + "notFound": { + "title": "Could Not Find Employee With EmployeeID: ", + "message": "Please check the EmployeeID and try again. If you think this is an error contact IT Operations" + }, + "loading": { + "title": "Loading Employee Details..." + }, + "error": { + "title": "Could Not Load Employee Data At This Time", + "message": "Please try again later or Contact IT Operations." + } + } + }, + "payouts": { + "for": "Roles: ", + "each": " each", + "range": "Range", + "pays": "Pays", + "progress": "progress.", + "product": "Product" + }, + "profile": { + "appRoles": "Application Roles", + "username": "Username (Email)", + "employeeId": "Employee ID", + "dayforceRole": "Base Role (Dayforce)", + "assignedRole": "Assigned Role", + "assignedDepartments": "Assigned Departments", + "permissions": "Permissions", + "payouts": "Eligible Payouts", + "loading": "Loading..." + }, + "profileEdit": { + "edit": "Edit", + "employeeEmail": "Employee Email", + "employeeId": "Employee ID", + "employeeFullName": "Employee Full Name", + "header": "Edit Employee Details(Email, ID, & Full Name) To Match Data In HCM System That Cannot Be Auto Updated", + "loading": "Loading...", + "loadingDetailUpdate": "Loading Detail Update...", + "submitButton": "Confirm" + }, + "profileSearch": { + "title": "Employee Search", + "loading": "Loading...", + "placeholder": "Search" + }, + "commissions": { + "tabbedComissionView": { + "ratesTab": "Rates", + "tableTab": "Details" + }, + "commissionTable": { + "headers": { + "id": "Id", + "createdBy": "Created By", + "created": "Created Date", + "createdByType": "Created User Type", + "lastUpdatedBy": "Updated By", + "lastUpdated": "Updated Date", + "lastUpdatedByType": "Updated User Type", + "role": "Role", + "ratePerProduct": "Rate Per Product", + "product": "Product" + } + } + }, + "roleMappings": { + "roleMappingTable": { + "title": "Role Mappings", + "headers": { + "id": "Id", + "applicationRole": "Application Role", + "businessRole": "Business Role", + "createdBy": "Created By", + "created": "Created Date", + "createdByType": "Created User Type", + "lastUpdatedBy": "Updated By", + "lastUpdated": "Updated Date", + "lastUpdatedByType": "Updated User Type" + }, + "applicationRolePlaceholder": "Role...", + "employeeUpdateButton": "Run DayForce Sync", + "employeeUpdateFailureMessage": "DayForce Sync Failed To Finish", + "employeeUpdateRunningMessage": "DayForce Sync Has Been Started", + "employeeUpdateSuccessMessage": "DayForce Sync Finished Successfully", + "editButton": "Edit", + "submitButton": "Confirm" + } + }, + "departmentDetail": { + "assignments": { + "headers": { + "home": "Home/Float", + "name": "Name", + "startDate": "Start Date", + "endDate": "End Date", + "role": "Role", + "payout": "Payout", + "note": "Note" + }, + "home": "Home", + "float": "Float", + "current": "Current", + "missingRole": "Missing Role!", + "noAssignments": "No Assignments" + }, + "detailDisplay": { + "headers": { + "id": "ID", + "region": "Region", + "regionalManager": "Regional Manager", + "status": "Status", + "businessLine": "Business Line", + "activeAssignments": "Active Assignments" + }, + "noRegion": "No region assigned", + "noRegionalManager": "No regional manager assigned" + } + } + } +} diff --git a/LCU.WebApps.Operations.IncentiveManager/src/types/minimatch/index.d.ts b/LCU.WebApps.Operations.IncentiveManager/src/types/minimatch/index.d.ts new file mode 100644 index 0000000..513aab6 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/src/types/minimatch/index.d.ts @@ -0,0 +1,5 @@ +declare module "minimatch" { + function minimatch(path: string, pattern: string, options?: any): boolean; + namespace minimatch {} + export = minimatch; +} diff --git a/LCU.WebApps.Operations.IncentiveManager/tsconfig.json b/LCU.WebApps.Operations.IncentiveManager/tsconfig.json new file mode 100644 index 0000000..a273b0c --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/tsconfig.json @@ -0,0 +1,26 @@ +{ + "compilerOptions": { + "target": "es5", + "lib": [ + "dom", + "dom.iterable", + "esnext" + ], + "allowJs": true, + "skipLibCheck": true, + "esModuleInterop": true, + "allowSyntheticDefaultImports": true, + "strict": true, + "forceConsistentCasingInFileNames": true, + "noFallthroughCasesInSwitch": true, + "module": "esnext", + "moduleResolution": "node", + "resolveJsonModule": true, + "isolatedModules": true, + "noEmit": true, + "jsx": "react-jsx" + }, + "include": [ + "src" + ] +} diff --git a/LCU.WebApps.Operations.IncentiveManager/version.js b/LCU.WebApps.Operations.IncentiveManager/version.js new file mode 100644 index 0000000..b814ae3 --- /dev/null +++ b/LCU.WebApps.Operations.IncentiveManager/version.js @@ -0,0 +1,23 @@ +/*eslint-disable*/ +const { gitDescribeSync } = require('git-describe'); +const { version } = require('./package.json'); +const { resolve, relative } = require('path'); +const { writeFileSync } = require('fs-extra'); + +const gitInfo = gitDescribeSync({ + dirtyMark: false, + dirtySemver: false +}); + +gitInfo.version = version; + +const file = resolve(__dirname, '.', 'src', 'environments', 'version.ts'); +writeFileSync(file, + `// IMPORTANT: THIS FILE IS AUTO GENERATED! DO NOT MANUALLY EDIT OR CHECKIN! +/* tslint:disable */ +export const VERSION = ${JSON.stringify(gitInfo, null, 4)}; +export const BUILD_STAMP = ${JSON.stringify(new Date())}; +/* tslint:enable */ +`, { encoding: 'utf-8' }); + +console.log(`Wrote version info ${gitInfo.raw} to ${relative(resolve(__dirname, '..'), file)}`);