From 2bdc776696c9d8d74fd8d9d4af981006b9c34236 Mon Sep 17 00:00:00 2001 From: Subir Date: Wed, 4 Mar 2020 20:38:42 +0530 Subject: [PATCH 01/21] Moved to new project structure --- .babelrc | 14 - .dockerignore | 13 + .eslintrc | 94 - .gitignore | 37 +- Dockerfile.dev | 8 + Dockerfile.prod | 15 + LICENSE | 2 +- NOTES | 0 README.md | 37 +- app.js | 73 - client/src/actions/Dashboard.js | 10 - client/src/actions/DefectSelector.js | 43 - client/src/actions/Defects.js | 120 - client/src/actions/ExecCycle.js | 191 - client/src/actions/GroupMultiSelect.js | 69 - client/src/actions/Shared.js | 143 - client/src/actions/TestDesign.js | 112 - client/src/actions/TestSelector.js | 58 - client/src/components/App.js | 34 - client/src/components/AppContainer.js | 22 - client/src/components/Dashboard/Dashboard.js | 128 - .../src/components/Dashboard/Dashboard.scss | 73 - client/src/components/Dashboard/index.js | 29 - .../src/components/Defects/AddEditDefect.js | 311 - .../Defects/AddEditDefectContainer.js | 95 - .../components/Defects/AddEditDefectForm.js | 147 - client/src/components/Defects/AddEditView.js | 16 - client/src/components/Defects/DefectList.js | 147 - .../components/Defects/DefectListContainer.js | 50 - .../src/components/Defects/DefectListItem.js | 50 - client/src/components/Defects/Defects.scss | 85 - client/src/components/Defects/LinkedTest.js | 53 - client/src/components/Defects/ListView.js | 10 - .../src/components/Defects/SelectorModal.js | 54 - .../Defects/SelectorModalContainer.js | 22 - client/src/components/Defects/Toolbar.js | 38 - client/src/components/Defects/index.js | 21 - client/src/components/Documents/index.js | 12 - .../components/ExecCycle/AddDefectModal.js | 71 - .../ExecCycle/AddDefectModalActionBar.js | 18 - .../components/ExecCycle/AddEditExecCycle.js | 93 - .../components/ExecCycle/CloneExecCycle.js | 67 - .../src/components/ExecCycle/ExecCycle.scss | 132 - .../src/components/ExecCycle/ExecCycleList.js | 136 - .../ExecCycle/ExecCycleListContainer.js | 45 - .../components/ExecCycle/ExecCycleToolbar.js | 45 - client/src/components/ExecCycle/ListView.js | 25 - .../src/components/ExecCycle/TestImporter.js | 42 - .../ExecCycle/TestImporterContainer.js | 48 - client/src/components/ExecCycle/TestRun.js | 223 - .../components/ExecCycle/TestRunContainer.js | 65 - .../src/components/ExecCycle/TestRunList.js | 184 - .../ExecCycle/TestRunListContainer.js | 73 - .../components/ExecCycle/TestRunListItem.js | 69 - .../components/ExecCycle/TestRunsToolbar.js | 88 - client/src/components/ExecCycle/index.js | 23 - client/src/components/Home/index.js | 83 - .../Shared/Attachment/Attachment.scss | 97 - .../components/Shared/Attachment/ViewEdit.js | 91 - .../src/components/Shared/Attachment/index.js | 104 - .../Shared/ColumnFilter/ColumnFilter.scss | 48 - .../components/Shared/ColumnFilter/index.js | 92 - .../components/Shared/Comment/Comment.scss | 30 - .../components/Shared/Comment/NewComment.js | 113 - client/src/components/Shared/Comment/index.js | 184 - .../Shared/DefectSelector/DefectSelector.scss | 15 - .../Shared/DefectSelector/Selector.js | 101 - .../components/Shared/DefectSelector/index.js | 68 - .../Shared/Description/Description.scss | 51 - .../components/Shared/Description/index.js | 118 - .../Shared/GroupMultiSelect/Breadcrumb.js | 72 - .../Shared/GroupMultiSelect/Filter.js | 20 - .../GroupMultiSelect/GroupMultiSelect.js | 114 - .../GroupMultiSelect/GroupMultiSelect.scss | 248 - .../Shared/GroupMultiSelect/ListA.js | 90 - .../Shared/GroupMultiSelect/ListB.js | 51 - .../Shared/GroupMultiSelect/PathTree.js | 84 - .../Shared/GroupMultiSelect/UnselectedItem.js | 66 - .../Shared/GroupMultiSelect/index.js | 92 - client/src/components/Shared/Header/Header.js | 76 - client/src/components/Shared/Header/index.js | 12 - .../Shared/LoadingOverlay/LoadingOverlay.scss | 34 - .../components/Shared/LoadingOverlay/index.js | 21 - client/src/components/Shared/Title/Title.scss | 17 - client/src/components/Shared/Title/index.js | 105 - .../components/TestDesign/AddEditTestCase.js | 255 - .../TestDesign/AddEditTestCaseContainer.js | 87 - .../components/TestDesign/AddEditTestPlan.js | 81 - .../src/components/TestDesign/AddEditView.js | 20 - .../src/components/TestDesign/LinkedDefect.js | 54 - client/src/components/TestDesign/ListView.js | 23 - .../src/components/TestDesign/TestCaseList.js | 100 - .../TestDesign/TestCaseListContainer.js | 30 - .../components/TestDesign/TestCasesToolbar.js | 38 - .../src/components/TestDesign/TestDesign.scss | 56 - client/src/components/TestDesign/TestPlans.js | 95 - .../src/components/TestDesign/TestPlans.scss | 38 - .../TestDesign/TestPlansContainer.js | 40 - .../components/TestDesign/TestPlansToolbar.js | 45 - .../components/TestDesign/UploadTestCases.js | 53 - client/src/components/TestDesign/index.js | 23 - .../src/components/TestSelector/Selector.js | 116 - .../TestSelector/TestCaseSelector.scss | 15 - client/src/components/TestSelector/index.js | 74 - client/src/constants/DashboardActions.js | 2 - client/src/constants/DefectSelectorActions.js | 10 - client/src/constants/DefectStateColors.js | 8 - client/src/constants/DefectsActions.js | 23 - client/src/constants/ExecCyclesActions.js | 38 - .../src/constants/GroupMultiSelectActions.js | 12 - client/src/constants/SharedActions.js | 24 - client/src/constants/TestDesignActions.js | 20 - client/src/constants/TestRunStateColors.js | 7 - client/src/constants/TestSelectorActions.js | 13 - client/src/initializers/index.js | 50 - client/src/initializers/login.js | 8 - client/src/pages/AddDefect.js | 13 - client/src/pages/AddTest.js | 24 - client/src/pages/Dashboard.js | 13 - client/src/pages/Defects.js | 13 - client/src/pages/Design.js | 13 - client/src/pages/Docs.js | 12 - client/src/pages/EditDefect.js | 23 - client/src/pages/EditTest.js | 26 - client/src/pages/ExecCycle.js | 23 - client/src/pages/ExecCycles.js | 13 - client/src/pages/Home.js | 10 - client/src/pages/TestPlan.js | 23 - client/src/pages/TestRun.js | 26 - client/src/reducers/Dashboard/index.js | 20 - client/src/reducers/DefectSelector/index.js | 88 - client/src/reducers/Defects/addEdit.js | 214 - client/src/reducers/Defects/index.js | 11 - client/src/reducers/Defects/list.js | 91 - client/src/reducers/ExecCycle/addEdit.js | 42 - .../reducers/ExecCycle/importTestDialog.js | 17 - client/src/reducers/ExecCycle/index.js | 17 - client/src/reducers/ExecCycle/list.js | 152 - client/src/reducers/ExecCycle/testRun.js | 115 - client/src/reducers/ExecCycle/testRuns.js | 148 - .../Shared/GroupMultiSelect/filterText.js | 17 - .../reducers/Shared/GroupMultiSelect/index.js | 59 - .../reducers/Shared/GroupMultiSelect/path.js | 34 - .../Shared/GroupMultiSelect/selectedItems.js | 35 - client/src/reducers/Shared/isLoading.js | 28 - client/src/reducers/Shared/session.js | 19 - client/src/reducers/Shared/users.js | 19 - client/src/reducers/TestDesign/addEdit.js | 162 - client/src/reducers/TestDesign/index.js | 11 - client/src/reducers/TestDesign/testPlans.js | 147 - client/src/reducers/TestSelector/index.js | 94 - client/src/reducers/index.js | 29 - client/src/sagas/Dashboard/getSummary.js | 28 - client/src/sagas/Dashboard/index.js | 9 - client/src/sagas/DefectSelector/index.js | 9 - .../sagas/DefectSelector/reqSelectorItems.js | 25 - client/src/sagas/Defects/attachFile.js | 26 - client/src/sagas/Defects/deleteDefect.js | 31 - client/src/sagas/Defects/deleteDefects.js | 35 - client/src/sagas/Defects/index.js | 19 - client/src/sagas/Defects/reqDefect.js | 33 - client/src/sagas/Defects/reqDefects.js | 28 - client/src/sagas/Defects/saveDefect.js | 54 - client/src/sagas/ExecCycle/addNewDefect.js | 31 - client/src/sagas/ExecCycle/cloneExecCycle.js | 34 - client/src/sagas/ExecCycle/importTests.js | 34 - client/src/sagas/ExecCycle/index.js | 35 - client/src/sagas/ExecCycle/linkDefects.js | 33 - .../src/sagas/ExecCycle/reqDeleteExecCycle.js | 31 - .../src/sagas/ExecCycle/reqDeleteTestRuns.js | 35 - client/src/sagas/ExecCycle/reqExecCycle.js | 28 - client/src/sagas/ExecCycle/reqExecCycles.js | 27 - client/src/sagas/ExecCycle/reqTestRun.js | 30 - client/src/sagas/ExecCycle/reqTestRuns.js | 34 - client/src/sagas/ExecCycle/saveExecCycle.js | 49 - client/src/sagas/ExecCycle/saveTestRun.js | 53 - client/src/sagas/ExecCycle/startStopExec.js | 48 - client/src/sagas/ExecCycle/unlinkDefect.js | 29 - .../src/sagas/Shared/attachFileToComment.js | 50 - client/src/sagas/Shared/checkLogin.js | 43 - client/src/sagas/Shared/deleteAttachment.js | 27 - client/src/sagas/Shared/deleteComment.js | 27 - client/src/sagas/Shared/downloadAttachment.js | 20 - client/src/sagas/Shared/index.js | 30 - client/src/sagas/Shared/redirects.js | 36 - client/src/sagas/Shared/reqUsers.js | 27 - client/src/sagas/Shared/saveComment.js | 52 - .../sagas/Shared/setupInvalidateSession.js | 40 - client/src/sagas/Shared/updateAttachment.js | 31 - client/src/sagas/Shared/uploadFiles.js | 34 - client/src/sagas/TestDesign/attachFile.js | 26 - client/src/sagas/TestDesign/deleteTestCase.js | 32 - client/src/sagas/TestDesign/index.js | 23 - client/src/sagas/TestDesign/reqTestCase.js | 30 - client/src/sagas/TestDesign/reqTestCases.js | 34 - client/src/sagas/TestDesign/reqTestPlans.js | 27 - client/src/sagas/TestDesign/saveTestCase.js | 65 - client/src/sagas/TestDesign/saveTestPlan.js | 50 - client/src/sagas/TestDesign/uploadCSV.js | 37 - .../src/sagas/TestSelector/importTestCases.js | 49 - client/src/sagas/TestSelector/index.js | 12 - .../sagas/TestSelector/reqSelectorItems.js | 30 - client/src/sagas/index.js | 21 - client/src/sass/_bootstrap_variables.scss | 870 - client/src/sass/_bootswatch.sandstone.scss | 195 - client/src/sass/_grid.scss | 10 - client/src/sass/_utils.scss | 109 - client/src/sass/index.scss | 62 - client/src/sass/login.scss | 73 - client/src/selectors/Dashboard.js | 3 - client/src/selectors/DefectSelector.js | 5 - client/src/selectors/Defects.js | 36 - client/src/selectors/ExecCycle.js | 90 - client/src/selectors/GroupMultiSelect.js | 67 - client/src/selectors/Shared.js | 6 - client/src/selectors/TestDesign.js | 25 - client/src/selectors/TestSelector.js | 5 - .../DefectSelector/DefectSelectorCache.js | 89 - .../utils/DefectSelector/parseSelectorData.js | 9 - client/src/utils/Defects/buildDefect.js | 25 - client/src/utils/Defects/validateDefect.js | 13 - client/src/utils/ExecCycle/buildExecCycle.js | 14 - client/src/utils/ExecCycle/buildTestRun.js | 19 - .../src/utils/ExecCycle/validateExecCycle.js | 14 - client/src/utils/ExecCycle/validateTestRun.js | 26 - client/src/utils/GroupMultiSelect/flatten.js | 21 - .../GroupMultiSelect/getUnselectedItems.js | 46 - .../src/utils/GroupMultiSelect/isChildOf.js | 18 - .../GroupMultiSelect/shouldShowFilter.js | 6 - client/src/utils/Shared/buildComment.js | 24 - .../src/utils/Shared/createTempAttachment.js | 13 - client/src/utils/Shared/history.js | 5 - client/src/utils/Shared/request.js | 231 - client/src/utils/Shared/store.js | 9 - client/src/utils/TestDesign/buildTestCase.js | 16 - client/src/utils/TestDesign/buildTestPlan.js | 10 - client/src/utils/TestDesign/parseComment.js | 11 - client/src/utils/TestDesign/parseTestCase.js | 14 - .../src/utils/TestDesign/validateTestCase.js | 14 - .../src/utils/TestDesign/validateTestPlan.js | 14 - .../utils/TestSelector/TestSelectorCache.js | 89 - .../utils/TestSelector/parseSelectorData.js | 24 - common/constants/DefectStates.js | 6 - common/constants/ExecCycleCloneTypes.js | 17 - common/constants/TestRunStates.js | 5 - common/constants/permissions.js | 38 - common/images/TestMan.png | Bin 2721 -> 0 bytes common/images/google_signin.png | Bin 3983 -> 0 bytes common/utils/dateFormat.js | 5 - common/utils/fromNow.js | 5 - config-overrides.js | 17 + dist/client/asset-manifest.json | 22 + dist/client/favicon.ico | Bin 0 -> 3150 bytes dist/client/index.html | 1 + dist/client/logo192.png | Bin 0 -> 5347 bytes dist/client/logo512.png | Bin 0 -> 9664 bytes dist/client/manifest.json | 25 + ...nifest.7ad3c67ad1ebe92ef48b7d29f649614c.js | 26 + dist/client/robots.txt | 2 + dist/client/service-worker.js | 39 + .../client/static/css/main.1e813b89.chunk.css | 2 + .../static/css/main.1e813b89.chunk.css.map | 1 + dist/client/static/js/2.2441c286.chunk.js | 3 + .../static/js/2.2441c286.chunk.js.LICENSE.txt | 41 + dist/client/static/js/2.2441c286.chunk.js.map | 1 + dist/client/static/js/main.5239a377.chunk.js | 2 + .../static/js/main.5239a377.chunk.js.map | 1 + .../client/static/js/runtime-main.3940b3dc.js | 2 + .../static/js/runtime-main.3940b3dc.js.map | 1 + dist/constants/actions.js | 19 + dist/constants/apiRoot.js | 3 + dist/server/index.js | 133 + dist/server/models/Item.js | 33 + dist/server/utils/dao.js | 149 + dist/types/actions.js | 2 + dist/types/rootState.js | 2 + dist/types/todo.js | 2 + dist/utils/request.js | 230 + docker-compose.dev.yml | 29 + docker-compose.prod.yml | 22 + jest.config.js | 9 + migrations/20180222192900_initial.js | 108 - migrations/20180303212106_defect_testrun.js | 23 - migrations/20180308012901_user_model.js | 23 - migrations/20180310212215_user_relations.js | 67 - migrations/20180317205928_attachment.js | 26 - nginx.conf | 49 + nodemon-ignore | 8 - npm_scripts/ChalkConfig.js | 7 - npm_scripts/lint.js | 21 - package.json | 144 +- public/favicon.ico | Bin 0 -> 3150 bytes public/index.html | 40 + public/logo192.png | Bin 0 -> 5347 bytes public/logo512.png | Bin 0 -> 9664 bytes public/manifest.json | 25 + public/robots.txt | 2 + scripts/run.sh | 3 + scripts/setup.ts | 27 + server/constants/paths.js | 7 - server/controllers/AttachmentController.js | 98 - server/controllers/AuthController.js | 43 - server/controllers/CommentController.js | 117 - server/controllers/DashboardController.js | 125 - server/controllers/DefectController.js | 201 - server/controllers/ExecCycleController.js | 225 - server/controllers/TestCaseController.js | 191 - server/controllers/TestPlanController.js | 108 - server/controllers/TestRunController.js | 290 - server/controllers/UserController.js | 61 - server/helpers/HttpError.js | 12 - server/helpers/multer.js | 23 - server/helpers/sendHttpError.js | 21 - server/models/Comment.js | 34 - server/models/Defect.js | 48 - server/models/ExecCycle.js | 49 - server/models/File.js | 45 - server/models/RichText.js | 32 - server/models/TestCase.js | 45 - server/models/TestPlan.js | 32 - server/models/TestRun.js | 46 - server/models/User.js | 44 - server/repositories/Comment.js | 142 - server/repositories/Defect.js | 138 - server/repositories/ExecCycle.js | 70 - server/repositories/File.js | 67 - server/repositories/TestCase.js | 129 - server/repositories/TestRun.js | 60 - server/routes/AttachmentRoutes.js | 77 - server/routes/AuthRoutes.js | 35 - server/routes/CommentRoutes.js | 61 - server/routes/DashboardRoutes.js | 22 - server/routes/DefectRoutes.js | 79 - server/routes/ExecCycleRoutes.js | 110 - server/routes/LoginCheckRoute.js | 21 - server/routes/TestCaseRoutes.js | 102 - server/routes/TestPlanRoutes.js | 67 - server/routes/TestRunRoutes.js | 113 - server/routes/UserRoutes.js | 29 - server/routes/index.js | 31 - server/routes/pages.js | 27 - server/views/index.pug | 11 - server/views/layouts/base.pug | 9 - server/views/login.pug | 11 - src/client/actions/index.ts | 92 + src/client/components/App.css | 68 + src/client/components/App.test.tsx | 10 + src/client/components/App.tsx | 61 + src/client/components/CreateTodo.tsx | 92 + src/client/components/TodoList.tsx | 123 + src/client/css/index.css | 55 + src/client/images/logo.svg | 7 + src/client/index.tsx | 44 + src/client/react-app-env.d.ts | 1 + src/client/reducers/index.ts | 15 + src/client/reducers/loading.ts | 24 + src/client/reducers/todos.ts | 40 + src/client/sagas/deleteTodo.ts | 31 + src/client/sagas/getTodos.ts | 23 + src/client/sagas/index.ts | 10 + src/client/sagas/saveTodo.ts | 28 + src/client/sagas/updateTodo.ts | 32 + src/client/selectors/index.ts | 7 + src/client/serviceWorker.ts | 142 + src/client/setupTests.ts | 5 + src/client/store/index.js | 18 + src/client/utils/history.ts | 3 + src/client/utils/spinanim.ts | 14 + src/constants/actions.ts | 17 + src/constants/apiRoot.ts | 1 + src/react-app-env.d.ts | 1 + src/server/index.ts | 54 + src/server/models/Item.ts | 13 + src/server/utils/dao.ts | 65 + src/setupTests.js | 4 + src/types/actions.ts | 61 + src/types/rootState.ts | 6 + src/types/todo.ts | 5 + src/utils/request.ts | 190 + tsconfig.json | 25 + tsconfig.server.json | 74 + webpack.config.dev.js | 141 - webpack.config.prod.js | 112 - wetland.js | 14 - yarn.lock | 13651 ++++++++++------ 385 files changed, 10793 insertions(+), 22812 deletions(-) delete mode 100644 .babelrc create mode 100644 .dockerignore delete mode 100644 .eslintrc create mode 100644 Dockerfile.dev create mode 100644 Dockerfile.prod create mode 100644 NOTES delete mode 100644 app.js delete mode 100644 client/src/actions/Dashboard.js delete mode 100644 client/src/actions/DefectSelector.js delete mode 100644 client/src/actions/Defects.js delete mode 100644 client/src/actions/ExecCycle.js delete mode 100644 client/src/actions/GroupMultiSelect.js delete mode 100644 client/src/actions/Shared.js delete mode 100644 client/src/actions/TestDesign.js delete mode 100644 client/src/actions/TestSelector.js delete mode 100644 client/src/components/App.js delete mode 100644 client/src/components/AppContainer.js delete mode 100644 client/src/components/Dashboard/Dashboard.js delete mode 100644 client/src/components/Dashboard/Dashboard.scss delete mode 100644 client/src/components/Dashboard/index.js delete mode 100644 client/src/components/Defects/AddEditDefect.js delete mode 100644 client/src/components/Defects/AddEditDefectContainer.js delete mode 100644 client/src/components/Defects/AddEditDefectForm.js delete mode 100644 client/src/components/Defects/AddEditView.js delete mode 100644 client/src/components/Defects/DefectList.js delete mode 100644 client/src/components/Defects/DefectListContainer.js delete mode 100644 client/src/components/Defects/DefectListItem.js delete mode 100644 client/src/components/Defects/Defects.scss delete mode 100644 client/src/components/Defects/LinkedTest.js delete mode 100644 client/src/components/Defects/ListView.js delete mode 100644 client/src/components/Defects/SelectorModal.js delete mode 100644 client/src/components/Defects/SelectorModalContainer.js delete mode 100644 client/src/components/Defects/Toolbar.js delete mode 100644 client/src/components/Defects/index.js delete mode 100644 client/src/components/Documents/index.js delete mode 100644 client/src/components/ExecCycle/AddDefectModal.js delete mode 100644 client/src/components/ExecCycle/AddDefectModalActionBar.js delete mode 100644 client/src/components/ExecCycle/AddEditExecCycle.js delete mode 100644 client/src/components/ExecCycle/CloneExecCycle.js delete mode 100644 client/src/components/ExecCycle/ExecCycle.scss delete mode 100644 client/src/components/ExecCycle/ExecCycleList.js delete mode 100644 client/src/components/ExecCycle/ExecCycleListContainer.js delete mode 100644 client/src/components/ExecCycle/ExecCycleToolbar.js delete mode 100644 client/src/components/ExecCycle/ListView.js delete mode 100644 client/src/components/ExecCycle/TestImporter.js delete mode 100644 client/src/components/ExecCycle/TestImporterContainer.js delete mode 100644 client/src/components/ExecCycle/TestRun.js delete mode 100644 client/src/components/ExecCycle/TestRunContainer.js delete mode 100644 client/src/components/ExecCycle/TestRunList.js delete mode 100644 client/src/components/ExecCycle/TestRunListContainer.js delete mode 100644 client/src/components/ExecCycle/TestRunListItem.js delete mode 100644 client/src/components/ExecCycle/TestRunsToolbar.js delete mode 100644 client/src/components/ExecCycle/index.js delete mode 100644 client/src/components/Home/index.js delete mode 100644 client/src/components/Shared/Attachment/Attachment.scss delete mode 100644 client/src/components/Shared/Attachment/ViewEdit.js delete mode 100644 client/src/components/Shared/Attachment/index.js delete mode 100644 client/src/components/Shared/ColumnFilter/ColumnFilter.scss delete mode 100644 client/src/components/Shared/ColumnFilter/index.js delete mode 100644 client/src/components/Shared/Comment/Comment.scss delete mode 100644 client/src/components/Shared/Comment/NewComment.js delete mode 100644 client/src/components/Shared/Comment/index.js delete mode 100644 client/src/components/Shared/DefectSelector/DefectSelector.scss delete mode 100644 client/src/components/Shared/DefectSelector/Selector.js delete mode 100644 client/src/components/Shared/DefectSelector/index.js delete mode 100644 client/src/components/Shared/Description/Description.scss delete mode 100644 client/src/components/Shared/Description/index.js delete mode 100644 client/src/components/Shared/GroupMultiSelect/Breadcrumb.js delete mode 100644 client/src/components/Shared/GroupMultiSelect/Filter.js delete mode 100644 client/src/components/Shared/GroupMultiSelect/GroupMultiSelect.js delete mode 100644 client/src/components/Shared/GroupMultiSelect/GroupMultiSelect.scss delete mode 100644 client/src/components/Shared/GroupMultiSelect/ListA.js delete mode 100644 client/src/components/Shared/GroupMultiSelect/ListB.js delete mode 100644 client/src/components/Shared/GroupMultiSelect/PathTree.js delete mode 100644 client/src/components/Shared/GroupMultiSelect/UnselectedItem.js delete mode 100644 client/src/components/Shared/GroupMultiSelect/index.js delete mode 100644 client/src/components/Shared/Header/Header.js delete mode 100644 client/src/components/Shared/Header/index.js delete mode 100644 client/src/components/Shared/LoadingOverlay/LoadingOverlay.scss delete mode 100644 client/src/components/Shared/LoadingOverlay/index.js delete mode 100644 client/src/components/Shared/Title/Title.scss delete mode 100644 client/src/components/Shared/Title/index.js delete mode 100644 client/src/components/TestDesign/AddEditTestCase.js delete mode 100644 client/src/components/TestDesign/AddEditTestCaseContainer.js delete mode 100644 client/src/components/TestDesign/AddEditTestPlan.js delete mode 100644 client/src/components/TestDesign/AddEditView.js delete mode 100644 client/src/components/TestDesign/LinkedDefect.js delete mode 100644 client/src/components/TestDesign/ListView.js delete mode 100644 client/src/components/TestDesign/TestCaseList.js delete mode 100644 client/src/components/TestDesign/TestCaseListContainer.js delete mode 100644 client/src/components/TestDesign/TestCasesToolbar.js delete mode 100644 client/src/components/TestDesign/TestDesign.scss delete mode 100644 client/src/components/TestDesign/TestPlans.js delete mode 100644 client/src/components/TestDesign/TestPlans.scss delete mode 100644 client/src/components/TestDesign/TestPlansContainer.js delete mode 100644 client/src/components/TestDesign/TestPlansToolbar.js delete mode 100644 client/src/components/TestDesign/UploadTestCases.js delete mode 100644 client/src/components/TestDesign/index.js delete mode 100644 client/src/components/TestSelector/Selector.js delete mode 100644 client/src/components/TestSelector/TestCaseSelector.scss delete mode 100644 client/src/components/TestSelector/index.js delete mode 100644 client/src/constants/DashboardActions.js delete mode 100644 client/src/constants/DefectSelectorActions.js delete mode 100644 client/src/constants/DefectStateColors.js delete mode 100644 client/src/constants/DefectsActions.js delete mode 100644 client/src/constants/ExecCyclesActions.js delete mode 100644 client/src/constants/GroupMultiSelectActions.js delete mode 100644 client/src/constants/SharedActions.js delete mode 100644 client/src/constants/TestDesignActions.js delete mode 100644 client/src/constants/TestRunStateColors.js delete mode 100644 client/src/constants/TestSelectorActions.js delete mode 100644 client/src/initializers/index.js delete mode 100644 client/src/initializers/login.js delete mode 100644 client/src/pages/AddDefect.js delete mode 100644 client/src/pages/AddTest.js delete mode 100644 client/src/pages/Dashboard.js delete mode 100644 client/src/pages/Defects.js delete mode 100644 client/src/pages/Design.js delete mode 100644 client/src/pages/Docs.js delete mode 100644 client/src/pages/EditDefect.js delete mode 100644 client/src/pages/EditTest.js delete mode 100644 client/src/pages/ExecCycle.js delete mode 100644 client/src/pages/ExecCycles.js delete mode 100644 client/src/pages/Home.js delete mode 100644 client/src/pages/TestPlan.js delete mode 100644 client/src/pages/TestRun.js delete mode 100644 client/src/reducers/Dashboard/index.js delete mode 100644 client/src/reducers/DefectSelector/index.js delete mode 100644 client/src/reducers/Defects/addEdit.js delete mode 100644 client/src/reducers/Defects/index.js delete mode 100644 client/src/reducers/Defects/list.js delete mode 100644 client/src/reducers/ExecCycle/addEdit.js delete mode 100644 client/src/reducers/ExecCycle/importTestDialog.js delete mode 100644 client/src/reducers/ExecCycle/index.js delete mode 100644 client/src/reducers/ExecCycle/list.js delete mode 100644 client/src/reducers/ExecCycle/testRun.js delete mode 100644 client/src/reducers/ExecCycle/testRuns.js delete mode 100644 client/src/reducers/Shared/GroupMultiSelect/filterText.js delete mode 100644 client/src/reducers/Shared/GroupMultiSelect/index.js delete mode 100644 client/src/reducers/Shared/GroupMultiSelect/path.js delete mode 100644 client/src/reducers/Shared/GroupMultiSelect/selectedItems.js delete mode 100644 client/src/reducers/Shared/isLoading.js delete mode 100644 client/src/reducers/Shared/session.js delete mode 100644 client/src/reducers/Shared/users.js delete mode 100644 client/src/reducers/TestDesign/addEdit.js delete mode 100644 client/src/reducers/TestDesign/index.js delete mode 100644 client/src/reducers/TestDesign/testPlans.js delete mode 100644 client/src/reducers/TestSelector/index.js delete mode 100644 client/src/reducers/index.js delete mode 100644 client/src/sagas/Dashboard/getSummary.js delete mode 100644 client/src/sagas/Dashboard/index.js delete mode 100644 client/src/sagas/DefectSelector/index.js delete mode 100644 client/src/sagas/DefectSelector/reqSelectorItems.js delete mode 100644 client/src/sagas/Defects/attachFile.js delete mode 100644 client/src/sagas/Defects/deleteDefect.js delete mode 100644 client/src/sagas/Defects/deleteDefects.js delete mode 100644 client/src/sagas/Defects/index.js delete mode 100644 client/src/sagas/Defects/reqDefect.js delete mode 100644 client/src/sagas/Defects/reqDefects.js delete mode 100644 client/src/sagas/Defects/saveDefect.js delete mode 100644 client/src/sagas/ExecCycle/addNewDefect.js delete mode 100644 client/src/sagas/ExecCycle/cloneExecCycle.js delete mode 100644 client/src/sagas/ExecCycle/importTests.js delete mode 100644 client/src/sagas/ExecCycle/index.js delete mode 100644 client/src/sagas/ExecCycle/linkDefects.js delete mode 100644 client/src/sagas/ExecCycle/reqDeleteExecCycle.js delete mode 100644 client/src/sagas/ExecCycle/reqDeleteTestRuns.js delete mode 100644 client/src/sagas/ExecCycle/reqExecCycle.js delete mode 100644 client/src/sagas/ExecCycle/reqExecCycles.js delete mode 100644 client/src/sagas/ExecCycle/reqTestRun.js delete mode 100644 client/src/sagas/ExecCycle/reqTestRuns.js delete mode 100644 client/src/sagas/ExecCycle/saveExecCycle.js delete mode 100644 client/src/sagas/ExecCycle/saveTestRun.js delete mode 100644 client/src/sagas/ExecCycle/startStopExec.js delete mode 100644 client/src/sagas/ExecCycle/unlinkDefect.js delete mode 100644 client/src/sagas/Shared/attachFileToComment.js delete mode 100644 client/src/sagas/Shared/checkLogin.js delete mode 100644 client/src/sagas/Shared/deleteAttachment.js delete mode 100644 client/src/sagas/Shared/deleteComment.js delete mode 100644 client/src/sagas/Shared/downloadAttachment.js delete mode 100644 client/src/sagas/Shared/index.js delete mode 100644 client/src/sagas/Shared/redirects.js delete mode 100644 client/src/sagas/Shared/reqUsers.js delete mode 100644 client/src/sagas/Shared/saveComment.js delete mode 100644 client/src/sagas/Shared/setupInvalidateSession.js delete mode 100644 client/src/sagas/Shared/updateAttachment.js delete mode 100644 client/src/sagas/Shared/uploadFiles.js delete mode 100644 client/src/sagas/TestDesign/attachFile.js delete mode 100644 client/src/sagas/TestDesign/deleteTestCase.js delete mode 100644 client/src/sagas/TestDesign/index.js delete mode 100644 client/src/sagas/TestDesign/reqTestCase.js delete mode 100644 client/src/sagas/TestDesign/reqTestCases.js delete mode 100644 client/src/sagas/TestDesign/reqTestPlans.js delete mode 100644 client/src/sagas/TestDesign/saveTestCase.js delete mode 100644 client/src/sagas/TestDesign/saveTestPlan.js delete mode 100644 client/src/sagas/TestDesign/uploadCSV.js delete mode 100644 client/src/sagas/TestSelector/importTestCases.js delete mode 100644 client/src/sagas/TestSelector/index.js delete mode 100644 client/src/sagas/TestSelector/reqSelectorItems.js delete mode 100644 client/src/sagas/index.js delete mode 100644 client/src/sass/_bootstrap_variables.scss delete mode 100644 client/src/sass/_bootswatch.sandstone.scss delete mode 100644 client/src/sass/_grid.scss delete mode 100644 client/src/sass/_utils.scss delete mode 100644 client/src/sass/index.scss delete mode 100644 client/src/sass/login.scss delete mode 100644 client/src/selectors/Dashboard.js delete mode 100644 client/src/selectors/DefectSelector.js delete mode 100644 client/src/selectors/Defects.js delete mode 100644 client/src/selectors/ExecCycle.js delete mode 100644 client/src/selectors/GroupMultiSelect.js delete mode 100644 client/src/selectors/Shared.js delete mode 100644 client/src/selectors/TestDesign.js delete mode 100644 client/src/selectors/TestSelector.js delete mode 100644 client/src/utils/DefectSelector/DefectSelectorCache.js delete mode 100644 client/src/utils/DefectSelector/parseSelectorData.js delete mode 100644 client/src/utils/Defects/buildDefect.js delete mode 100644 client/src/utils/Defects/validateDefect.js delete mode 100644 client/src/utils/ExecCycle/buildExecCycle.js delete mode 100644 client/src/utils/ExecCycle/buildTestRun.js delete mode 100644 client/src/utils/ExecCycle/validateExecCycle.js delete mode 100644 client/src/utils/ExecCycle/validateTestRun.js delete mode 100644 client/src/utils/GroupMultiSelect/flatten.js delete mode 100644 client/src/utils/GroupMultiSelect/getUnselectedItems.js delete mode 100644 client/src/utils/GroupMultiSelect/isChildOf.js delete mode 100644 client/src/utils/GroupMultiSelect/shouldShowFilter.js delete mode 100644 client/src/utils/Shared/buildComment.js delete mode 100644 client/src/utils/Shared/createTempAttachment.js delete mode 100644 client/src/utils/Shared/history.js delete mode 100644 client/src/utils/Shared/request.js delete mode 100644 client/src/utils/Shared/store.js delete mode 100644 client/src/utils/TestDesign/buildTestCase.js delete mode 100644 client/src/utils/TestDesign/buildTestPlan.js delete mode 100644 client/src/utils/TestDesign/parseComment.js delete mode 100644 client/src/utils/TestDesign/parseTestCase.js delete mode 100644 client/src/utils/TestDesign/validateTestCase.js delete mode 100644 client/src/utils/TestDesign/validateTestPlan.js delete mode 100644 client/src/utils/TestSelector/TestSelectorCache.js delete mode 100644 client/src/utils/TestSelector/parseSelectorData.js delete mode 100644 common/constants/DefectStates.js delete mode 100644 common/constants/ExecCycleCloneTypes.js delete mode 100644 common/constants/TestRunStates.js delete mode 100644 common/constants/permissions.js delete mode 100644 common/images/TestMan.png delete mode 100644 common/images/google_signin.png delete mode 100644 common/utils/dateFormat.js delete mode 100644 common/utils/fromNow.js create mode 100644 config-overrides.js create mode 100644 dist/client/asset-manifest.json create mode 100644 dist/client/favicon.ico create mode 100644 dist/client/index.html create mode 100644 dist/client/logo192.png create mode 100644 dist/client/logo512.png create mode 100644 dist/client/manifest.json create mode 100644 dist/client/precache-manifest.7ad3c67ad1ebe92ef48b7d29f649614c.js create mode 100644 dist/client/robots.txt create mode 100644 dist/client/service-worker.js create mode 100644 dist/client/static/css/main.1e813b89.chunk.css create mode 100644 dist/client/static/css/main.1e813b89.chunk.css.map create mode 100644 dist/client/static/js/2.2441c286.chunk.js create mode 100644 dist/client/static/js/2.2441c286.chunk.js.LICENSE.txt create mode 100644 dist/client/static/js/2.2441c286.chunk.js.map create mode 100644 dist/client/static/js/main.5239a377.chunk.js create mode 100644 dist/client/static/js/main.5239a377.chunk.js.map create mode 100644 dist/client/static/js/runtime-main.3940b3dc.js create mode 100644 dist/client/static/js/runtime-main.3940b3dc.js.map create mode 100644 dist/constants/actions.js create mode 100644 dist/constants/apiRoot.js create mode 100644 dist/server/index.js create mode 100644 dist/server/models/Item.js create mode 100644 dist/server/utils/dao.js create mode 100644 dist/types/actions.js create mode 100644 dist/types/rootState.js create mode 100644 dist/types/todo.js create mode 100644 dist/utils/request.js create mode 100644 docker-compose.dev.yml create mode 100644 docker-compose.prod.yml create mode 100644 jest.config.js delete mode 100644 migrations/20180222192900_initial.js delete mode 100644 migrations/20180303212106_defect_testrun.js delete mode 100644 migrations/20180308012901_user_model.js delete mode 100644 migrations/20180310212215_user_relations.js delete mode 100644 migrations/20180317205928_attachment.js create mode 100644 nginx.conf delete mode 100644 nodemon-ignore delete mode 100644 npm_scripts/ChalkConfig.js delete mode 100644 npm_scripts/lint.js create mode 100644 public/favicon.ico create mode 100644 public/index.html create mode 100644 public/logo192.png create mode 100644 public/logo512.png create mode 100644 public/manifest.json create mode 100644 public/robots.txt create mode 100644 scripts/run.sh create mode 100644 scripts/setup.ts delete mode 100644 server/constants/paths.js delete mode 100644 server/controllers/AttachmentController.js delete mode 100644 server/controllers/AuthController.js delete mode 100644 server/controllers/CommentController.js delete mode 100644 server/controllers/DashboardController.js delete mode 100644 server/controllers/DefectController.js delete mode 100644 server/controllers/ExecCycleController.js delete mode 100644 server/controllers/TestCaseController.js delete mode 100644 server/controllers/TestPlanController.js delete mode 100644 server/controllers/TestRunController.js delete mode 100644 server/controllers/UserController.js delete mode 100644 server/helpers/HttpError.js delete mode 100644 server/helpers/multer.js delete mode 100644 server/helpers/sendHttpError.js delete mode 100644 server/models/Comment.js delete mode 100644 server/models/Defect.js delete mode 100644 server/models/ExecCycle.js delete mode 100644 server/models/File.js delete mode 100644 server/models/RichText.js delete mode 100644 server/models/TestCase.js delete mode 100644 server/models/TestPlan.js delete mode 100644 server/models/TestRun.js delete mode 100644 server/models/User.js delete mode 100644 server/repositories/Comment.js delete mode 100644 server/repositories/Defect.js delete mode 100644 server/repositories/ExecCycle.js delete mode 100644 server/repositories/File.js delete mode 100644 server/repositories/TestCase.js delete mode 100644 server/repositories/TestRun.js delete mode 100644 server/routes/AttachmentRoutes.js delete mode 100644 server/routes/AuthRoutes.js delete mode 100644 server/routes/CommentRoutes.js delete mode 100644 server/routes/DashboardRoutes.js delete mode 100644 server/routes/DefectRoutes.js delete mode 100644 server/routes/ExecCycleRoutes.js delete mode 100644 server/routes/LoginCheckRoute.js delete mode 100644 server/routes/TestCaseRoutes.js delete mode 100644 server/routes/TestPlanRoutes.js delete mode 100644 server/routes/TestRunRoutes.js delete mode 100644 server/routes/UserRoutes.js delete mode 100644 server/routes/index.js delete mode 100644 server/routes/pages.js delete mode 100644 server/views/index.pug delete mode 100644 server/views/layouts/base.pug delete mode 100644 server/views/login.pug create mode 100644 src/client/actions/index.ts create mode 100644 src/client/components/App.css create mode 100644 src/client/components/App.test.tsx create mode 100644 src/client/components/App.tsx create mode 100644 src/client/components/CreateTodo.tsx create mode 100644 src/client/components/TodoList.tsx create mode 100644 src/client/css/index.css create mode 100644 src/client/images/logo.svg create mode 100644 src/client/index.tsx create mode 100644 src/client/react-app-env.d.ts create mode 100644 src/client/reducers/index.ts create mode 100644 src/client/reducers/loading.ts create mode 100644 src/client/reducers/todos.ts create mode 100644 src/client/sagas/deleteTodo.ts create mode 100644 src/client/sagas/getTodos.ts create mode 100644 src/client/sagas/index.ts create mode 100644 src/client/sagas/saveTodo.ts create mode 100644 src/client/sagas/updateTodo.ts create mode 100644 src/client/selectors/index.ts create mode 100644 src/client/serviceWorker.ts create mode 100644 src/client/setupTests.ts create mode 100644 src/client/store/index.js create mode 100644 src/client/utils/history.ts create mode 100644 src/client/utils/spinanim.ts create mode 100644 src/constants/actions.ts create mode 100644 src/constants/apiRoot.ts create mode 100644 src/react-app-env.d.ts create mode 100644 src/server/index.ts create mode 100644 src/server/models/Item.ts create mode 100644 src/server/utils/dao.ts create mode 100644 src/setupTests.js create mode 100644 src/types/actions.ts create mode 100644 src/types/rootState.ts create mode 100644 src/types/todo.ts create mode 100644 src/utils/request.ts create mode 100644 tsconfig.json create mode 100644 tsconfig.server.json delete mode 100644 webpack.config.dev.js delete mode 100644 webpack.config.prod.js delete mode 100644 wetland.js diff --git a/.babelrc b/.babelrc deleted file mode 100644 index 8634826..0000000 --- a/.babelrc +++ /dev/null @@ -1,14 +0,0 @@ -{ - "presets": [ "env", "stage-1", "react" ], - "env": { - "development": { - "plugins": [ - "react-hot-loader/babel", - "transform-runtime" - ] - }, - "production": { - "plugins": [ "transform-runtime" ] - } - } -} diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..0db1c7a --- /dev/null +++ b/.dockerignore @@ -0,0 +1,13 @@ +node_modules +npm-debug.log +yarn-debug.log +Dockerfile* +docker-compose* +.dockerignore +.git +.gitignore +dist +README.md +LICENSE +.DS_Store +.env* diff --git a/.eslintrc b/.eslintrc deleted file mode 100644 index 023f35a..0000000 --- a/.eslintrc +++ /dev/null @@ -1,94 +0,0 @@ -{ - "extends": [ - "eslint:recommended", - "plugin:import/errors", - "plugin:import/warnings" - ], - "plugins": [ - "react" - ], - "parserOptions": { - "ecmaVersion": 8, - "sourceType": "module", - "ecmaFeatures": { - "jsx": true, - "experimentalObjectRestSpread": true - } - }, - "env": { - "es6": true, - "browser": true, - "node": true, - "jquery": true, - "mocha": true - }, - "rules": { - "brace-style": [1,"1tbs",{"allowSingleLine":true}], - "camelcase": [1,{"properties":"always"}], - "comma-spacing": [1,{"after":true}], - "comma-style": 1, - "consistent-this": [2,"that"], - "constructor-super": 1, - "eol-last": 1, - "indent": [2, 4, {"SwitchCase": 1}], - "quotes": [1,"double"], - "no-class-assign": 2, - "no-console": 1, - "no-const-assign": 2, - "no-debugger": 1, - "no-this-before-super": 2, - "no-trailing-spaces": 1, - "no-underscore-dangle": 0, - "no-alert": 0, - "no-lone-blocks": 0, - "no-var": 1, - "prefer-const": 1, - "prefer-spread": 1, - "require-yield": 2, - "semi": [1, "always"], - "jsx-quotes": 1, - "react/display-name": [ 1, {"ignoreTranspilerName": false }], - "react/forbid-prop-types": [1, {"forbid": ["any"]}], - "react/jsx-boolean-value": 0, - "react/jsx-closing-bracket-location": 0, - "react/jsx-curly-spacing": 1, - "react/jsx-indent-props": 0, - "react/jsx-key": 1, - "react/jsx-max-props-per-line": 0, - "react/jsx-no-bind": 0, - "react/jsx-no-duplicate-props": 1, - "react/jsx-no-literals": 0, - "react/jsx-no-undef": 1, - "react/jsx-pascal-case": 1, - "react/jsx-sort-prop-types": 0, - "react/jsx-sort-props": 0, - "react/jsx-uses-react": 1, - "react/jsx-uses-vars": 1, - "react/no-danger": 1, - "react/no-did-mount-set-state": 1, - "react/no-did-update-set-state": 1, - "react/no-direct-mutation-state": 1, - "react/no-multi-comp": 1, - "react/no-unknown-property": 1, - "react/prefer-es6-class": 1, - "react/prop-types": 1, - "react/react-in-jsx-scope": 1, - "import/extensions": 1, - "react/self-closing-comp": 1, - "react/sort-comp": 1, - "react/jsx-wrap-multilines": 1 - }, - "globals": { - }, - "settings": { - "import/resolver": { - "node": { - "moduleDirectory": [ - "node_modules", - ".", - "client/src" - ] - } - } - } -} diff --git a/.gitignore b/.gitignore index 72d093a..fed25c1 100644 --- a/.gitignore +++ b/.gitignore @@ -1,27 +1,20 @@ -# Logs -logs -*.log -npm-debug.log* -yarn-debug.log* -yarn-error.log* +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. -# Runtime data -pids -*.pid -*.seed -*.pid.lock +# dependencies +/node_modules +/.pnp +.pnp.js -# Directory for instrumented libs generated by jscoverage/JSCover -lib-cov +# testing +/coverage -# Coverage directory used by tools like istanbul -coverage +# production +/dist -node_modules/ -.npm -.yarn-integrity +# misc +.DS_Store +.env.* -.data -.db -client/dist -.env +npm-debug.log* +yarn-debug.log* +yarn-error.log* diff --git a/Dockerfile.dev b/Dockerfile.dev new file mode 100644 index 0000000..8c5b224 --- /dev/null +++ b/Dockerfile.dev @@ -0,0 +1,8 @@ +FROM node:alpine + +WORKDIR /usr/src/webapp + +COPY package.json . +COPY yarn.lock . +RUN yarn +COPY . . diff --git a/Dockerfile.prod b/Dockerfile.prod new file mode 100644 index 0000000..1ebdd4b --- /dev/null +++ b/Dockerfile.prod @@ -0,0 +1,15 @@ +FROM nginx + +RUN apt-get update +RUN apt-get install -y nodejs curl +RUN curl -o- -L https://yarnpkg.com/install.sh | bash + +RUN rm /etc/nginx/conf.d/default.conf +COPY nginx.conf /etc/nginx/nginx.conf + +WORKDIR /usr/src/webapp +COPY package.json . +COPY yarn.lock . +RUN ~/.yarn/bin/yarn +COPY . . +RUN ~/.yarn/bin/yarn build diff --git a/LICENSE b/LICENSE index daab263..e891e0d 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2018 Subir +Copyright (c) 2020 Subir Chowdhuri Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/NOTES b/NOTES new file mode 100644 index 0000000..e69de29 diff --git a/README.md b/README.md index 12d9996..3860be7 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@

Logo TestMan - + License MIT npm >= 8.x

@@ -10,24 +10,33 @@ Minimal test-case management tool ## Development -Install deps: -``` -npm install -``` -Generate production bundles: -``` -npm run build -``` +1. Create the `.env.dev` file and add `MYSQL_ROOT_PASSWORD=` -Start the server: -``` -npm start +2. Get the development environment up and running: + +```bash +$ docker-compose -f docker-compose.dev.yml up --build +$ docker-compose -f docker-compose.dev.yml exec db mysql -u root -p --execute="CREATE DATABASE testman;" +$ docker-compose -f docker-compose.dev.yml exec webapp node_modules/.bin/ts-node -P tsconfig.server.json scripts/setup.ts +$ docker-compose -f docker-compose.dev.yml restart ``` -Head to http://localhost:3200/ +Open `http://localhost:3000` + +## Deploy + +1. Create the `.env.prod` file and add `MYSQL_ROOT_PASSWORD=` +2. Create the production docker image: + +```bash +$ docker-compose -f docker-compose.prod.yml up --build +$ docker-compose -f docker-compose.prod.yml exec db mysql -u root -p --execute="CREATE DATABASE testman;" +$ docker-compose -f docker-compose.prod.yml exec webapp node_modules/.bin/ts-node -P tsconfig.server.json scripts/setup.ts +$ docker-compose -f docker-compose.prod.yml restart +``` -PS: TestMan works and is stable. Though, I never got around to completing the [Roadmap to v1.0](https://github.com/schowdhuri/testman/projects/1?fullscreen=true) +The app is served on `http://localhost/` ## License MIT diff --git a/app.js b/app.js deleted file mode 100644 index 60fd580..0000000 --- a/app.js +++ /dev/null @@ -1,73 +0,0 @@ -require("dotenv").config(); -const path = require("path"); -const express = require("express"); -const expressSession = require("express-session"); -const bodyParser = require("body-parser"); -const cookieParser = require("cookie-parser"); -const expressWetland = require("express-wetland"); -const passport = require("passport"); -const { Wetland } = require("wetland"); - -const wetlandConfig = require("./wetland"); -const routes = require("./server/routes"); -const AuthController = require("./server/controllers/AuthController"); - -const { - STATIC_DIR, - STATIC_DIR_ALIAS, - UPLOAD_DIR, - UPLOAD_DIR_ALIAS -} = require("./server/constants/paths"); - -const PORT = 3200; - -const app = express(); -const wetland = new Wetland(wetlandConfig); - -// Middleware -app.use(bodyParser.json()); -app.use(bodyParser.urlencoded({ extended: true })); -app.use(UPLOAD_DIR_ALIAS, express.static(UPLOAD_DIR)); -app.use(STATIC_DIR_ALIAS, express.static(STATIC_DIR)); -app.use(cookieParser()); -app.use(expressWetland(wetland)); - -// express session -app.use(expressSession({ - secret: "brokenrecord", - resave: false, - saveUninitialized: false -})); -// auth setup -app.use(passport.initialize()); -app.use(passport.session()); -passport.use(AuthController.createStrategy(wetland)); -passport.serializeUser(AuthController.serializeUser); -passport.deserializeUser(AuthController.createDeserializer(wetland)); - -// view engine -app.set("views", path.join(__dirname, "server", "views")); -app.set("view engine", "pug"); - -// Routes -routes(app); - -// HMR setup (dev-only) -if(process.env.NODE_ENV == "development") { - const webpack = require("webpack"); - const webpackConfig = require("./webpack.config.dev"); - const webpackDevMiddleware = require("webpack-dev-middleware"); - const webpackHotMiddleware = require("webpack-hot-middleware"); - - const compiler = webpack(webpackConfig); - app.use(webpackDevMiddleware(compiler, { - noInfo: true, - publicPath: webpackConfig.output.publicPath - })); - app.use(webpackHotMiddleware(compiler)); -} - -// Start! -app.listen(PORT, "127.0.0.1", () => { - console.log(`Server running on PORT ${PORT}`); -}); diff --git a/client/src/actions/Dashboard.js b/client/src/actions/Dashboard.js deleted file mode 100644 index c76b01c..0000000 --- a/client/src/actions/Dashboard.js +++ /dev/null @@ -1,10 +0,0 @@ -import * as ACTIONS from "constants/DashboardActions"; - -export const reqSummary = () => ({ - type: ACTIONS.REQ_SUMMARY -}); - -export const rcvSummary = summary => ({ - type: ACTIONS.RCV_SUMMARY, - summary -}); diff --git a/client/src/actions/DefectSelector.js b/client/src/actions/DefectSelector.js deleted file mode 100644 index 93fbbe5..0000000 --- a/client/src/actions/DefectSelector.js +++ /dev/null @@ -1,43 +0,0 @@ -import * as ACTIONS from "constants/DefectSelectorActions"; - -export const reqItems = (selectedItems=[], path=[]) => ({ - type: ACTIONS.REQ_ITEMS, - selectedItems, - path -}); - -export const rcvItems = (items, selectedItems=[]) => ({ - type: ACTIONS.RCV_ITEMS, - items, - selectedItems -}); - -export const changePath = path => ({ - type: ACTIONS.CHANGE_PATH, - path -}); - -export const deselect = item => ({ - type: ACTIONS.DESELECT, - item -}); - -export const deselectAll = () => ({ - type: ACTIONS.DESELECT_ALL -}); - -export const select = (item, path) => ({ - type: ACTIONS.SELECT, - item, - path -}); - -export const selectAll = items => ({ - type: ACTIONS.SELECT_ALL, - items -}); - -export const resetSelection = selectedItems => ({ - type: ACTIONS.RESET_SELECT, - selectedItems -}); diff --git a/client/src/actions/Defects.js b/client/src/actions/Defects.js deleted file mode 100644 index 30584bb..0000000 --- a/client/src/actions/Defects.js +++ /dev/null @@ -1,120 +0,0 @@ -import * as ACTIONS from "constants/DefectsActions"; - -export const reqDefects = () => ({ - type: ACTIONS.REQ_DEFECTS -}); - -export const rcvDefects = defects => ({ - type: ACTIONS.RCV_DEFECTS, - defects -}); - -export const reqDefect = id => ({ - type: ACTIONS.REQ_DEFECT, - id -}); - -export const rcvDefect = defect => ({ - type: ACTIONS.RCV_DEFECT, - defect -}); - -export const resetAddEdit = () => ({ - type: ACTIONS.RESET_DF_ADD_EDIT -}); - -export const changeAssignee = value => ({ - type: ACTIONS.CHANGE_DF_ASSIGNEE, - value -}); - -export const changeDefectName = value => ({ - type: ACTIONS.CHANGE_DF_NAME, - value -}); - -export const changeDefectDescription = value => ({ - type: ACTIONS.CHANGE_DF_DESCR, - value -}); - -export const changeDefectStatus = value => ({ - type: ACTIONS.CHANGE_DF_STATUS, - value -}); - -export const reqSaveDefect = (defect, files=[], redirect=false) => ({ - type: ACTIONS.REQ_SAVE_DEFECT, - defect, - files, - redirect -}); - -export const rcvSaveDefect = defect => ({ - type: ACTIONS.RCV_SAVE_DEFECT, - defect -}); - -export const toggleSelect = (defect, status) => ({ - type: ACTIONS.TOGGLE_SELECT_DEF, - defect, - status -}); - -export const toggleSelectAll = status => ({ - type: ACTIONS.TOGGLE_SELECT_DEF_ALL, - status -}); - -export const reqDeleteDefect = (id, redirect=false) => ({ - type: ACTIONS.REQ_DELETE_DEFECT, - id, - redirect -}); - -export const rcvDeleteDefect = id => ({ - type: ACTIONS.RCV_DELETE_DEFECT, - id -}); - -export const reqDeleteDefects = (idArr=[]) => ({ - type: ACTIONS.REQ_DELETE_DEFECTS, - idArr -}); - -export const rcvDeleteDefects = idArr => ({ - type: ACTIONS.RCV_DELETE_DEFECTS, - idArr -}); - -export const addTests = testCases => ({ - type: ACTIONS.ADD_TESTS_TO_DEFECT, - testCases -}); - -export const deleteTestCase = id => ({ - type: ACTIONS.DEL_TEST_FRM_DEFECT, - id -}); - -export const reqAttachToDefect = (file, defect) => ({ - type: ACTIONS.REQ_ATTACH_TO_DEFECT, - file, - defect -}); - -export const rcvAttachToDefect = (file, defect) => ({ - type: ACTIONS.RCV_ATTACH_TO_DEFECT, - file, - defect -}); - -export const changeStatusFilter = value => ({ - type: ACTIONS.CHANGE_STATUS_FILTER, - value -}); - -export const changeAssigneeFilter = value => ({ - type: ACTIONS.CHANGE_ASSIGNEE_FILTER, - value -}); diff --git a/client/src/actions/ExecCycle.js b/client/src/actions/ExecCycle.js deleted file mode 100644 index f1ef6d0..0000000 --- a/client/src/actions/ExecCycle.js +++ /dev/null @@ -1,191 +0,0 @@ -import * as ACTIONS from "constants/ExecCyclesActions"; - -export const reqExecCycles = () => ({ - type: ACTIONS.REQ_EXEC_CYCLES -}); - -export const rcvExecCycles = execCycles => ({ - type: ACTIONS.RCV_EXEC_CYCLES, - execCycles -}); - -export const reqExecCycle = id => ({ - type: ACTIONS.REQ_EXEC_CYCLE, - id -}); - -export const rcvExecCycle = execCycle => ({ - type: ACTIONS.RCV_EXEC_CYCLE, - execCycle -}); - -export const reqSaveExecCycle = execCycle => ({ - type: ACTIONS.REQ_EC_SAVE, - execCycle -}); - -export const rcvSaveExecCycle = execCycle => ({ - type: ACTIONS.RCV_EC_SAVE, - execCycle -}); - -export const selectExecCycle = execCycle => ({ - type: ACTIONS.SELECT_EXEC_CYCLE, - execCycle -}); - -export const reqTestRuns = execCycleId => ({ - type: ACTIONS.REQ_TEST_RUNS, - execCycleId -}); - -export const rcvTestRuns = (execCycleId, testRuns) => ({ - type: ACTIONS.RCV_TEST_RUNS, - execCycleId, - testRuns -}); - -export const toggleImportDialog = show => ({ - type: ACTIONS.TOGGLE_IMPORT_DLG, - show -}); - -export const toggleSelect = (execCycleId, testRun, status) => ({ - type: ACTIONS.TOGGLE_SELECT_TR, - execCycleId, - testRun, - status -}); - -export const toggleSelectAll = (execCycleId, status) => ({ - type: ACTIONS.TOGGLE_SELECT_TR_ALL, - execCycleId, - status -}); - -export const reqDeleteTestRuns = (idArr=[]) => ({ - type: ACTIONS.REQ_DEL_TEST_RUNS, - idArr -}); - -export const rcvDeleteTestRuns = idArr => ({ - type: ACTIONS.RCV_DEL_TEST_RUNS, - idArr -}); - -export const reqSaveTestRun = testRun => ({ - type: ACTIONS.REQ_SAVE_TR, - testRun -}); - -export const rcvSaveTestRun = (testRun, execCycleId) => ({ - type: ACTIONS.RCV_SAVE_TR, - testRun, - execCycleId -}); - -export const initEditExecCycle = execCycle => ({ - type: ACTIONS.INIT_EC_EDIT, - execCycle -}); - -export const reqDeleteExecCycle = execCycle => ({ - type: ACTIONS.REQ_DEL_EC, - execCycle -}); - -export const rcvDeleteExecCycle = execCycle => ({ - type: ACTIONS.RCV_DEL_EC, - execCycle -}); - -export const reqStartExecCycle = execCycle => ({ - type: ACTIONS.REQ_START_EC, - execCycle -}); - -export const rcvStartExecCycle = execCycle => ({ - type: ACTIONS.RCV_START_EC, - execCycle -}); - -export const reqEndExecCycle = execCycle => ({ - type: ACTIONS.REQ_END_EC, - execCycle -}); - -export const rcvEndExecCycle = execCycle => ({ - type: ACTIONS.RCV_END_EC, - execCycle -}); - -export const resetTRAddEdit = () => ({ - type: ACTIONS.RESET_TR_ADD_EDIT -}); - -export const reqTestRun = id => ({ - type: ACTIONS.REQ_TEST_RUN, - id -}); - -export const rcvTestRun = testRun => ({ - type: ACTIONS.RCV_TEST_RUN, - testRun -}); - -export const rcvImportTests = tests => ({ - type: ACTIONS.RCV_IMPORT_TESTS, - tests -}); - -export const reqLinkDefects = (testRun, defects) => ({ - type: ACTIONS.REQ_LINK_DEFECTS, - defects, - testRun -}); - -export const rcvLinkDefects = (defects, testRun) => ({ - type: ACTIONS.RCV_LINK_DEFECTS, - defects, - testRun -}); - -export const reqAddNewDefect = (defect, files, testRun) => ({ - type: ACTIONS.REQ_ADD_NEW_DEFECT, - defect, - files, - testRun -}); - -export const rcvAddNewDefect = defect => ({ - type: ACTIONS.RCV_ADD_NEW_DEFECT, - defect -}); - -export const reqUnlinkDefect = (testRun, defect) => ({ - type: ACTIONS.REQ_UNLINK_DEFECT, - testRun, - defect -}); - -export const rcvUnlinkDefect = (testRun, defect) => ({ - type: ACTIONS.RCV_UNLINK_DEFECT, - testRun, - defect -}); - -export const reqCloneExecCycle = (id, cloneType) => ({ - type: ACTIONS.REQ_CLONE_EC, - id, - cloneType -}); - -export const rcvCloneExecCycle = execCycle => ({ - type: ACTIONS.RCV_CLONE_EC, - execCycle -}); - -export const changeStatusFilter = value => ({ - type: ACTIONS.CHANGE_STATUS_FILTER, - value -}); diff --git a/client/src/actions/GroupMultiSelect.js b/client/src/actions/GroupMultiSelect.js deleted file mode 100644 index b666ef8..0000000 --- a/client/src/actions/GroupMultiSelect.js +++ /dev/null @@ -1,69 +0,0 @@ -import * as ACTIONS from "constants/GroupMultiSelectActions"; - -export const deselectItem = (cid, item, items) => ({ - type: ACTIONS.DESELECT_ITEM, - cid, - item, - items -}); - -export const deselectAll = (cid, items) => ({ - type: ACTIONS.DESELECT_ALL, - cid, - items -}); - -export const destroy = cid => ({ - type: ACTIONS.DESTROY, - cid -}); - -export const filterList = (cid, filterText) => ({ - type: ACTIONS.FILTER_LIST, - cid, - filterText -}); - -export const init = (cid, selectedItems) => ({ - type: ACTIONS.INIT, - cid, - selectedItems -}); - -export const navDown = (cid, item) => ({ - type: ACTIONS.NAV_DOWN, - cid, - item: { - id: item.id, - name: item.name - } -}); - -export const navUp = cid => ({ - type: ACTIONS.NAV_UP, - cid -}); - -export const navTo = (cid, item) => ({ - type: ACTIONS.NAV_TO, - cid, - item: item ? { - id: item.id, - name: item.name - } : undefined -}); - -export const selectAll = (cid, items) => ({ - type: ACTIONS.SELECT_ALL, - cid, - items -}); - -export const selectItem = (cid, item, path) => ({ - type: ACTIONS.SELECT_ITEM, - cid, - item: { - ...item, - path - } -}); diff --git a/client/src/actions/Shared.js b/client/src/actions/Shared.js deleted file mode 100644 index 11a369a..0000000 --- a/client/src/actions/Shared.js +++ /dev/null @@ -1,143 +0,0 @@ -import * as ACTIONS from "constants/SharedActions"; - -export const setupInvalidateSession = () => ({ - type: ACTIONS.SETUP_SESSION_INV -}); - -export const reqLoginStatus = () => ({ - type: ACTIONS.REQ_LOGIN_STATUS -}); - -export const rcvLoginStatus = user => ({ - type: ACTIONS.RCV_LOGIN_STATUS, - user -}); - -export const setLoading = (id, status) => ({ - type: ACTIONS.IS_LOADING, - id, - status -}); - -export const redirectToTestDesign = id => ({ - type: ACTIONS.REDIRECT_TEST_DESIGN, - id -}); - -export const redirectToDefects = id => ({ - type: ACTIONS.REDIRECT_DEFECTS, - id -}); - -export const redirectToExecCycle = id => ({ - type: ACTIONS.REDIRECT_EXEC_CYCLE, - id -}); - -export const redirectToLogin = () => ({ - type: ACTIONS.REDIRECT_LOGIN -}); - -export const reqUsers = () => ({ - type: ACTIONS.REQ_USERS -}); - -export const rcvUsers = users => ({ - type: ACTIONS.RCV_USERS, - users -}); - -export const reqUploadFiles = files => ({ - type: ACTIONS.REQ_UPLOAD_FILES, - files -}); - -export const rcvUploadFiles = files => ({ - type: ACTIONS.RCV_UPLOAD_FILES, - files -}); - -export const reqDeleteAttachment = attachment => ({ - type: ACTIONS.REQ_DELETE_ATTACH, - attachment -}); - -export const rcvDeleteAttachment = attachment => ({ - type: ACTIONS.RCV_DELETE_ATTACH, - attachment -}); - -export const reqUpdateAttachment = attachment => ({ - type: ACTIONS.REQ_UPDATE_ATTACH, - attachment -}); - -export const rcvUpdateAttachment = attachment => ({ - type: ACTIONS.RCV_UPDATE_ATTACH, - attachment -}); - -export const reqDownloadAttachment = attachment => ({ - type: ACTIONS.REQ_DOWNLOAD_ATTACH, - attachment -}); - -export const reqAttachToDefectComment = (file, comment, defectId) => ({ - type: ACTIONS.REQ_ATTACH_TO_COMM, - file, - comment, - entity: "defect", - entityId: defectId -}); - -export const reqAttachToTestCaseComment = (file, comment, testCaseId) => ({ - type: ACTIONS.REQ_ATTACH_TO_COMM, - file, - comment, - entity: "testCase", - entityId: testCaseId -}); - -export const rcvAttachToComment = (file, comment, entity, entityId) => ({ - type: ACTIONS.RCV_ATTACH_TO_COMM, - file, - comment, - [entity]: entityId -}); - -export const reqSaveComment = (comment, entity, entityId) => ({ - type: ACTIONS.REQ_SAVE_COMMENT, - comment, - entity, - entityId -}); - -export const reqSaveDefectComment = (comment, defectId) => ({ - type: ACTIONS.REQ_SAVE_COMMENT, - comment, - entity: "defect", - entityId: defectId -}); - -export const reqSaveTestCaseComment = (comment, testCaseId) => ({ - type: ACTIONS.REQ_SAVE_COMMENT, - comment, - entity: "testCase", - entityId: testCaseId -}); - -export const rcvSaveComment = (comment, entity, entityId) => ({ - type: ACTIONS.RCV_SAVE_COMMENT, - comment, - [entity]: entityId -}); - -export const reqDeleteComment = id => ({ - type: ACTIONS.REQ_DELETE_COMMENT, - id -}); - -export const rcvDeleteComment = id => ({ - type: ACTIONS.RCV_DELETE_COMMENT, - id -}); diff --git a/client/src/actions/TestDesign.js b/client/src/actions/TestDesign.js deleted file mode 100644 index a55893d..0000000 --- a/client/src/actions/TestDesign.js +++ /dev/null @@ -1,112 +0,0 @@ -import * as ACTIONS from "constants/TestDesignActions"; - -export const reqTestCases = testPlanId => ({ - type: ACTIONS.REQ_TEST_CASES, - testPlanId -}); - -export const rcvTestCases = (testPlanId, testCases) => ({ - type: ACTIONS.RCV_TEST_CASES, - testPlanId, - testCases -}); - -export const reqTestPlans = () => ({ - type: ACTIONS.REQ_TEST_PLANS -}); - -export const rcvTestPlans = testPlans => ({ - type: ACTIONS.RCV_TEST_PLANS, - testPlans -}); - -export const resetAddEdit = () => ({ - type: ACTIONS.RESET_TC_ADD_EDIT -}); - -export const reqTestCase = id => ({ - type: ACTIONS.REQ_TEST_CASE, - id -}); - -export const rcvTestCase = testCase => ({ - type: ACTIONS.RCV_TEST_CASE, - testCase -}); - -export const reqSaveTestCase = (testPlanId, testCase, files=[], redirect=false) => ({ - type: ACTIONS.REQ_TC_SAVE, - testPlanId, - testCase, - files, - redirect -}); - -export const rcvSaveTestCase = (testPlanId, testCase) => ({ - type: ACTIONS.RCV_TC_SAVE, - testPlanId, - testCase -}); - -export const reqDeleteTestCase = (id, testPlanId) => ({ - type: ACTIONS.REQ_DEL_TC, - id, - testPlanId -}); - -export const rcvDeleteTestCase = (id, testPlanId) => ({ - type: ACTIONS.RCV_DEL_TC, - id, - testPlanId -}); - -export const selectTestPlan = testPlan => ({ - type: ACTIONS.SELECT_TEST_PLAN, - testPlan -}); - -export const changeTCDescription = value => ({ - type: ACTIONS.CHANGE_TC_DESCR, - value -}); - -export const changeTCName = value => ({ - type: ACTIONS.CHANGE_TC_NAME, - value -}); - -export const reqSaveTestPlan = testPlan => ({ - type: ACTIONS.REQ_TP_SAVE, - testPlan -}); - -export const rcvSaveTestPlan = testPlan => ({ - type: ACTIONS.RCV_TP_SAVE, - testPlan -}); - -export const reqUploadTests = (testPlanId, file) => ({ - type: ACTIONS.REQ_UPLOAD_TESTS, - testPlanId, - file -}); - -export const rcvUploadTests = (testPlanId, testCases) => ({ - type: ACTIONS.RCV_UPLOAD_TESTS, - testPlanId, - testCases -}); - -export const reqAttachToTestCase = (file, testCase, testPlanId) => ({ - type: ACTIONS.REQ_ATTACH_TO_TC, - file, - testCase, - testPlanId -}); - -export const rcvAttachToTestCase = (file, testCase, testPlanId) => ({ - type: ACTIONS.RCV_ATTACH_TO_TC, - file, - testCase, - testPlanId -}); diff --git a/client/src/actions/TestSelector.js b/client/src/actions/TestSelector.js deleted file mode 100644 index c8fe38b..0000000 --- a/client/src/actions/TestSelector.js +++ /dev/null @@ -1,58 +0,0 @@ -import * as ACTIONS from "constants/TestSelectorActions"; - -export const reqInitialData = () => ({ - type: ACTIONS.REQ_INIT_DATA -}); - -export const reqItems = (selectedItems=[], path=[]) => ({ - type: ACTIONS.REQ_ITEMS, - selectedItems, - path -}); - -export const rcvItems = (items, selectedItems=[]) => ({ - type: ACTIONS.RCV_ITEMS, - items, - selectedItems -}); - -export const changePath = path => ({ - type: ACTIONS.CHANGE_PATH, - path -}); - -export const deselect = item => ({ - type: ACTIONS.DESELECT, - item -}); - -export const deselectAll = () => ({ - type: ACTIONS.DESELECT_ALL -}); - -export const select = (item, path) => ({ - type: ACTIONS.SELECT, - item, - path -}); - -export const selectAll = items => ({ - type: ACTIONS.SELECT_ALL, - items -}); - -export const resetSelection = selectedItems => ({ - type: ACTIONS.RESET_SELECT, - selectedItems -}); - -export const reqImportTests = (selectedItems, importActionContract) => ({ - type: ACTIONS.REQ_IMPORT_TESTS, - selectedItems, - importActionContract -}); - -export const rcvImportTests = tests => ({ - type: ACTIONS.RCV_IMPORT_TESTS, - tests -}); diff --git a/client/src/components/App.js b/client/src/components/App.js deleted file mode 100644 index a26a84f..0000000 --- a/client/src/components/App.js +++ /dev/null @@ -1,34 +0,0 @@ -import React from "react"; -import PropTypes from "prop-types"; -import Alert from "react-s-alert"; - -import LoadingOverlay from "./Shared/LoadingOverlay"; -import Header from "./Shared/Header"; - -class App extends React.Component { - componentDidMount() { - this.props.onInit(); - } - render() { - const { navId } = this.props; - return (
-
-
- {this.props.children} -
- - -
); - } -} -App.propTypes = { - children: PropTypes.oneOfType([ - PropTypes.node, - PropTypes.array - ]), - isLoading: PropTypes.bool.isRequired, - navId: PropTypes.string, - onInit: PropTypes.func.isRequired -}; - -export default App; diff --git a/client/src/components/AppContainer.js b/client/src/components/AppContainer.js deleted file mode 100644 index ccaeb34..0000000 --- a/client/src/components/AppContainer.js +++ /dev/null @@ -1,22 +0,0 @@ -import { connect } from "react-redux"; - -import App from "./App"; - -import { reqLoginStatus, setupInvalidateSession } from "actions/Shared"; - -import { isLoading } from "selectors/Shared"; - -const mapStateToProps = state => ({ - isLoading: isLoading(state) -}); - -const mapDispatchToProps = dispatch => ({ - onInit() { - dispatch(setupInvalidateSession()); - dispatch(reqLoginStatus()); - } -}); - -const AppContainer = connect(mapStateToProps, mapDispatchToProps)(App); - -export default AppContainer; diff --git a/client/src/components/Dashboard/Dashboard.js b/client/src/components/Dashboard/Dashboard.js deleted file mode 100644 index 46bfb83..0000000 --- a/client/src/components/Dashboard/Dashboard.js +++ /dev/null @@ -1,128 +0,0 @@ -import React from "react"; -import { Link } from "react-router-dom"; -import PropTypes from "prop-types"; -import { - Panel, - ProgressBar, - Well -} from "react-bootstrap"; -import Markdown from "react-markdown"; - -import dateFormat from "common/utils/dateFormat"; -import fromNow from "common/utils/fromNow"; - -import "./Dashboard.scss"; - -class Dashboard extends React.Component { - constructor() { - super(...arguments); - } - componentDidMount() { - this.props.onInit(); - } - render() { - const { - recentComments, - defects, - execCycles - } = this.props; - - return (
-
- - Assigned Defects - - {defects.map(defect => -
- - {`DF-${defect.id} `} - - {defect.name} - - - {" "} - {dateFormat(defect.created)} - - - - {" "} - {defect.user && defect.user.name || "< Unknown >"} - -
)} -
-
-
-
- - In Progress - - {!execCycles.length ? None : null} - {execCycles.map(execCycle => { - const total = execCycle.failed + - execCycle.passed + - execCycle.pending; - const percPass = Math.ceil(100*execCycle.passed / total); - const percFail = Math.ceil(100*execCycle.failed / total); - return (
- {execCycle.name} - - - - -
); - })} -
-
- - Activity - - {recentComments.map(comment => - -
- -
- - {comment.user && comment.user.name || "Unknown"} - - {" commented on "} - - {comment.entity=="testCase" - ? - {`TC-${comment.entityId} `} - - : - {`DF-${comment.entityId} `} - } - - - - {fromNow(comment.created)} - -
)} -
-
-
-
); - } -} -Dashboard.propTypes = { - recentComments: PropTypes.arrayOf(PropTypes.shape({ - id: PropTypes.number, - name: PropTypes.string - })), - defects: PropTypes.arrayOf(PropTypes.shape({ - id: PropTypes.number, - name: PropTypes.string - })), - execCycles: PropTypes.arrayOf(PropTypes.shape({ - id: PropTypes.number, - name: PropTypes.string - })), - isLoading: PropTypes.bool.isRequired, - onInit: PropTypes.func.isRequired -}; - -export default Dashboard; diff --git a/client/src/components/Dashboard/Dashboard.scss b/client/src/components/Dashboard/Dashboard.scss deleted file mode 100644 index 8c8bcde..0000000 --- a/client/src/components/Dashboard/Dashboard.scss +++ /dev/null @@ -1,73 +0,0 @@ -.dashboard { - display: grid; - grid-template-columns: 2fr 1fr; - grid-gap: 16px; - padding: 16px; - - .assigned-defects { - .defect { - color: #606061; - display: flex; - font-size: 1.25rem; - margin-bottom: 5px; - .defect-id { - flex: 1; - } - .name { - flex: 9; - overflow: hidden; - padding: 0 5px; - text-overflow: ellipsis; - white-space: nowrap; - } - .status { - flex: 1; - padding: 0 5px; - } - .raised-date { - flex: 2; - padding: 0 5px; - } - .raised-by { - flex: 2; - padding: 0 5px; - } - } - } - .exec-cycles { - .exec-cycle { - font-size: 1.25rem; - display: flex; - margin: 10px 0; - &:first-child { - margin-top: 0; - } - .name { - flex: 1; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - } - .progress { - flex: 2; - height: 8px; - margin: 5px; - } - } - } - .recent-comments { - .well { - font-size: 1.25rem; - - .text, - .commenter, - .comment-time { - color: #999; - font-size: 1.1rem; - } - .glyphicon-time { - margin: 0 5px 0 10px; - } - } - } -} diff --git a/client/src/components/Dashboard/index.js b/client/src/components/Dashboard/index.js deleted file mode 100644 index eaa059f..0000000 --- a/client/src/components/Dashboard/index.js +++ /dev/null @@ -1,29 +0,0 @@ -import { connect } from "react-redux"; - -import * as actions from "actions/Dashboard"; - -import Dashboard from "./Dashboard"; - -import { - getComments, - getDefects, - getExecCycles -} from "selectors/Dashboard"; -import { isLoading } from "selectors/Shared"; - -const mapStateToProps = state => ({ - isLoading: isLoading(state), - recentComments: getComments(state), - defects: getDefects(state), - execCycles: getExecCycles(state) -}); - -const mapDispatchToProps = dispatch => ({ - onInit() { - dispatch(actions.reqSummary()) - } -}); - -const DashboardContainer = connect(mapStateToProps, mapDispatchToProps)(Dashboard); - -export default DashboardContainer; diff --git a/client/src/components/Defects/AddEditDefect.js b/client/src/components/Defects/AddEditDefect.js deleted file mode 100644 index 2600d56..0000000 --- a/client/src/components/Defects/AddEditDefect.js +++ /dev/null @@ -1,311 +0,0 @@ -import React from "react"; -import PropTypes from "prop-types"; -import { - Button, - ButtonToolbar, - Panel, - Table -} from "react-bootstrap"; - -import createTempAttachment from "utils/Shared/createTempAttachment"; - -import Comment from "components/Shared/Comment"; - -import DefectForm from "./AddEditDefectForm"; -import LinkedTest from "./LinkedTest"; -import SelectorModal from "./SelectorModalContainer"; - -class AddEditDefect extends React.Component { - constructor(props) { - super(props); - this.state = { - attachments: [], - showImportDialog: false - }; - this.handleAddTests = this.handleAddTests.bind(this); - this.handleAttachFile = this.handleAttachFile.bind(this); - this.handleAttachFileToComment = this.handleAttachFileToComment.bind(this); - this.handleCancel = this.handleCancel.bind(this); - this.handleChangeAssignee = this.handleChangeAssignee.bind(this); - this.handleChangeName = this.handleChangeName.bind(this); - this.handleDelete = this.handleDelete.bind(this); - this.handleDeleteAttachment = this.handleDeleteAttachment.bind(this); - this.handleDeleteComment = this.handleDeleteComment.bind(this); - this.handleDeleteTestCase = this.handleDeleteTestCase.bind(this); - this.handleChangeDescr = this.handleChangeDescr.bind(this); - this.handleChangeStatus = this.handleChangeStatus.bind(this); - this.handleSave = this.handleSave.bind(this); - this.handleSaveComment = this.handleSaveComment.bind(this); - this.handleUpdateComment = this.handleUpdateComment.bind(this); - this.hideSelector = this.hideSelector.bind(this); - this.showSelector = this.showSelector.bind(this); - } - componentDidMount() { - this.props.onInit(this.props.defectID); - } - handleAddTests(testCases) { - this.props.onAddTests(testCases); - this.hideSelector(); - } - handleAttachFile(file) { - if(this.props.isEditMode) { - this.props.onAttachFile( - file, - this.props.defect - ); - } else { - this.setState({ - attachments: [ - ...this.state.attachments, - createTempAttachment(file) - ] - }); - } - } - handleAttachFileToComment(file, comment) { - this.props.onAttachFileToComment(file, comment, this.props.defectID); - } - handleCancel() { - this.props.onCancel(); - } - handleChangeAssignee(user) { - if(user && !this.props.assignee || user.props != this.props.assignee.id) - this.props.onChangeAssignee(user); - } - handleChangeDescr(value) { - this.props.onChangeDescription(value); - } - handleChangeName(value) { - this.props.onChangeName(value); - } - handleChangeStatus(value) { - this.props.onChangeStatus(value); - } - handleDelete() { - if(confirm(`Delete this defect?`)) - this.props.onDelete(this.props.defect.id); - } - handleDeleteAttachment(attachment) { - if(this.props.isEditMode) { - this.props.onDeleteAttachment(attachment); - } else { - const index = this.state.attachments.findIndex(a => - attachment.id==a.id); - this.setState({ - attachments: [ - ...this.state.attachments.slice(0, index), - ...this.state.attachments.slice(index + 1) - ] - }); - } - } - handleDeleteComment(commentId) { - this.props.onDeleteComment(commentId); - } - handleDeleteTestCase(testCaseId) { - this.props.onDeleteTestCase(testCaseId); - } - handleSave() { - this.props.onSave( - this.props.defect, - this.state.attachments.map(a => a.file), - !Boolean(this.props.defect.id) - ); - } - handleSaveComment(value, attachments) { - this.props.onSaveComment( - { - content: value, - attachments - }, - this.props.defect.id - ); - } - handleUpdateComment(comment) { - this.props.onSaveComment( - comment, - this.props.defect.id - ); - } - hideSelector() { - this.setState({ - showImportDialog: false - }); - } - showSelector() { - this.setState({ - showImportDialog: true - }); - } - render() { - const { - allowAddTestCase=true, - allowDeleteTestCase=true, - ActionBar, - defectID, - defect, - isEditMode, - onDeleteAttachment, - onDownloadAttachment, - onSaveAttachment, - users - } = this.props; - const { comments } = defect; - const testCases = this.props.testCases || defect.testCases; - const { showImportDialog } = this.state; - const attachments = isEditMode - ? defect.description.attachments - : this.state.attachments; - return (
- {!ActionBar - ?
- - {!defectID || } - - - - {defectID ?

Edit Defect

:

Add Defect

} -
- : null} -
- - - - - - - - {allowAddTestCase - ? - : null} - Linked Test Cases - - - {testCases && testCases.length - ? - - {testCases.map(tc => )} - -
- :

No linked tests

} -
-
- - {defect.id - ? - - Comments - - - -
- {comments.map(comment => )} -
-
- : null} -
- {ActionBar - ? - : null} - -
); - } -} -AddEditDefect.propTypes = { - ActionBar: PropTypes.func, // React component - actionbar: PropTypes.bool, - allowAddTestCase: PropTypes.bool, - allowDeleteTestCase: PropTypes.bool, - assignee: PropTypes.shape({ - id: PropTypes.number.isRequired, - name: PropTypes.string.isRequired, - email: PropTypes.string.isRequired - }), - defect: PropTypes.shape({ - id: PropTypes.number, - name: PropTypes.string - }), - defectID: PropTypes.number, - isEditMode: PropTypes.bool.isRequired, - onAddTests: PropTypes.func.isRequired, - onAttachFileToComment: PropTypes.func.isRequired, - onAttachFile: PropTypes.func.isRequired, - onInit: PropTypes.func.isRequired, - onCancel: PropTypes.func.isRequired, - onChangeAssignee: PropTypes.func.isRequired, - onChangeDescription: PropTypes.func.isRequired, - onChangeName: PropTypes.func.isRequired, - onChangeStatus: PropTypes.func.isRequired, - onDelete: PropTypes.func.isRequired, - onDeleteAttachment: PropTypes.func.isRequired, - onDeleteComment: PropTypes.func.isRequired, - onDeleteTestCase: PropTypes.func.isRequired, - onDownloadAttachment: PropTypes.func.isRequired, - onSave: PropTypes.func.isRequired, - onSaveComment: PropTypes.func.isRequired, - onSaveAttachment: PropTypes.func.isRequired, - testCases: PropTypes.arrayOf(PropTypes.shape({ - id: PropTypes.number, - name: PropTypes.string - })), - users: PropTypes.arrayOf(PropTypes.shape({ - id: PropTypes.number.isRequired, - name: PropTypes.string.isRequired, - email: PropTypes.string.isRequired - })) -}; - -export default AddEditDefect; diff --git a/client/src/components/Defects/AddEditDefectContainer.js b/client/src/components/Defects/AddEditDefectContainer.js deleted file mode 100644 index b7b3aaa..0000000 --- a/client/src/components/Defects/AddEditDefectContainer.js +++ /dev/null @@ -1,95 +0,0 @@ -import { connect } from "react-redux"; - -import * as actions from "actions/Defects"; -import { - redirectToDefects, - reqAttachToDefectComment, - reqDeleteAttachment, - reqDeleteComment, - reqDownloadAttachment, - reqSaveDefectComment, - reqUpdateAttachment, - reqUsers -} from "actions/Shared"; - -import AddEditDefect from "./AddEditDefect"; - -import { getDefectAddEditState, isEditMode } from "selectors/Defects"; -import { getSelected } from "selectors/TestSelector"; -import { getUsers, isLoading } from "selectors/Shared"; - -const mapStateToProps = state => ({ - isEditMode: isEditMode(state), - isLoading: isLoading(state), - defect: getDefectAddEditState(state), - selectedTestCases: getSelected(state), - users: getUsers(state) -}); - -const mapDispatchToProps = dispatch => ({ - onAddTests(testCases) { - dispatch(actions.addTests(testCases)); - }, - onAttachFile(file, defect) { - dispatch(actions.reqAttachToDefect(file, defect)); - }, - onAttachFileToComment(file, comment, defectId) { - dispatch(reqAttachToDefectComment(file, comment, defectId)); - }, - onCancel() { - dispatch(actions.resetAddEdit()); - dispatch(redirectToDefects()); - }, - onChangeAssignee(user) { - dispatch(actions.changeAssignee(user)); - }, - onChangeDescription(val) { - dispatch(actions.changeDefectDescription(val)); - }, - onChangeName(val) { - dispatch(actions.changeDefectName(val)); - }, - onChangeStatus(val) { - dispatch(actions.changeDefectStatus(val)); - }, - onDelete(id) { - dispatch(actions.reqDeleteDefect(id, true)); - }, - onDeleteAttachment(attachment) { - dispatch(reqDeleteAttachment(attachment)); - }, - onDeleteComment(id) { - dispatch(reqDeleteComment(id)); - }, - onDeleteTestCase(id) { - dispatch(actions.deleteTestCase(id)); - }, - onDownloadAttachment(attachment) { - dispatch(reqDownloadAttachment(attachment)); - }, - onInit(id) { - dispatch(actions.resetAddEdit()); - dispatch(reqUsers()); - if(id) - dispatch(actions.reqDefect(id)); - }, - onSave(defect, files, redirect) { - dispatch(actions.reqSaveDefect(defect, files, redirect)); - }, - onSaveAttachment(attachment) { - dispatch(reqUpdateAttachment(attachment)); - }, - onSaveComment(comment, defectId) { - dispatch(reqSaveDefectComment(comment, defectId)); - } -}); - -const mergeProps = (stateProps, dispatchProps, ownProps) => ({ - ...stateProps, - ...dispatchProps, - ...ownProps -}); - -const AddEditDefectContainer = connect(mapStateToProps, mapDispatchToProps, mergeProps)(AddEditDefect); - -export default AddEditDefectContainer; diff --git a/client/src/components/Defects/AddEditDefectForm.js b/client/src/components/Defects/AddEditDefectForm.js deleted file mode 100644 index 7205f8f..0000000 --- a/client/src/components/Defects/AddEditDefectForm.js +++ /dev/null @@ -1,147 +0,0 @@ -import Alert from "react-s-alert"; -import React from "react"; -import PropTypes from "prop-types"; -import Dropzone from "react-dropzone"; -import { - DropdownButton, - MenuItem -} from "react-bootstrap"; - -import Attachment from "components/Shared/Attachment"; - -import DEF_STATES from "common/constants/DefectStates"; -import DEF_COLORS from "constants/DefectStateColors"; - -import Description from "components/Shared/Description"; -import Title from "components/Shared/Title"; - -const AddEditDefectForm = props => { - const { - assignee, - attachments, - defectId, - description, - isEditMode, - name, - onAttachFile, - onChangeAssignee, - onChangeName, - onChangeDescription, - onChangeStatus, - onDeleteAttachment, - onDownloadAttachment, - onSaveAttachment, - status, - users - } = props; - - const handleAttach = files => { - if(files && files.length) { - const file = files[0]; - if(!file || !file.name || !file.size) { - Alert.error("Invalid file"); - return; - } - if(onAttachFile) - onAttachFile(file); - } - }; - const handleChangeAssignee = val => onChangeAssignee - ? onChangeAssignee(val) - : null; - const handleChangeStatus = val => onChangeStatus - ? onChangeStatus(val) - : null; - - return ( -
-
- - </div> - <div className="status-container"> - {defectId - ? <DropdownButton - bsStyle={DEF_COLORS[status]} - bsSize="small" - title={status} - id="status-dd" - > - {DEF_STATES.map(s => (<MenuItem - key={s} - onSelect={() => handleChangeStatus(s)} - >{s}</MenuItem>))} - </DropdownButton> - : null} - </div> - <div className="description-container"> - <Description - placeholder="Description" - onUpdate={onChangeDescription} - onUpload={handleAttach} - value={description} /> - </div> - <div className="assignee-container"> - {users - ? <DropdownButton - bsSize="small" - title={assignee && assignee.name || "< Unassigned >"} - id="assignee-dd" - > - {users.map(u => (<MenuItem - key={`assignee-${u.id}`} - onSelect={() => handleChangeAssignee(u)} - >{u.name}</MenuItem>))} - </DropdownButton> - : null} - </div> - <div className="attachments"> - {attachments.map(attachment => <Attachment - key={`attachment-${attachment.name}`} - allowDownload={isEditMode} - allowEdit={isEditMode} - attachment={attachment} - onDelete={onDeleteAttachment} - onDownload={onDownloadAttachment} - onSave={onSaveAttachment} />)} - </div> - </div> - </Dropzone>); -}; -AddEditDefectForm.propTypes = { - assignee: PropTypes.shape({ - id: PropTypes.number.isRequired, - name: PropTypes.string.isRequired, - email: PropTypes.string.isRequired - }), - attachments: PropTypes.arrayOf(PropTypes.shape({ - id: PropTypes.number, - name: PropTypes.string.isRequired, - path: PropTypes.string.isRequired - })), - defectId: PropTypes.number, - description: PropTypes.string, - isEditMode: PropTypes.bool, - name: PropTypes.string, - onAttachFile: PropTypes.func, - onChangeAssignee: PropTypes.func.isRequired, - onChangeDescription: PropTypes.func.isRequired, - onChangeName: PropTypes.func.isRequired, - onChangeStatus: PropTypes.func.isRequired, - onDeleteAttachment: PropTypes.func.isRequired, - onDownloadAttachment: PropTypes.func.isRequired, - onSaveAttachment: PropTypes.func.isRequired, - status: PropTypes.string, - users: PropTypes.arrayOf(PropTypes.shape({ - id: PropTypes.number.isRequired, - name: PropTypes.string.isRequired, - email: PropTypes.string.isRequired - })) -}; - -export default AddEditDefectForm; diff --git a/client/src/components/Defects/AddEditView.js b/client/src/components/Defects/AddEditView.js deleted file mode 100644 index 8715de7..0000000 --- a/client/src/components/Defects/AddEditView.js +++ /dev/null @@ -1,16 +0,0 @@ -import React from "react"; -import PropTypes from "prop-types"; - -import AddEditDefect from "./AddEditDefectContainer"; - -const AddEditView = props => { - const { defectID } = props; - return (<div className="defects add-edit"> - <AddEditDefect defectID={defectID} /> - </div>); -}; -AddEditView.propTypes = { - defectID: PropTypes.number -}; - -export default AddEditView; diff --git a/client/src/components/Defects/DefectList.js b/client/src/components/Defects/DefectList.js deleted file mode 100644 index 9ef2f44..0000000 --- a/client/src/components/Defects/DefectList.js +++ /dev/null @@ -1,147 +0,0 @@ -import React from "react"; -import PropTypes from "prop-types"; -import { Table } from "react-bootstrap"; - -import STATES from "common/constants/DefectStates"; - -import ColumnFilter from "components/Shared/ColumnFilter/index"; - -import ListItem from "./DefectListItem"; -import Toolbar from "./Toolbar"; - -const defectStateFilterItems = STATES.map(s => ({ id: s, name: s })); -const UNASSIGNED = "<Unassigned>"; - -class DefectList extends React.Component { - constructor(props) { - super(props); - this.applyFilters = this.applyFilters.bind(this); - this.bulkDelete = this.bulkDelete.bind(this); - this.handleAssigneeFilter = this.handleAssigneeFilter.bind(this); - this.handleStatusFilter = this.handleStatusFilter.bind(this); - this.toggleSelect = this.toggleSelect.bind(this); - this.toggleSelectAll = this.toggleSelectAll.bind(this); - } - componentDidMount() { - this.props.fetchDefects(); - } - bulkDelete() { - if(confirm(`Delete ${this.props.selected.length} defects?`)) - this.props.onDelete(this.props.selected.map(d => d.id)); - } - handleAssigneeFilter(value) { - this.props.onChangeAssigneeFilter(value); - } - handleStatusFilter(value) { - this.props.onChangeStatusFilter(value); - } - toggleSelect(defect, status) { - this.props.onToggleSelect(defect, status); - } - toggleSelectAll(ev) { - this.props.onToggleSelectAll(ev.target.checked); - } - applyFilters(defects) { - const { assigneeFilter, statusFilter } = this.props; - if(assigneeFilter) { - if(assigneeFilter.id==UNASSIGNED) - defects = defects.filter(d => !d.assignee); - else - defects = defects.filter(d => d.assignee && d.assignee.id==assigneeFilter.id); - } - if(statusFilter) - defects = defects.filter(d => d.status==statusFilter.id); - return defects; - } - render() { - const { - allowDelete, - allSelected, - assigneeFilter, - defects, - statusFilter - } = this.props; - - const assigneeList = [{ id: UNASSIGNED, name: UNASSIGNED }]; - defects - .filter(d => d && d.assignee) - .forEach(d => { - if(!assigneeList.find(a => a.id == d.assignee.id)) - assigneeList.unshift(d.assignee) - }); - - return (<div className="defects-list"> - <Toolbar allowDelete={allowDelete} onDelete={this.bulkDelete} /> - <Table striped condensed hover className="data-grid "> - <thead> - <tr> - <th> - <input - type="checkbox" - checked={allSelected} - onChange={(ev) => this.toggleSelectAll(ev)} /> - </th> - <th>ID</th> - <th>Name</th> - <th> - Assigned To - <ColumnFilter - items={assigneeList} - selected={assigneeFilter} - onChange={this.handleAssigneeFilter} /> - </th> - <th> - Status - <ColumnFilter - items={defectStateFilterItems} - selected={statusFilter} - onChange={this.handleStatusFilter} /> - </th> - <th>Test Cases</th> - <th>Comments</th> - </tr> - </thead> - <tbody> - {this.applyFilters(defects).map(d => (<ListItem - key={`defect-${d.id}`} - defect={d} - onToggle={this.toggleSelect} />))} - </tbody> - </Table> - </div>); - } -} -DefectList.propTypes = { - allowDelete: PropTypes.bool, - allSelected: PropTypes.bool, - assigneeFilter: PropTypes.shape({ - id: PropTypes.oneOfType([ - PropTypes.number, - PropTypes.string - ]), - name: PropTypes.string.isRequired - }), - defects: PropTypes.arrayOf(PropTypes.shape({ - id: PropTypes.number.isRequired, - name: PropTypes.string.isRequired - })), - fetchDefects: PropTypes.func.isRequired, - onChangeAssigneeFilter: PropTypes.func.isRequired, - onChangeStatusFilter: PropTypes.func.isRequired, - onDelete: PropTypes.func.isRequired, - onToggleSelect: PropTypes.func.isRequired, - onToggleSelectAll: PropTypes.func.isRequired, - selected: PropTypes.arrayOf(PropTypes.shape({ - id: PropTypes.number.isRequired, - name: PropTypes.string.isRequired - })), - statusFilter: PropTypes.shape({ - id: PropTypes.oneOfType([ - PropTypes.number, - PropTypes.string - ]), - name: PropTypes.string.isRequired - }) -}; - -export default DefectList; diff --git a/client/src/components/Defects/DefectListContainer.js b/client/src/components/Defects/DefectListContainer.js deleted file mode 100644 index 1c50d6a..0000000 --- a/client/src/components/Defects/DefectListContainer.js +++ /dev/null @@ -1,50 +0,0 @@ -import { connect } from "react-redux"; -import DefectList from "./DefectList"; - -import * as actions from "actions/Defects"; - -import { - allowDelete, - areAllDefectsSelected, - getAssigneeFilter, - getDefects, - getStatusFilter, - getSelectedDefects -} from "selectors/Defects"; - -import { isLoading } from "selectors/Shared"; - -const mapStateToProps = state => ({ - allowDelete: allowDelete(state), - allSelected: areAllDefectsSelected(state), - assigneeFilter: getAssigneeFilter(state), - isLoading: isLoading(state), - defects: getDefects(state), - selected: getSelectedDefects(state), - statusFilter: getStatusFilter(state) -}); - -const mapDispatchToProps = dispatch => ({ - fetchDefects() { - dispatch(actions.reqDefects()); - }, - onChangeAssigneeFilter(value) { - dispatch(actions.changeAssigneeFilter(value)); - }, - onChangeStatusFilter(value) { - dispatch(actions.changeStatusFilter(value)); - }, - onDelete(idArr) { - dispatch(actions.reqDeleteDefects(idArr)); - }, - onToggleSelect(defect, status) { - dispatch(actions.toggleSelect(defect, status)); - }, - onToggleSelectAll(status) { - dispatch(actions.toggleSelectAll(status)); - } -}); - -const DefectListContainer = connect(mapStateToProps, mapDispatchToProps)(DefectList); - -export default DefectListContainer; diff --git a/client/src/components/Defects/DefectListItem.js b/client/src/components/Defects/DefectListItem.js deleted file mode 100644 index e1c231d..0000000 --- a/client/src/components/Defects/DefectListItem.js +++ /dev/null @@ -1,50 +0,0 @@ -import React from "react"; -import { Link } from "react-router-dom"; -import PropTypes from "prop-types"; - -const DefectListItem = props => { - const { - defect, - onToggle - } = props; - - const { - assignee, - id, - name, - status, - selected, - testCases, - comments, - } = defect; - - const handleToggle = ev => { - onToggle(defect, ev.target.checked); - }; - - return (<tr> - <td> - <input - type="checkbox" - checked={selected} - onChange={handleToggle} /> - </td> - <td>DF-{id}</td> - <td> - <Link to={`/defects/edit/${id}`}>{name}</Link> - </td> - <td>{assignee && assignee.name || "< Unassigned >"}</td> - <td>{status}</td> - <td>{testCases ? testCases.length : 0}</td> - <td>{comments ? comments.length : 0}</td> - </tr>); -}; -DefectListItem.propTypes = { - defect: PropTypes.shape({ - id: PropTypes.number.isRequired, - name: PropTypes.string.isRequired - }), - onToggle: PropTypes.func.isRequired -}; - -export default DefectListItem; diff --git a/client/src/components/Defects/Defects.scss b/client/src/components/Defects/Defects.scss deleted file mode 100644 index 15b2b5b..0000000 --- a/client/src/components/Defects/Defects.scss +++ /dev/null @@ -1,85 +0,0 @@ -.defects { - background: #fff; - display: grid; - grid-auto-rows: 100%; - - .data-grid { - td:first-child, - th:first-child { - padding-left: 16px; - } - td:last-child, - th:last-child { - padding-right: 16px; - } - } - .add-edit-defect { - position: relative; - margin: 64px 24px; - - .action-bar { - position: absolute; - left: -24px; - right: -24px; - top: -64px; - .btn-toolbar { - float: right; - margin: 6px 24px 0 0; - } - } - .defect-status { - margin-right: 15px; - } - .assignee { - #assignee-dd { - text-transform: none; - } - } - .wrapper { - display: grid; - grid-column-gap: 10px; - grid-template-columns: 7fr 1fr; - } - .status-container { - text-align: right; - } - } - .defect-list { - background: #fff; - } - - .toolbar .btn-link { - text-decoration: none; - } - - .panel.testcases { - .panel-body { - padding: 0; - } - .table { - td:first-child { - padding-left: 15px; - width: 80px; - } - td:last-child { - padding-right: 15px; - text-align: right; - width: 80px; - } - .btn { - padding: 0; - } - } - .btn-add-test { - margin-top: -9px; - position: absolute; - right: 0; - text-decoration: none; - } - .empty-message { - color: #999; - font-size: 1.2rem; - padding: 15px; - } - } -} diff --git a/client/src/components/Defects/LinkedTest.js b/client/src/components/Defects/LinkedTest.js deleted file mode 100644 index 7842993..0000000 --- a/client/src/components/Defects/LinkedTest.js +++ /dev/null @@ -1,53 +0,0 @@ -import React from "react"; -import { Link } from "react-router-dom"; -import PropTypes from "prop-types"; -import { Button } from "react-bootstrap"; - -const LinkedTest = props => { - const { - allowDelete=true, - testCase, - onDelete - } = props; - - const { - id, - name, - testPlan - } = testCase; - - const deleteTestCase = () => onDelete(id); - - return (<tr className="test-case"> - <td className="test-id"> - <Link - to={`/design/testplan/${testPlan}/testcase/edit/${id}`} - target="_blank" - className="text-info" - > - TC-{id} - {" "} - <i className="glyphicon glyphicon-share" /> - </Link> - </td> - <td className="test-name">{name}</td> - {allowDelete - ? <td> - <Button bsStyle="link" className="btn-delete-test" onClick={deleteTestCase}> - <i className="glyphicon glyphicon-trash text-danger" /> - </Button> - </td> - : null} - </tr>); -}; -LinkedTest.propTypes = { - allowDelete: PropTypes.bool, - onDelete: PropTypes.func, - testCase: PropTypes.shape({ - id: PropTypes.number.isRequired, - name: PropTypes.string.isRequired, - testPlan: PropTypes.number.isRequired - }).isRequired -}; - -export default LinkedTest; diff --git a/client/src/components/Defects/ListView.js b/client/src/components/Defects/ListView.js deleted file mode 100644 index 4459bdf..0000000 --- a/client/src/components/Defects/ListView.js +++ /dev/null @@ -1,10 +0,0 @@ -import React from "react"; - -import DefectList from "./DefectListContainer"; - - -const ListView = () => (<div className="defects list"> - <DefectList /> -</div>); - -export default ListView; diff --git a/client/src/components/Defects/SelectorModal.js b/client/src/components/Defects/SelectorModal.js deleted file mode 100644 index 50350e6..0000000 --- a/client/src/components/Defects/SelectorModal.js +++ /dev/null @@ -1,54 +0,0 @@ -import React from "react"; -import PropTypes from "prop-types"; - -import Selector from "components/TestSelector"; - -class SelectorModal extends React.Component { - constructor(props) { - super(props); - this.handleInit = this.handleInit.bind(this); - this.handleSave = this.handleSave.bind(this); - } - handleInit() { - // this.props.onInit(this.props.execCycle); - } - handleSave(selectedItems) { - this.props.onSave(selectedItems); - } - render() { - const { - allowAdd, - allowAddFolder, - show, - onClose, - importActionContract - } = this.props; - return (<Selector - allowAdd={allowAdd} - allowAddFolder={allowAddFolder} - show={show} - importActionContract={importActionContract} - onInit={this.handleInit} - onClose={onClose} - onSave={this.handleSave} />); - } -} -SelectorModal.propTypes = { - allowAdd: PropTypes.bool, - allowAddFolder: PropTypes.bool, - importActionContract: PropTypes.shape({ - type: PropTypes.string.isRequired, - key: PropTypes.string.isRequired, - extra: PropTypes.shape({ - execCycle: PropTypes.shape({ - id: PropTypes.number, - name: PropTypes.string - }) - }).isRequired - }), - onClose: PropTypes.func.isRequired, - onSave: PropTypes.func.isRequired, - show: PropTypes.bool.isRequired -}; - -export default SelectorModal; diff --git a/client/src/components/Defects/SelectorModalContainer.js b/client/src/components/Defects/SelectorModalContainer.js deleted file mode 100644 index ae51c64..0000000 --- a/client/src/components/Defects/SelectorModalContainer.js +++ /dev/null @@ -1,22 +0,0 @@ -import { connect } from "react-redux"; - -import SelectorModal from "./SelectorModal"; - -const mapDispatchToProps = (dispatch, ownProps) => ({ - onClose() { - if(typeof(ownProps.onClose)==="function") - ownProps.onClose(); - } -}); - -const mergeProps = (stateProps, dispatchProps, ownProps) => { - return { - ...stateProps, - ...dispatchProps, - ...ownProps - }; -}; - -const SelectorModalContainer = connect(undefined, mapDispatchToProps, mergeProps)(SelectorModal); - -export default SelectorModalContainer; diff --git a/client/src/components/Defects/Toolbar.js b/client/src/components/Defects/Toolbar.js deleted file mode 100644 index fa6a1fc..0000000 --- a/client/src/components/Defects/Toolbar.js +++ /dev/null @@ -1,38 +0,0 @@ -import React from "react"; -import PropTypes from "prop-types"; -import { Link } from "react-router-dom"; -import { - Button, - OverlayTrigger, - Tooltip -} from "react-bootstrap"; - -const addTooltip = <Tooltip id="add-tooltip">Add Defect</Tooltip>; -const deleteTooltip = <Tooltip id="delete-tooltip">Delete</Tooltip>; - -class DefectToolbar extends React.Component { - render() { - const { allowDelete, onDelete } = this.props; - return (<div className="toolbar"> - <OverlayTrigger placement="bottom" overlay={addTooltip}> - <Link to={`/defects/add`} className="btn btn-link"> - <i className="glyphicon glyphicon-plus text-info" /> - </Link> - </OverlayTrigger> - {allowDelete - ? <OverlayTrigger placement="bottom" overlay={deleteTooltip}> - <Button bsStyle="link" onClick={onDelete}> - <i className="glyphicon glyphicon-trash text-danger" /> - </Button> - </OverlayTrigger> - : null} - <h3 className="header-gradient-1">Defects</h3> - </div>); - } -} -DefectToolbar.propTypes = { - allowDelete: PropTypes.bool, - onDelete: PropTypes.func.isRequired -}; - -export default DefectToolbar; diff --git a/client/src/components/Defects/index.js b/client/src/components/Defects/index.js deleted file mode 100644 index 78ffde9..0000000 --- a/client/src/components/Defects/index.js +++ /dev/null @@ -1,21 +0,0 @@ -import React from "react"; -import PropTypes from "prop-types"; - -import AddEditView from "./AddEditView"; -import ListView from "./ListView"; - -import "./Defects.scss"; - - -const Defects = props => { - const { mode, defectID } = props; - return mode=="list" - ? <ListView /> - : <AddEditView defectID={defectID} />; -}; -Defects.propTypes = { - mode: PropTypes.string, - defectID: PropTypes.number -}; - -export default Defects; diff --git a/client/src/components/Documents/index.js b/client/src/components/Documents/index.js deleted file mode 100644 index b452748..0000000 --- a/client/src/components/Documents/index.js +++ /dev/null @@ -1,12 +0,0 @@ -import React from "react"; -// import PropTypes from "prop-types"; - -class Documents extends React.Component { - render() { - return (<div> - Documents - </div>); - } -} - -export default Documents; diff --git a/client/src/components/ExecCycle/AddDefectModal.js b/client/src/components/ExecCycle/AddDefectModal.js deleted file mode 100644 index e4a1762..0000000 --- a/client/src/components/ExecCycle/AddDefectModal.js +++ /dev/null @@ -1,71 +0,0 @@ -import React from "react"; -import PropTypes from "prop-types"; -import { Modal } from "react-bootstrap"; - -import AddEditDefect from "components/Defects/AddEditDefectContainer"; - -import ActionBar from "./AddDefectModalActionBar"; - - -class AddDefectModal extends React.Component { - constructor(props) { - super(props); - - this.handleClose = this.handleClose.bind(this); - this.handleSave = this.handleSave.bind(this); - } - handleClose() { - this.props.onClose(); - } - handleSave(defect, files) { - const { testCase } = this.props; - this.props.onSave( - { - ...defect, - testCases: [{ - id: testCase.id, - name: testCase.name, - testPlan: testCase.testPlan.id - }] - }, - files - ); - } - render() { - const { show, testCase } = this.props; - const linkedTestCases = [{ - id: testCase.id, - name: testCase.name, - testPlan: testCase.testPlan && testCase.testPlan.id - }]; - return (<Modal show={show} className="add-defect-modal"> - <Modal.Header> - <Modal.Title>Add Defect</Modal.Title> - </Modal.Header> - <Modal.Body> - <AddEditDefect - allowAddTestCase={false} - allowDeleteTestCase={false} - ActionBar={ActionBar} - testCases={linkedTestCases} - onCancel={this.handleClose} - onSave={this.handleSave} /> - </Modal.Body> - </Modal>); - } -} -AddDefectModal.propTypes = { - onClose: PropTypes.func.isRequired, - onSave: PropTypes.func.isRequired, - show: PropTypes.bool.isRequired, - testCase: PropTypes.shape({ - id: PropTypes.number, - name: PropTypes.string, - testPlan: PropTypes.shape({ - id: PropTypes.number, - name: PropTypes.string - }) - }), -}; - -export default AddDefectModal; diff --git a/client/src/components/ExecCycle/AddDefectModalActionBar.js b/client/src/components/ExecCycle/AddDefectModalActionBar.js deleted file mode 100644 index a0f5420..0000000 --- a/client/src/components/ExecCycle/AddDefectModalActionBar.js +++ /dev/null @@ -1,18 +0,0 @@ -import React from "react"; -import PropTypes from "prop-types"; -import { Button } from "react-bootstrap"; - - -const ActionBar = props => { - const { onCancel, onSave } = props; - return (<div className="custom-actionbar modal-footer"> - <Button onClick={onCancel}>Cancel</Button> - <Button bsStyle="success" onClick={onSave}>Save</Button> - </div>); -}; -ActionBar.propTypes = { - onCancel: PropTypes.func.isRequired, - onSave: PropTypes.func.isRequired -}; - -export default ActionBar; diff --git a/client/src/components/ExecCycle/AddEditExecCycle.js b/client/src/components/ExecCycle/AddEditExecCycle.js deleted file mode 100644 index 4ed557c..0000000 --- a/client/src/components/ExecCycle/AddEditExecCycle.js +++ /dev/null @@ -1,93 +0,0 @@ -import React from "react"; -import PropTypes from "prop-types"; -import Alert from "react-s-alert"; -import { - Button, - ControlLabel, - FormControl, - FormGroup, - Modal -} from "react-bootstrap"; - -class AddEditExecCycle extends React.Component { - constructor(props) { - super(props); - this.state = { - name: "" - }; - this.handleChangeName = this.handleChangeName.bind(this); - this.handleClose = this.handleClose.bind(this); - this.handleDelete = this.handleDelete.bind(this); - this.handleSave = this.handleSave.bind(this); - } - componentWillReceiveProps(nextProps) { - if(nextProps.show && !this.props.show) { - this.setState({ - id: nextProps.execCycle && nextProps.execCycle.id, - name: nextProps.execCycle && nextProps.execCycle.name || "" - }); - } - } - handleChangeName(ev) { - this.setState({ - name: ev.target.value - }); - } - handleClose() { - this.props.onClose(); - } - handleDelete() { - if(this.props.execCycle.testRuns.length) { - Alert.error("Can't delete a non-empty execution cycle"); - return; - } - if(confirm("Are you sure you want to delete this execution cycle?")) - this.props.onDelete(this.props.execCycle); - } - handleSave() { - this.props.onSave({ - ...this.props.execCycle, - name: this.state.name - }); - } - render() { - const { id, name } = this.state; - - return (<Modal show={this.props.show} onHide={this.handleClose}> - <Modal.Header closeButton> - {id - ? <Modal.Title>Edit Execution Cycle</Modal.Title> - : <Modal.Title>New Execution Cycle</Modal.Title>} - </Modal.Header> - <Modal.Body> - <FormGroup controlId="name"> - <ControlLabel>Name</ControlLabel> - <FormControl - value={name} - onChange={this.handleChangeName} - type="text" /> - </FormGroup> - </Modal.Body> - <Modal.Footer> - <Button onClick={this.handleClose}>Cancel</Button> - <Button bsStyle="success" onClick={this.handleSave}>Save</Button> - </Modal.Footer> - </Modal>); - } -} -AddEditExecCycle.propTypes = { - show: PropTypes.bool.isRequired, - execCycle: PropTypes.shape({ - id: PropTypes.number, - name: PropTypes.string, - testRuns: PropTypes.arrayOf(PropTypes.shape({ - id: PropTypes.number.isRequired, - name: PropTypes.string.isRequired - })) - }), - onClose: PropTypes.func.isRequired, - onDelete: PropTypes.func, - onSave: PropTypes.func.isRequired -}; - -export default AddEditExecCycle; diff --git a/client/src/components/ExecCycle/CloneExecCycle.js b/client/src/components/ExecCycle/CloneExecCycle.js deleted file mode 100644 index 843894c..0000000 --- a/client/src/components/ExecCycle/CloneExecCycle.js +++ /dev/null @@ -1,67 +0,0 @@ -import React from "react"; -import PropTypes from "prop-types"; -import { - Button, - FormGroup, - Modal, - Radio -} from "react-bootstrap"; - -import CLONE_TYPES from "common/constants/ExecCycleCloneTypes"; - -class CloneExecCycle extends React.Component { - constructor(props) { - super(props); - this.state = { - cloneType: CLONE_TYPES[0].id - }; - this.handleChangeCloneType = this.handleChangeCloneType.bind(this); - this.handleClone = this.handleClone.bind(this); - this.handleClose = this.handleClose.bind(this); - } - handleChangeCloneType(cloneType) { - this.setState({ cloneType: cloneType.id }); - } - handleClone() { - this.props.onClone( - this.props.execCycle.id, - this.state.cloneType - ); - } - handleClose() { - this.props.onClose(); - } - render() { - const { cloneType } = this.state; - return (<Modal show={this.props.show} onHide={this.handleClose}> - <Modal.Header closeButton> - <Modal.Title>Clone Execution Cycle</Modal.Title> - </Modal.Header> - <Modal.Body> - <FormGroup> - {CLONE_TYPES.map(ct => - <Radio - key={`clone-type-${ct.id}`} - checked={cloneType==ct.id} - name="items" - onChange={() => this.handleChangeCloneType(ct)} - >{ct.name}</Radio>)} - </FormGroup> - </Modal.Body> - <Modal.Footer> - <Button onClick={this.handleClose}>Cancel</Button> - <Button bsStyle="success" onClick={this.handleClone}>Clone</Button> - </Modal.Footer> - </Modal>); - } -} -CloneExecCycle.propTypes = { - show: PropTypes.bool.isRequired, - execCycle: PropTypes.shape({ - id: PropTypes.number - }), - onClone: PropTypes.func.isRequired, - onClose: PropTypes.func.isRequired -}; - -export default CloneExecCycle; diff --git a/client/src/components/ExecCycle/ExecCycle.scss b/client/src/components/ExecCycle/ExecCycle.scss deleted file mode 100644 index 79a0f4f..0000000 --- a/client/src/components/ExecCycle/ExecCycle.scss +++ /dev/null @@ -1,132 +0,0 @@ -.exec-cycles { - display: grid; - grid-auto-rows: 100%; - &.list { - grid-template-columns: 280px auto; - } -} - -.cycles { - border-right: solid 2px #ccc; - ul { - list-style-type: none; - margin: 0; - padding: 0; - font-size: 1.2rem; - - li { - color: #999; - cursor: pointer; - margin: 0; - padding: 0; - - &.selected a { - color: #404041; - font-weight: bold; - } - - a { - display: block; - padding: 8px 16px; - text-decoration: none; - &:hover { - color: #404041; - } - } - - .btn { - box-shadow: none; - float: right; - margin-left: 8px; - outline: none; - padding: 2px; - } - } - } -} - -.add-edit-ec { - position: relative; - margin: 64px 24px; - - .action-bar { - position: absolute; - left: -24px; - right: -24px; - top: -64px; - .btn-toolbar { - float: right; - margin: 6px 24px 0 0; - } - } - -} -.test-runs { - background: #fff; - - .btn-xs { - font-weight: 400; - text-transform: none; - width: 50px; - } -} -.edit-tr { - position: relative; - margin: 64px 24px; - - .action-bar { - position: absolute; - left: -24px; - right: -24px; - top: -64px; - .btn-toolbar { - float: right; - margin: 6px 24px 0 0; - } - } - .defect-id { - width: 80px; - } - .status-dd { - line-height: 22px; - width: 55px; - } - .test-link { - margin-right: 20px; - } - .btn-delete-defect { - padding: 0; - } - .panel.defects .panel-body { - padding: 0; - } - .defect-status { - width: 100px; - } - .del-defect { - width: 40px; - } - .empty-message { - color: #999; - font-size: 1.2rem; - padding: 15px; - } -} - -.panel.defects { - - .btn-add-defect, - .btn-link-defect { - text-decoration: none; - } -} - -.add-defect-modal { - .container { - padding: 0; - width: 100%; - } - .panel.testcases .panel-body { - padding: 0; - } -} \ No newline at end of file diff --git a/client/src/components/ExecCycle/ExecCycleList.js b/client/src/components/ExecCycle/ExecCycleList.js deleted file mode 100644 index c9cceaf..0000000 --- a/client/src/components/ExecCycle/ExecCycleList.js +++ /dev/null @@ -1,136 +0,0 @@ -import React from "react"; -import PropTypes from "prop-types"; -import { Link } from "react-router-dom"; - -import CloneExecCycle from "./CloneExecCycle"; -import EditExecCycle from "./AddEditExecCycle"; -import ExecCycleToolbar from "./ExecCycleToolbar"; - -class ExecCycleList extends React.Component { - constructor(props) { - super(props); - this.state = { - cloneDialog: false, - editDialog: false - }; - this.handleClone = this.handleClone.bind(this); - this.handleDelete = this.handleDelete.bind(this); - this.handleSave = this.handleSave.bind(this); - this.handleSelect = this.handleSelect.bind(this); - this.hideCloneDialog = this.hideCloneDialog.bind(this); - this.hideEditDialog = this.hideEditDialog.bind(this); - this.showCloneDialog = this.showCloneDialog.bind(this); - this.showEditDialog = this.showEditDialog.bind(this); - } - componentDidMount() { - if(!this.props.execCycleId && this.props.selected && this.props.selected.id) { - this.handleSelect(this.props.selected.id, true); - } else { - this.props.reqExecCycles(); - } - } - componentWillReceiveProps(nextProps) { - const redirect = Boolean(!this.props.execCycleId && - nextProps.selected && - nextProps.selected.id); - - const newId = nextProps.execCycleId || - nextProps.selected && nextProps.selected.id; - - if(!nextProps.selected || - newId != nextProps.selected.id || - (this.props.selected && !this.props.execCycleId) - ) { - this.handleSelect(newId, redirect); - } - } - handleClone(id, cloneType) { - this.hideCloneDialog(); - this.props.onClone(id, cloneType); - } - handleDelete(execCycle) { - this.props.onDeleteExecCycle(execCycle); - this.hideEditDialog(); - } - handleSave(data) { - this.props.onSave(data); - this.hideEditDialog(); - } - handleSelect(execCycleId, redirect) { - this.props.onSelect({ - id: execCycleId - }, redirect); - } - hideCloneDialog() { - this.setState({ cloneDialog: false, execCycle: {} }); - } - hideEditDialog() { - this.setState({ editDialog: false, execCycle: {} }); - } - showCloneDialog(execCycle, ev) { - ev.preventDefault(); - this.setState({ - execCycle, - cloneDialog: true - }); - } - showEditDialog(execCycle, ev) { - ev.preventDefault(); - this.setState({ - execCycle, - editDialog: true - }); - } - render() { - const { execCycles } = this.props; - const { cloneDialog, editDialog, execCycle } = this.state; - return (<div className="cycles"> - <ExecCycleToolbar onSave={this.props.onSave} /> - <ul> - {execCycles.map(ec => (<li - className={`${ec.selected ? "bg-success selected" : ""}`} - key={ec.id} - > - <Link to={`/execution/${ec.id}`}> - {ec.name} - <button className="btn btn-link btn-edit" onClick={ev => this.showEditDialog(ec, ev)}> - <i className="glyphicon glyphicon-pencil text-info" /> - </button> - <button className="btn btn-link btn-clone" onClick={ev => this.showCloneDialog(ec, ev)}> - <i className="glyphicon glyphicon-duplicate text-info" /> - </button> - </Link> - </li>))} - </ul> - <CloneExecCycle - show={cloneDialog} - execCycle={execCycle} - onClose={this.hideCloneDialog} - onClone={this.handleClone} /> - <EditExecCycle - show={editDialog} - execCycle={execCycle} - onClose={this.hideEditDialog} - onDelete={this.handleDelete} - onSave={this.handleSave} /> - </div>); - } -} -ExecCycleList.propTypes = { - execCycleId: PropTypes.number, - execCycles: PropTypes.arrayOf(PropTypes.shape({ - id: PropTypes.number.isRequired, - name: PropTypes.string.isRequired - })), - onClone: PropTypes.func.isRequired, - onDeleteExecCycle: PropTypes.func.isRequired, - onSelect: PropTypes.func.isRequired, - onSave: PropTypes.func.isRequired, - reqExecCycles: PropTypes.func.isRequired, - selected: PropTypes.shape({ - id: PropTypes.number.isRequired, - name: PropTypes.string.isRequired - }) -}; - -export default ExecCycleList; diff --git a/client/src/components/ExecCycle/ExecCycleListContainer.js b/client/src/components/ExecCycle/ExecCycleListContainer.js deleted file mode 100644 index 1522c6d..0000000 --- a/client/src/components/ExecCycle/ExecCycleListContainer.js +++ /dev/null @@ -1,45 +0,0 @@ -import { connect } from "react-redux"; -import ExecCycleList from "./ExecCycleList"; - -import * as actions from "actions/ExecCycle"; -import { redirectToExecCycle } from "actions/Shared"; - -import { getExecCycles, getSelectedExecCycle } from "selectors/ExecCycle"; -import { isLoading } from "selectors/Shared"; - - -const mapStateToProps = state => ({ - selected: getSelectedExecCycle(state), - isLoading: isLoading(state), - execCycles: getExecCycles(state) -}); - -const mapDispatchToProps = dispatch => ({ - onClone(id, cloneType) { - dispatch(actions.reqCloneExecCycle(id, cloneType)); - }, - onDeleteExecCycle(execCycle) { - dispatch(actions.reqDeleteExecCycle(execCycle)); - }, - onSave(execCycle) { - dispatch(actions.reqSaveExecCycle(execCycle)); - }, - onSelect(execCycle, redirect) { - dispatch(actions.selectExecCycle(execCycle)); - if(redirect) - dispatch(redirectToExecCycle(execCycle.id)); - }, - reqExecCycles() { - dispatch(actions.reqExecCycles()); - } -}); - -const mergeProps = (stateProps, dispatchProps, ownProps) => ({ - ...stateProps, - ...dispatchProps, - ...ownProps -}); - -const ExecCycleListContainer = connect(mapStateToProps, mapDispatchToProps, mergeProps)(ExecCycleList); - -export default ExecCycleListContainer; diff --git a/client/src/components/ExecCycle/ExecCycleToolbar.js b/client/src/components/ExecCycle/ExecCycleToolbar.js deleted file mode 100644 index fbc3ad9..0000000 --- a/client/src/components/ExecCycle/ExecCycleToolbar.js +++ /dev/null @@ -1,45 +0,0 @@ -import React from "react"; -import PropTypes from "prop-types"; - -import AddEditExecCycle from "./AddEditExecCycle"; - - -class ExecCycleToolbar extends React.Component { - constructor(props) { - super(props); - this.state = { show: false }; - this.handleSave = this.handleSave.bind(this); - this.hideDialog = this.hideDialog.bind(this); - this.showDialog = this.showDialog.bind(this); - } - handleSave(data) { - this.props.onSave(data); - this.hideDialog(); - } - hideDialog() { - this.setState({ show: false }); - } - showDialog() { - this.setState({ show: true }); - } - render() { - const { show } = this.state; - return (<div className="toolbar"> - <button className="btn btn-link" onClick={this.showDialog}> - <i className="glyphicon glyphicon-plus text-info" /> - {" "} - <span className="text-info">New</span> - </button> - <h3 className="header-gradient-1">Execution Cycles</h3> - <AddEditExecCycle - show={show} - onClose={this.hideDialog} - onSave={this.handleSave} /> - </div>); - } -} -ExecCycleToolbar.propTypes = { - onSave: PropTypes.func.isRequired -}; - -export default ExecCycleToolbar; diff --git a/client/src/components/ExecCycle/ListView.js b/client/src/components/ExecCycle/ListView.js deleted file mode 100644 index 993f6b8..0000000 --- a/client/src/components/ExecCycle/ListView.js +++ /dev/null @@ -1,25 +0,0 @@ -import React from "react"; -import PropTypes from "prop-types"; - -import ExecCycles from "./ExecCycleListContainer"; -import TestRunList from "./TestRunListContainer"; - -class ListView extends React.Component { - render() { - const { execCycleId } = this.props; - return (<div className="exec-cycles list"> - <ExecCycles execCycleId={execCycleId} /> - {execCycleId - ? <div className="test-runs"> - <TestRunList /> - </div> - : null} - </div>); - } -} -ListView.propTypes = { - execCycleId: PropTypes.number, - testID: PropTypes.number -}; - -export default ListView; diff --git a/client/src/components/ExecCycle/TestImporter.js b/client/src/components/ExecCycle/TestImporter.js deleted file mode 100644 index dfe4d28..0000000 --- a/client/src/components/ExecCycle/TestImporter.js +++ /dev/null @@ -1,42 +0,0 @@ -import React from "react"; -import PropTypes from "prop-types"; - -import Selector from "components/TestSelector"; - -const TestImporter = props => { - const { - execCycle, - importActionContract, - onClose, - onInit, - show - } = props; - - const handleInit = () => onInit(execCycle); - - return (<Selector - show={show} - importActionContract={importActionContract} - onInit={handleInit} - onClose={onClose} />); -}; -TestImporter.propTypes = { - execCycle: PropTypes.shape({ - id: PropTypes.number, - name: PropTypes.string - }), - importActionContract: PropTypes.shape({ - type: PropTypes.string.isRequired, - key: PropTypes.string.isRequired, - extra: PropTypes.shape({ - execCycle: PropTypes.shape({ - id: PropTypes.number, - name: PropTypes.string - }) - }).isRequired - }), - onClose: PropTypes.func.isRequired, - onInit: PropTypes.func.isRequired, - show: PropTypes.bool -} -export default TestImporter; diff --git a/client/src/components/ExecCycle/TestImporterContainer.js b/client/src/components/ExecCycle/TestImporterContainer.js deleted file mode 100644 index b57055d..0000000 --- a/client/src/components/ExecCycle/TestImporterContainer.js +++ /dev/null @@ -1,48 +0,0 @@ -import { connect } from "react-redux"; -import TestImporter from "./TestImporter"; - -import { REQ_IMPORT_TESTS } from "constants/ExecCyclesActions"; -import * as actions from "actions/ExecCycle"; - -import { getAddEditState, showImportDialog } from "selectors/ExecCycle"; - -const mapStateToProps = state => ({ - execCycle: getAddEditState(state), - show: showImportDialog(state), - importActionContract: { - type: REQ_IMPORT_TESTS, - key: "tests", - extra: { - execCycle: getAddEditState(state) - } - } -}); - -const mapDispatchToProps = (dispatch, ownProps) => ({ - onClose() { - if(typeof(ownProps.onClose)==="function") - ownProps.onClose(); - }, - onInit(execCycle) { - console.log("FETCHING ExecCycle: ", execCycle.id) - dispatch(actions.reqExecCycle(execCycle.id)); - } -}); - -const mergeProps = (stateProps, dispatchProps, ownProps) => { - return { - ...stateProps, - ...dispatchProps, - show: ownProps.show, - allowAdd: ownProps.allowAdd !== undefined - ? ownProps.allowAdd - : true, - readOnly: ownProps.readOnly !== undefined - ? ownProps.readOnly - : false - }; -}; - -const TestImporterContainer = connect(mapStateToProps, mapDispatchToProps, mergeProps)(TestImporter); - -export default TestImporterContainer; diff --git a/client/src/components/ExecCycle/TestRun.js b/client/src/components/ExecCycle/TestRun.js deleted file mode 100644 index 4f28487..0000000 --- a/client/src/components/ExecCycle/TestRun.js +++ /dev/null @@ -1,223 +0,0 @@ -import React from "react"; -import PropTypes from "prop-types"; -import { Link } from "react-router-dom"; -import { - Button, - ButtonToolbar, - DropdownButton, - MenuItem, - Panel, - Table -} from "react-bootstrap"; -import Markdown from "react-markdown"; - -import TR_STATES from "common/constants/TestRunStates"; -import TR_COLORS from "constants/TestRunStateColors"; - -import dateFormat from "common/utils/dateFormat"; - -import LinkedDefect from "components/TestDesign/LinkedDefect"; -import DefectSelector from "components/Shared/DefectSelector"; - -import DefectModal from "./AddDefectModal"; - -class TestRun extends React.Component { - constructor(props) { - super(props); - this.state = { - showDefectModal: false, - showSelectDefectModal: false - }; - this.handleCancel = this.handleCancel.bind(this); - this.handleChangeStatus = this.handleChangeStatus.bind(this); - this.handleDelete = this.handleDelete.bind(this); - this.handleLinkDefects = this.handleLinkDefects.bind(this); - this.handleSaveDefect = this.handleSaveDefect.bind(this); - this.handleUnlinkDefect = this.handleUnlinkDefect.bind(this); - this.hideDefectModal = this.hideDefectModal.bind(this); - this.hideSelectDefectModal = this.hideSelectDefectModal.bind(this); - this.showDefectModal = this.showDefectModal.bind(this); - this.showSelectDefectModal = this.showSelectDefectModal.bind(this); - } - componentDidMount() { - this.props.onInit(this.props.execCycleId, this.props.testRunId); - } - handleCancel() { - this.props.onCancel(this.props.execCycleId); - } - handleChangeStatus(newStatus) { - this.props.onChangeStatus(this.props.testRun, newStatus); - } - handleDelete() { - if(confirm("Delete this test?")) - this.props.onDelete(this.props.testRun); - } - handleLinkDefects(defects) { - this.props.onLinkDefects(defects, this.props.testRun); - this.hideSelectDefectModal(); - } - handleSaveDefect(defect, files) { - this.props.onSaveDefect(defect, files, this.props.testRun); - this.hideDefectModal(); - } - handleUnlinkDefect(defect) { - this.props.onUnlinkDefect(this.props.testRun, defect); - } - hideDefectModal() { - this.setState({ - showDefectModal: false - }); - } - hideSelectDefectModal() { - this.setState({ - showSelectDefectModal: false - }); - } - showDefectModal() { - this.setState({ - showDefectModal: true - }); - } - showSelectDefectModal() { - this.setState({ - showSelectDefectModal: true - }); - } - render() { - const { - allowDelete, - isInProgress, - testRun - } = this.props; - const { defects, testCase } = testRun; - const { showDefectModal, showSelectDefectModal } = this.state; - - return (<div className="edit-tr"> - <div className="action-bar header-gradient-1"> - <ButtonToolbar> - {testRun.state == TR_STATES[2] - ? <Button bsSize="small" bsStyle="warning"> - <i className="glyphicon glyphicon-plus" /> - {" "} - Defect - </Button> - : null} - {allowDelete - ? <Button bsSize="small" bsStyle="danger" onClick={this.handleDelete}>Delete</Button> - : null} - <Button bsSize="small" onClick={this.handleCancel}>Close</Button> - </ButtonToolbar> - <h3>{testRun.name || ""}</h3> - </div> - <div className="container"> - <Panel> - <Panel.Heading> - <div className="header-buttons"> - {testCase && testCase.testPlan - ? <Link to={`/design/testplan/${testCase.testPlan.id}/testcase/edit/${testCase.id}`} - target="_blank" - className="test-link" - > - <span className="text-info">TC-{testCase.id}</span> - {" "} - <i className="glyphicon glyphicon-share text-info" /> - </Link> - : null} - {isInProgress - ? <DropdownButton - bsStyle={TR_COLORS[testRun.status]} - bsSize="small" - className="status-dd" - title={testRun.status} - id={`status-dd-${testRun.id}`} - > - {TR_STATES.map(s => (<MenuItem - key={s} - onSelect={() => this.handleChangeStatus(s)} - >{s}</MenuItem>))} - </DropdownButton> - : <Button - bsSize="small" - bsStyle={TR_COLORS[testRun.status]} - disabled - >{testRun.status}</Button>} - </div> - Details - </Panel.Heading> - <Panel.Body> - <div className="markdown-static" onClick={this.handleEdit}> - <Markdown source={testCase.description} /> - </div> - {testCase && testCase.description && testCase.description.value - ? <p className="description">{testCase.description.value}</p> - : null} - {testRun.runDate - ? <p className="run-date">Last run: {dateFormat(testRun.runDate)}</p> - : null} - </Panel.Body> - </Panel> - - <Panel className="defects"> - <Panel.Heading> - <div className="header-buttons"> - <Button bsStyle="link" bsSize="small" className="btn-link-defect" onClick={this.showSelectDefectModal}> - <i className="glyphicon glyphicon-link text-danger" /> - {" "} - <span className="text-danger">Link</span> - </Button> - <Button bsStyle="link" bsSize="small" className="btn-add-defect" onClick={this.showDefectModal}> - <i className="glyphicon glyphicon-plus text-danger" /> - {" "} - <span className="text-danger">New</span> - </Button> - </div> - <Panel.Title componentClass="h3">Defects</Panel.Title> - </Panel.Heading> - <Panel.Body> - {defects.length - ? <Table hover> - <tbody> - {defects.map(defect => - <LinkedDefect - allowDelete={true} - onDelete={this.handleUnlinkDefect} - key={`defect-${defect.id}`} - defect={defect} />)} - </tbody> - </Table> - : <p className="empty-message">No defects tagged</p>} - - </Panel.Body> - </Panel> - </div> - <DefectModal - show={showDefectModal} - testCase={testRun.testCase} - onSave={this.handleSaveDefect} - onClose={this.hideDefectModal} /> - <DefectSelector - show={showSelectDefectModal} - onSave={this.handleLinkDefects} - onClose={this.hideSelectDefectModal} /> - </div>); - } -} -TestRun.propTypes = { - allowDelete: PropTypes.bool.isRequired, - isInProgress: PropTypes.bool, - testRunId: PropTypes.number, - execCycleId: PropTypes.number.isRequired, - onCancel: PropTypes.func.isRequired, - onChangeStatus: PropTypes.func.isRequired, - onDelete: PropTypes.func.isRequired, - onInit: PropTypes.func.isRequired, - onLinkDefects: PropTypes.func.isRequired, - onSaveDefect: PropTypes.func.isRequired, - onUnlinkDefect: PropTypes.func.isRequired, - testRun: PropTypes.shape({ - id: PropTypes.number, - name: PropTypes.string - }) -}; - -export default TestRun; diff --git a/client/src/components/ExecCycle/TestRunContainer.js b/client/src/components/ExecCycle/TestRunContainer.js deleted file mode 100644 index 17b2e7e..0000000 --- a/client/src/components/ExecCycle/TestRunContainer.js +++ /dev/null @@ -1,65 +0,0 @@ -import { connect } from "react-redux"; - -import * as actions from "actions/ExecCycle"; -import { redirectToExecCycle } from "actions/Shared"; - -import TestRun from "./TestRun"; - -import { - allowDeleteTestRun, - getTestRun, - isInProgress -} from "selectors/ExecCycle"; -import { isLoading } from "selectors/Shared"; - -const mapStateToProps = state => ({ - allowDelete: allowDeleteTestRun(state), - isInProgress: isInProgress(state), - isLoading: isLoading(state), - testRun: getTestRun(state) -}); - -const mapDispatchToProps = dispatch => ({ - onLinkDefects(defects, testRun) { - dispatch(actions.reqLinkDefects(testRun, defects)); - }, - onCancel(execCycleId) { - dispatch(actions.resetTRAddEdit()); - dispatch(redirectToExecCycle(execCycleId)); - }, - onChangeStatus(testRun, status) { - dispatch(actions.reqSaveTestRun({ - ...testRun, - status - })); - }, - onDelete(testRun) { - dispatch(actions.reqDeleteTestRuns([ testRun.id ])); - }, - onInit(execCycleId, id) { - dispatch(actions.resetTRAddEdit()); - dispatch(actions.selectExecCycle({ - id: execCycleId - })); - dispatch(actions.reqExecCycles()); - dispatch(actions.reqTestRun(id)); - }, - onSaveDefect(defect, files, testRun) { - dispatch(actions.reqAddNewDefect(defect, files, testRun)); - }, - onUnlinkDefect(testRun, defect) { - dispatch(actions.reqUnlinkDefect(testRun, defect)); - } -}); - -const mergeProps = (ownProps, stateProps, dispatchProps) => { - return { - ...ownProps, - ...stateProps, - ...dispatchProps - }; -}; - -const TestRunContainer = connect(mapStateToProps, mapDispatchToProps, mergeProps)(TestRun); - -export default TestRunContainer; diff --git a/client/src/components/ExecCycle/TestRunList.js b/client/src/components/ExecCycle/TestRunList.js deleted file mode 100644 index 813e12f..0000000 --- a/client/src/components/ExecCycle/TestRunList.js +++ /dev/null @@ -1,184 +0,0 @@ -import React from "react"; -import PropTypes from "prop-types"; -import { - Table -} from "react-bootstrap"; - -import STATES from "common/constants/TestRunStates"; - -import ColumnFilter from "components/Shared/ColumnFilter/index"; - -import TestImporter from "./TestImporterContainer"; -import TestRunListItem from "./TestRunListItem"; -import TestRunsToolbar from "./TestRunsToolbar"; - -const testRunStatusFilterOptions = STATES.map(s => ({ id: s, name: s })); - - -class TestRunList extends React.Component { - constructor(props) { - super(props); - this.applyFilters = this.applyFilters.bind(this); - this.bulkDelete = this.bulkDelete.bind(this); - this.handleChangeStatus = this.handleChangeStatus.bind(this); - this.handleStatusFilter = this.handleStatusFilter.bind(this); - this.hideSelector = this.hideSelector.bind(this); - this.importTests = this.importTests.bind(this); - this.showSelector = this.showSelector.bind(this); - this.toggleSelect = this.toggleSelect.bind(this); - this.toggleSelectAll = this.toggleSelectAll.bind(this); - this.toggleSelector = this.toggleSelector.bind(this); - } - componentDidMount() { - if(this.props.execCycle && this.props.execCycle.id) - this.props.fetchTestRuns(this.props.execCycle); - } - componentWillReceiveProps(nextProps) { - if( - (nextProps.execCycle && !this.props.execCycle) || - (nextProps.execCycle && - this.props.execCycle && - nextProps.execCycle.id != this.props.execCycle.id - ) - ) { - this.props.fetchTestRuns(nextProps.execCycle); - } - } - applyFilters(testRuns) { - const { statusFilter } = this.props; - if(statusFilter) - return testRuns.filter(tr => tr.status == statusFilter.id); - return testRuns; - } - bulkDelete() { - this.props.onDeleteTestRuns(this.props.selectedTestRuns.map(tr => tr.id)); - } - handleChangeStatus(testRun, status) { - this.props.onChangeTestRunStatus(testRun, status); - } - handleStatusFilter(value) { - this.props.onChangeStatusFilter(value); - } - hideSelector() { - this.toggleSelector(false); - } - importTests() { - // console.log("import: ", data); - } - showSelector() { - this.toggleSelector(true); - } - toggleSelect(testRun, status) { - this.props.onToggleSelect(this.props.execCycle.id, testRun, status); - } - toggleSelectAll(ev) { - this.props.onToggleSelectAll(this.props.execCycle.id, ev.target.checked); - } - toggleSelector(show=false) { - this.props.onToggleImportDialog(show); - } - render() { - const { - allowAddTestRuns, - allowDeleteTestRuns, - allowEndExec, - allowStartExec, - allTestRunsSelected, - execCycle, - isInProgress, - onEndExec, - onStartExec, - testRuns, - showImportDialog, - statusFilter - } = this.props; - - return (<div className="test-runs-list"> - <TestRunsToolbar - allowAdd={allowAddTestRuns} - allowDelete={allowDeleteTestRuns} - allowEnd={allowEndExec} - allowStart={allowStartExec} - execCycle={execCycle} - onAdd={this.showSelector} - onDelete={this.bulkDelete} - onEnd={onEndExec} - onStart={onStartExec} /> - <Table striped condensed hover className="data-grid "> - <thead> - <tr> - <th> - <input - type="checkbox" - checked={allTestRunsSelected} - onChange={(ev) => this.toggleSelectAll(ev)} /> - </th> - <th>ID</th> - <th>Name</th> - <th> - Status - <ColumnFilter - items={testRunStatusFilterOptions} - selected={statusFilter} - onChange={this.handleStatusFilter} /> - </th> - <th>Defects</th> - </tr> - </thead> - <tbody> - {this.applyFilters(testRuns).map(tr => <TestRunListItem - key={`tr-${tr.id}`} - testRun={tr} - execCycleId={execCycle.id} - allowChangeStatus={isInProgress} - onChangeStatus={this.handleChangeStatus} - onToggle={this.toggleSelect} />)} - </tbody> - </Table> - <TestImporter - show={showImportDialog} - onClose={this.hideSelector} - onSave={this.importTests} /> - </div>); - } -} -TestRunList.propTypes = { - allowAddTestRuns: PropTypes.bool, - allowDeleteTestRuns: PropTypes.bool, - allowEndExec: PropTypes.bool, - allowStartExec: PropTypes.bool, - allTestRunsSelected: PropTypes.bool, - execCycle: PropTypes.shape({ - id: PropTypes.number.isRequired, - name: PropTypes.string.isRequired - }), - execCycleId: PropTypes.number, - fetchTestRuns: PropTypes.func.isRequired, - isInProgress: PropTypes.bool, - onChangeStatusFilter: PropTypes.func.isRequired, - onChangeTestRunStatus: PropTypes.func.isRequired, - onDeleteTestRuns: PropTypes.func.isRequired, - onEndExec: PropTypes.func.isRequired, - onStartExec: PropTypes.func.isRequired, - onToggleImportDialog: PropTypes.func.isRequired, - onToggleSelect: PropTypes.func.isRequired, - onToggleSelectAll: PropTypes.func.isRequired, - selectedTestRuns: PropTypes.arrayOf(PropTypes.shape({ - id: PropTypes.number.isRequired, - name: PropTypes.string.isRequired - })), - showImportDialog: PropTypes.bool, - statusFilter: PropTypes.shape({ - id: PropTypes.oneOfType([ - PropTypes.number, - PropTypes.string - ]), - name: PropTypes.string.isRequired - }), - testRuns: PropTypes.arrayOf(PropTypes.shape({ - id: PropTypes.number.isRequired, - name: PropTypes.string.isRequired - })) -}; - -export default TestRunList; diff --git a/client/src/components/ExecCycle/TestRunListContainer.js b/client/src/components/ExecCycle/TestRunListContainer.js deleted file mode 100644 index 7cd0594..0000000 --- a/client/src/components/ExecCycle/TestRunListContainer.js +++ /dev/null @@ -1,73 +0,0 @@ -import { connect } from "react-redux"; -import TestRunList from "./TestRunList"; - -import * as actions from "actions/ExecCycle"; - -import { - allowAddTestRun, - allowDeleteTestRuns, - allowEndExec, - allowStartExec, - getTestRuns, - getSelectedExecCycle, - getSelectedTestRuns, - getStatusFilter, - isInProgress, - areAllTestRunsSelected, - showImportDialog -} from "selectors/ExecCycle"; - -import { isLoading } from "selectors/Shared"; - -const mapStateToProps = state => ({ - allowAddTestRuns: allowAddTestRun(state), - allowDeleteTestRuns: allowDeleteTestRuns(state), - allowEndExec: allowEndExec(state), - allowStartExec: allowStartExec(state), - allTestRunsSelected: areAllTestRunsSelected(state), - isInProgress: isInProgress(state), - isLoading: isLoading(state), - execCycle: getSelectedExecCycle(state), - testRuns: getTestRuns(state), - selectedTestRuns: getSelectedTestRuns(state), - showImportDialog: showImportDialog(state), - statusFilter: getStatusFilter(state) -}); - -const mapDispatchToProps = dispatch => ({ - fetchTestRuns(execCycle) { - if(execCycle) - dispatch(actions.reqTestRuns(execCycle.id)); - }, - onChangeStatusFilter(value) { - dispatch(actions.changeStatusFilter(value)); - }, - onChangeTestRunStatus(testRun, status) { - dispatch(actions.reqSaveTestRun({ - ...testRun, - status - })); - }, - onDeleteTestRuns(idArr) { - dispatch(actions.reqDeleteTestRuns(idArr)); - }, - onEndExec(execCycle) { - dispatch(actions.reqEndExecCycle(execCycle)); - }, - onStartExec(execCycle) { - dispatch(actions.reqStartExecCycle(execCycle)); - }, - onToggleImportDialog(show) { - dispatch(actions.toggleImportDialog(show)); - }, - onToggleSelect(execCycleId, testRun, status) { - dispatch(actions.toggleSelect(execCycleId, testRun, status)); - }, - onToggleSelectAll(execCycleId, status) { - dispatch(actions.toggleSelectAll(execCycleId, status)); - } -}); - -const TestRunListContainer = connect(mapStateToProps, mapDispatchToProps)(TestRunList); - -export default TestRunListContainer; diff --git a/client/src/components/ExecCycle/TestRunListItem.js b/client/src/components/ExecCycle/TestRunListItem.js deleted file mode 100644 index ec8a7a7..0000000 --- a/client/src/components/ExecCycle/TestRunListItem.js +++ /dev/null @@ -1,69 +0,0 @@ -import React from "react"; -import { Link } from "react-router-dom"; -import PropTypes from "prop-types"; -import { - DropdownButton, - MenuItem -} from "react-bootstrap"; - -import TR_STATES from "common/constants/TestRunStates"; -import TR_COLORS from "constants/TestRunStateColors"; - -const TestRun = props => { - const { - allowChangeStatus, - execCycleId, - onChangeStatus, - onToggle, - testRun - } = props; - - const handleToggle = ev => { - onToggle(testRun, ev.target.checked); - }; - - const handleChangeStatus = newStatus => { - onChangeStatus(testRun, newStatus); - }; - - return (<tr> - <td> - <input - type="checkbox" - checked={testRun.selected} - onChange={handleToggle} /> - </td> - <td>{`TC-${testRun.testCase}`}</td> - <td> - <Link to={`/execution/${execCycleId}/test/${testRun.id}`}>{testRun.name}</Link> - </td> - <td> - {allowChangeStatus - ? <DropdownButton - bsStyle={TR_COLORS[testRun.status]} - bsSize="xsmall" - title={testRun.status} - id={`tr-${testRun.id}-status-dd`} - > - {TR_STATES.map(s => (<MenuItem - key={s} - onSelect={() => handleChangeStatus(s)} - >{s}</MenuItem>))} - </DropdownButton> - : testRun.status} - </td> - <td>{testRun.defects ? testRun.defects.length : 0}</td> - </tr>); -}; -TestRun.propTypes = { - allowChangeStatus: PropTypes.bool, - execCycleId: PropTypes.number, - onChangeStatus: PropTypes.func.isRequired, - onToggle: PropTypes.func.isRequired, - testRun: PropTypes.shape({ - id: PropTypes.number.isRequired, - name: PropTypes.string.isRequired - }) -}; - -export default TestRun; diff --git a/client/src/components/ExecCycle/TestRunsToolbar.js b/client/src/components/ExecCycle/TestRunsToolbar.js deleted file mode 100644 index 3f2996a..0000000 --- a/client/src/components/ExecCycle/TestRunsToolbar.js +++ /dev/null @@ -1,88 +0,0 @@ -import React from "react"; -import PropTypes from "prop-types"; -import { - Button, - OverlayTrigger, - Tooltip -} from "react-bootstrap"; - -const addTooltip = <Tooltip id="add-tooltip">Add Tests</Tooltip>; -const deleteTooltip = <Tooltip id="delete-tooltip">Delete Tests</Tooltip>; -const startTooltip = <Tooltip id="start-tooltip">Start</Tooltip>; -const endTooltip = <Tooltip id="end-tooltip">End</Tooltip>; - -class TestRunToolbar extends React.Component { - constructor(props) { - super(props); - this.handleEnd = this.handleEnd.bind(this); - this.handleStart = this.handleStart.bind(this); - } - handleEnd() { - this.props.onEnd(this.props.execCycle); - } - handleStart() { - this.props.onStart(this.props.execCycle); - } - render() { - const { - allowAdd, - allowDelete, - allowEnd, - allowStart, - execCycle, - onAdd, - onDelete - } = this.props; - return (<div className="toolbar"> - {execCycle && allowAdd - ? <OverlayTrigger placement="bottom" overlay={addTooltip}> - <Button bsStyle="link" onClick={onAdd}> - <i className="glyphicon glyphicon-plus text-info" /> - </Button> - </OverlayTrigger> - : null} - {execCycle && allowStart - ? <OverlayTrigger placement="bottom" overlay={startTooltip}> - <Button bsStyle="link" onClick={this.handleStart}> - <i className="glyphicon glyphicon-play" /> - </Button> - </OverlayTrigger> - : null} - {execCycle && allowEnd - ? <OverlayTrigger placement="bottom" overlay={endTooltip}> - <Button bsStyle="link" onClick={this.handleEnd}> - <i className="glyphicon glyphicon-stop text-warning" /> - </Button> - </OverlayTrigger> - : null} - {execCycle && allowDelete - ? <OverlayTrigger placement="bottom" overlay={deleteTooltip}> - <Button bsStyle="link" onClick={onDelete}> - <i className="glyphicon glyphicon-trash text-danger" /> - </Button> - </OverlayTrigger> - : null} - - <h3 className="header-gradient-1"> - Tests - {execCycle ? " - " + execCycle.name : ""} - </h3> - </div>); - } -} -TestRunToolbar.propTypes = { - allowAdd: PropTypes.bool, - allowDelete: PropTypes.bool, - allowEnd: PropTypes.bool, - allowStart: PropTypes.bool, - execCycle: PropTypes.shape({ - id: PropTypes.number.isRequired, - name: PropTypes.string.isRequired - }), - onAdd: PropTypes.func.isRequired, - onEnd: PropTypes.func.isRequired, - onDelete: PropTypes.func.isRequired, - onStart: PropTypes.func.isRequired -}; - -export default TestRunToolbar; diff --git a/client/src/components/ExecCycle/index.js b/client/src/components/ExecCycle/index.js deleted file mode 100644 index 0937a8b..0000000 --- a/client/src/components/ExecCycle/index.js +++ /dev/null @@ -1,23 +0,0 @@ -import React from "react"; -import PropTypes from "prop-types"; - -import ListView from "./ListView"; -import TestRun from "./TestRunContainer"; - -import "./ExecCycle.scss"; - -class ExecCycle extends React.Component { - render() { - const { mode, execCycleId, testRunId } = this.props; - return mode=="list" - ? <ListView execCycleId={execCycleId} /> - : <TestRun execCycleId={execCycleId} testRunId={testRunId} /> - } -} -ExecCycle.propTypes = { - mode: PropTypes.string.isRequired, - execCycleId: PropTypes.number, - testRunId: PropTypes.number -}; - -export default ExecCycle; diff --git a/client/src/components/Home/index.js b/client/src/components/Home/index.js deleted file mode 100644 index 22f3145..0000000 --- a/client/src/components/Home/index.js +++ /dev/null @@ -1,83 +0,0 @@ -import React from "react"; -import moment from "moment"; - -import Logo from "common/images/TestMan.png"; -import Button from "common/images/google_signin.png"; - -const changelog = [{ - date: "20180401", - changes: [ - "Added landing page", - "Filter Defects by Status and Assignee", - "Filter TestRuns by Status" - ] -}, { - date: "20180331", - changes: [ - "Fixed execution cycle routing", - "Unfinished execution cycles can no longer be stopped" - ] -}, { - date: "20180330", - changes: [ - "First alpha release" - ] -}]; - -const HomeApp = () => (<div className="login"> - <section className="login-panel"> - <img src={Logo} alt="TestMan Logo" className="logo" /> - <h1>TestMan</h1> - <p className="brief"> - A Minimalistic, Open-Source Test-Case Management tool - </p> - <a className="btn btn-login" href="/auth/google"> - <img src={Button} alt="Sign in with Google" /> - </a> - </section> - <section className="github-buttons"> - <iframe src="https://ghbtns.com/github-btn.html?user=schowdhuri&repo=testman&type=star&count=true" - frameBorder="0" - scrolling="0" - width="100px" - height="20px"></iframe> - <iframe src="https://ghbtns.com/github-btn.html?user=schowdhuri&repo=testman&type=watch&count=true&v=2" - frameBorder="0" - scrolling="0" - width="100px" - height="20px"></iframe> - <iframe src="https://ghbtns.com/github-btn.html?user=schowdhuri&repo=testman&type=fork&count=true" - frameBorder="0" - scrolling="0" - width="100px" - height="20px"></iframe> - </section> - <section className="changelog"> - <h2>What's next?</h2> - <p> - Track progress, see what's cooking and know what to expect next - <br/> - <a className="btn btn-sm btn-info btn-roadmap" href="https://github.com/schowdhuri/testman/projects/1?fullscreen=true"> - <i className="glyphicon glyphicon-road" /> - {" "} - v1 Roadmap - </a> - </p> - - <h2>Changelog</h2> - {changelog.map(log => (<React.Fragment key={log.date}> - <h3>{moment(log.date, "YYYYMMDD").format("MMM DD")}</h3> - <ul> - {log.changes.map((change, index) => - <li key={`change-${log.date}-${index}`}>{change}</li>)} - </ul> - </React.Fragment>))} - </section> - <footer> - © 2020 Subir Chowdhuri. - {" "} - Code released under the <a href="https://github.com/schowdhuri/testman/blob/master/LICENSE">MIT License</a> - </footer> -</div>); - -export default HomeApp; diff --git a/client/src/components/Shared/Attachment/Attachment.scss b/client/src/components/Shared/Attachment/Attachment.scss deleted file mode 100644 index bba6d86..0000000 --- a/client/src/components/Shared/Attachment/Attachment.scss +++ /dev/null @@ -1,97 +0,0 @@ -.attachment { - overflow: hidden; - position: relative; - display: inline-block; - margin: 0 10px 10px 0; - - .preview { - border-radius: 4px; - overflow: hidden; - - .image-type, - .unknown-type { - background-color: #e3e3e4; - display: grid; - grid-template-rows: 1fr 20px; - height: 120px; - width: 120px; - } - .image { - background-position: center; - background-repeat: no-repeat; - background-size: cover; - height: 100%; - width: 100%; - } - .unknown-type { - .glyphicon { - color: #999; - font-size: 3rem; - } - } - .dummy-image { - align-self: center; - justify-self: center; - } - .name { - align-self: center; - color: #606061; - font-size: 1.2rem; - overflow: hidden; - padding: 0 5px; - text-align: center; - text-overflow: ellipsis; - white-space: nowrap; - } - } - .controls { - background-color: #e3e3e4; - border-top-left-radius: 4px; - border-top-right-radius: 4px; - display: flex; - left: 0; - position: absolute; - right: 0; - top: -30px; - transition: top 0.3s; - .btn { - flex: 1; - padding: 2px 4px; - } - } - &:hover .controls { - top: 0; - } -} - -.view-attachment-modal { - .modal-body { - background-color: #e3e3e4; - } - .image { - text-align: center; - img { - max-width: 100%; - // max-height: 350px; - } - } - .editable-title { - margin-bottom: 0; - input[type="text"] { - font-size: 2.2rem; - } - } - - .details { - padding: 10px 0; - - .info { - display: inline-block; - margin: 0 10px 0 0; - - .glyphicon { - margin-right: 5px; - } - } - } -} diff --git a/client/src/components/Shared/Attachment/ViewEdit.js b/client/src/components/Shared/Attachment/ViewEdit.js deleted file mode 100644 index a8bb658..0000000 --- a/client/src/components/Shared/Attachment/ViewEdit.js +++ /dev/null @@ -1,91 +0,0 @@ -import React from "react"; -import PropTypes from "prop-types"; -import { Button, Modal } from "react-bootstrap"; - -import Title from "components/Shared/Title"; - - -class ViewEdit extends React.Component { - constructor(props) { - super(props); - this.state = { - name: props.attachment.name - }; - this.handleChangeName = this.handleChangeName.bind(this); - this.handleSave = this.handleSave.bind(this); - } - handleChangeName(val) { - this.setState({ name: val }); - } - handleSave() { - this.props.onSave({ - ...this.props.attachment, - name: this.state.name - }); - } - render() { - const { - attachment, - isImage, - onCancel, - onDownload, - show - } = this.props; - const { created, name, path, user } = attachment; - - return (<Modal show={show} className="view-attachment-modal"> - <Modal.Header> - <Modal.Title> - <Title placeholder="Name" onUpdate={this.handleChangeName} value={name} /> - </Modal.Title> - </Modal.Header> - <Modal.Body> - {isImage - ? <div className="image"> - <img src={path} alt={name} /> - </div> - : null} - <div className="details"> - {user - ? <div className="info" title="Uploaded by"> - <i className="glyphicon glyphicon-user text-info" /> - {user.name} - </div> - : null} - - <div className="info" title="Uploaded at"> - <i className="glyphicon glyphicon-time text-info" /> - {created} - </div> - </div> - </Modal.Body> - <Modal.Footer> - <Button onClick={onCancel}>Close</Button> - <Button bsStyle="info" onClick={onDownload}>Download</Button> - <Button - bsStyle="success" - onClick={this.handleSave} - disabled={name==this.state.name} - >Save</Button> - </Modal.Footer> - </Modal>); - } -} -ViewEdit.propTypes = { - attachment: PropTypes.shape({ - created: PropTypes.string.isRequired, - name: PropTypes.string.isRequired, - path: PropTypes.string.isRequired, - user: PropTypes.shape({ - name: PropTypes.string.isRequired - }) - }).isRequired, - isImage: PropTypes.bool.isRequired, - onDelete: PropTypes.func.isRequired, - onDownload: PropTypes.func.isRequired, - onCancel: PropTypes.func.isRequired, - onSave: PropTypes.func.isRequired, - show: PropTypes.bool.isRequired -}; - -export default ViewEdit; diff --git a/client/src/components/Shared/Attachment/index.js b/client/src/components/Shared/Attachment/index.js deleted file mode 100644 index 6f65688..0000000 --- a/client/src/components/Shared/Attachment/index.js +++ /dev/null @@ -1,104 +0,0 @@ -import React from "react"; -import PropTypes from "prop-types"; - -import ViewEdit from "./ViewEdit"; - -import "./Attachment.scss"; - -class Attachment extends React.Component { - constructor() { - super(...arguments); - this.state = { - modal: false - }; - this.handleDelete = this.handleDelete.bind(this); - this.handleDownload = this.handleDownload.bind(this); - this.handleSave = this.handleSave.bind(this); - this.hideModal = this.hideModal.bind(this); - this.showModal = this.showModal.bind(this); - } - handleDelete() { - this.props.onDelete(this.props.attachment); - } - handleDownload() { - this.props.onDownload(this.props.attachment); - this.hideModal(); - } - handleSave(val) { - this.props.onSave(val); - this.hideModal(); - } - hideModal() { - this.setState({ modal: false }); - } - showModal() { - this.setState({ modal: true }); - } - render() { - const { - allowDelete=true, - allowEdit=true, - allowDownload=true, - attachment - } = this.props; - const { name, path, file } = attachment; - const { modal } = this.state; - const reExtension = new RegExp("\\.png|\\.jpe?g|\\.gif|\\.svg$", "i"); - const reMime = new RegExp("image/png|image/jpe?g|image/gif|image/svg\\+xml", "i"); - const isImage = Boolean(reExtension.test(path) || (file && reMime.test(file.type))); - let preview; - if(isImage) { - preview = (<div className="image-type"> - <div className="image" style={{backgroundImage: `url(${path})`}} /> - <span className="name">{name}</span> - </div>); - } else { - preview = (<div className="unknown-type"> - <span className="dummy-image"> - <i className="glyphicon glyphicon-paperclip" /> - </span> - <span className="name">{name}</span> - </div>); - } - return (<div className="attachment"> - <div className="preview">{preview}</div> - <div className="controls"> - {!allowDelete || <button className="btn btn-link" onClick={this.handleDelete}> - <i className="glyphicon glyphicon-trash text-danger" /> - </button>} - {!allowDownload || <button className="btn btn-link" onClick={this.handleDownload}> - <i className="glyphicon glyphicon-download-alt text-success" /> - </button>} - {!allowEdit || <button className="btn btn-link" onClick={this.showModal}> - <i className="glyphicon glyphicon-zoom-in text-info" /> - </button>} - </div> - <ViewEdit show={modal} - isImage={isImage} - attachment={attachment} - onCancel={this.hideModal} - onDelete={this.handleDelete} - onDownload={this.handleDownload} - onSave={this.handleSave} /> - </div>); - } -} -Attachment.propTypes = { - attachment: PropTypes.shape({ - created: PropTypes.string.isRequired, - name: PropTypes.string.isRequired, - path: PropTypes.string.isRequired, - user: PropTypes.shape({ - name: PropTypes.string.isRequired - }) - }).isRequired, - allowDelete: PropTypes.bool, - allowEdit: PropTypes.bool, - allowDownload: PropTypes.bool, - onDelete: PropTypes.func.isRequired, - onDownload: PropTypes.func.isRequired, - onSave: PropTypes.func.isRequired - -}; - -export default Attachment; diff --git a/client/src/components/Shared/ColumnFilter/ColumnFilter.scss b/client/src/components/Shared/ColumnFilter/ColumnFilter.scss deleted file mode 100644 index a995154..0000000 --- a/client/src/components/Shared/ColumnFilter/ColumnFilter.scss +++ /dev/null @@ -1,48 +0,0 @@ -@import "~sass/bootstrap_variables"; - -.column-filter { - display: inline-block; - position: relative; - - .btn.filter { - text-decoration: none; - } - - ul { - background-color: #fff; - border-radius: 2px; - box-shadow: 2px 2px 6px #999; - display: none; - list-style-type: none; - margin: 0; - min-width: 9rem; - padding: 0; - position: absolute; - right: 0; - white-space: nowrap; - z-index: 2; - - &.open { - display: block; - } - - li { - cursor: pointer; - font-size: 1rem; - padding: 4px 16px; - text-align: center; - &:hover { - background-color: #e3e3e4; - color: #404041; - } - &.clear { - font-size: 0.9rem; - color: $brand-danger; - } - &.selected { - background-color: $btn-success-bg; - color: $btn-success-color; - } - } - } -} diff --git a/client/src/components/Shared/ColumnFilter/index.js b/client/src/components/Shared/ColumnFilter/index.js deleted file mode 100644 index 6b0c671..0000000 --- a/client/src/components/Shared/ColumnFilter/index.js +++ /dev/null @@ -1,92 +0,0 @@ -import React from "react"; -import PropTypes from "prop-types"; - -import "./ColumnFilter.scss"; - -class ColumnFilter extends React.Component { - constructor() { - super(...arguments); - this.state = { - open: false, - mouseDownInside: false - }; - this.handleFilter = this.handleFilter.bind(this); - this.handleMouseDown = this.handleMouseDown.bind(this); - this.handleMouseUp = this.handleMouseUp.bind(this); - this.handleOpen = this.handleOpen.bind(this); - this.onClickOutside = this.onClickOutside.bind(this); - } - componentDidMount() { - document.addEventListener("mousedown", this.onClickOutside, false); - } - componentWillUnmount() { - document.removeEventListener("mousedown", this.onClickOutside); - } - handleFilter(value) { - this.props.onChange(value); - this.setState({ open: false }); - } - handleMouseDown() { - this.setState({ mouseDownInside: true }); - } - handleMouseUp() { - this.setState({ mouseDownInside: false }); - } - handleOpen() { - this.setState({ open: true }); - } - onClickOutside() { - if(this.state.mouseDownInside) - return; - this.setState({ open: false }); - } - render() { - const { items = [], selected = null } = this.props; - - const { open } = this.state; - - return (<div className="column-filter" - onMouseDown={this.handleMouseDown} - onMouseUp={this.handleMouseUp} - > - <button className="btn-link filter" onClick={this.handleOpen}> - <i className={`glyphicon glyphicon-filter ${selected ? "" : "text-muted"}`} /> - </button> - <ul className={open ? "open" : ""}> - {selected - ? <li className="clear" onClick={() => this.handleFilter(null)}> - <i className="glyphicon glyphicon-remove" /> - {" "} - Clear - </li> - : null} - {items.map(item => (<li - key={`filter-item-${item.id}`} - onClick={() => this.handleFilter(item)} - className={selected && selected.id==item.id ? "selected" : ""} - > - {item.name} - </li>))} - </ul> - </div>); - } -}; -ColumnFilter.propTypes = { - items: PropTypes.arrayOf(PropTypes.shape({ - id: PropTypes.oneOfType([ - PropTypes.number, - PropTypes.string - ]), - name: PropTypes.string - })), - onChange: PropTypes.func.isRequired, - selected: PropTypes.shape({ - id: PropTypes.oneOfType([ - PropTypes.number, - PropTypes.string - ]), - name: PropTypes.string - }) -}; - -export default ColumnFilter; diff --git a/client/src/components/Shared/Comment/Comment.scss b/client/src/components/Shared/Comment/Comment.scss deleted file mode 100644 index cc5ae22..0000000 --- a/client/src/components/Shared/Comment/Comment.scss +++ /dev/null @@ -1,30 +0,0 @@ -.comment { - &, textarea { - font-size: 1.2rem; - } - margin-bottom: 20px; - .well { - margin-bottom: 10px; - } - .attachments { - margin-top: 15px; - } - .controls { - color: #999; - font-size: 1rem; - - span, a { - display: inline-block; - margin: 0 4px; - } - .glyphicon { - margin-right: 5px; - } - } - - .markdown-static { - p:last-child { - margin-bottom: 0; - } - } -} diff --git a/client/src/components/Shared/Comment/NewComment.js b/client/src/components/Shared/Comment/NewComment.js deleted file mode 100644 index b8bc55d..0000000 --- a/client/src/components/Shared/Comment/NewComment.js +++ /dev/null @@ -1,113 +0,0 @@ -import Alert from "react-s-alert"; -import React from "react"; -import PropTypes from "prop-types"; -import Dropzone from "react-dropzone"; -import { - Button, - ButtonToolbar, - Col, - FormControl, - FormGroup, - Row -} from "react-bootstrap"; - -import createTempAttachment from "utils/Shared/createTempAttachment"; - -import Attachment from "components/Shared/Attachment"; - - -class NewComment extends React.Component { - constructor() { - super(...arguments); - this.state = { - value: "", - attachments: [] - }; - this.handleAttach = this.handleAttach.bind(this); - this.handleChange = this.handleChange.bind(this); - this.handleDeleteAttachment = this.handleDeleteAttachment.bind(this); - this.handleSave = this.handleSave.bind(this); - } - handleAttach(files) { - if(files && files.length) { - const file = files[0]; - if(!file || !file.name || !file.size) { - Alert.error("Invalid file"); - return; - } - this.setState({ - attachments: [ - ...this.state.attachments, - createTempAttachment(file) - ] - }); - } - } - handleChange(ev) { - this.setState({ value: ev.target.value }); - } - handleDeleteAttachment(attachment) { - const index = this.state.attachments.findIndex(a => - a._id==attachment._id); - this.setState({ - attachments: [ - ...this.state.attachments.slice(0, index), - ...this.state.attachments.slice(index + 1) - ] - }); - } - handleSave() { - this.props.onSave( - this.state.value, - this.state.attachments.map(a => a.file) - ); - this.setState({ - value: "", - attachments: [] - }); - } - render() { - const { attachments, value } = this.state; - - return (<Dropzone - className="dropzone" - activeClassName="active" - multiple={false} - disableClick={true} - onDrop={this.handleAttach} - > - <FormGroup controlId="newComment"> - <FormControl - componentClass="textarea" - placeholder="Add Comment" - value={value} - onChange={this.handleChange} /> - </FormGroup> - <Row> - <Col md={12} className="attachments"> - {attachments.map(attachment => <Attachment - key={`attachment-${attachment._id}`} - attachment={attachment} - allowEdit={false} - allowDownload={false} - onDelete={this.handleDeleteAttachment} />)} - </Col> - </Row> - <ButtonToolbar> - <Button - bsSize="small" - bsStyle="success" - onClick={this.handleSave} - disabled={!value} - >Save</Button> - </ButtonToolbar> - </Dropzone>); - } -}; -NewComment.propTypes = { - value: PropTypes.string, - onAttachFile: PropTypes.func, - onSave: PropTypes.func.isRequired -}; - -export default NewComment; diff --git a/client/src/components/Shared/Comment/index.js b/client/src/components/Shared/Comment/index.js deleted file mode 100644 index 62ba916..0000000 --- a/client/src/components/Shared/Comment/index.js +++ /dev/null @@ -1,184 +0,0 @@ -import Alert from "react-s-alert"; -import React from "react"; -import PropTypes from "prop-types"; -import moment from "moment"; -import Markdown from "react-markdown"; -import Dropzone from "react-dropzone"; - -import { - FormControl, - FormGroup, - Well -} from "react-bootstrap"; - -import Attachment from "components/Shared/Attachment"; -import NewComment from "./NewComment"; - -import "./Comment.scss"; - -class Comment extends React.Component { - constructor(props) { - super(props); - this.state = { - content: props.data.content || "", - editMode: false - }; - this.handleAddAttachment = this.handleAddAttachment.bind(this); - this.handleCancel = this.handleCancel.bind(this); - this.handleChange = this.handleChange.bind(this); - this.handleDelete = this.handleDelete.bind(this); - this.handleRemoveAttachment = this.handleRemoveAttachment.bind(this); - this.handleEdit = this.handleEdit.bind(this); - this.handleSave = this.handleSave.bind(this); - } - handleAddAttachment(files) { - if(files && files.length) { - const file = files[0]; - if(!file || !file.name || !file.size) { - Alert.error("Invalid file"); - return; - } - this.props.onAddAttachment(file, this.props.data); - } - } - handleRemoveAttachment(attachment) { - this.props.onRemoveAttachment(attachment); - } - handleCancel(ev) { - ev.preventDefault(); - this.setState({ - content: this.props.content, - editMode: false - }); - } - handleChange(ev) { - this.setState({ content: ev.target.value }); - } - handleDelete(ev) { - ev.preventDefault(); - if(confirm("Delete this comment?")) - this.props.onDelete(this.props.data.id); - } - handleEdit(ev) { - ev.preventDefault(); - this.setState({ editMode: true }); - } - handleSave(ev) { - ev.preventDefault(); - this.setState({ editMode: false }); - this.props.onUpdate({ - ...this.props.data, - content: this.state.content - }); - } - render() { - const { data, onDownloadAttachment, onSaveAttachment } = this.props; - const { - attachments, - modified, - content, - user - } = data; - const { editMode } = this.state; - - const elAttachments = (<div className="attachments"> - {attachments.map(attachment => <Attachment - key={`attachment-${attachment.id}`} - attachment={attachment} - onDownload={onDownloadAttachment} - onSave={onSaveAttachment} - onDelete={this.handleRemoveAttachment} />)} - </div>); - return (<div className="comment"> - <Dropzone - className="dropzone" - activeClassName="active" - multiple={false} - disableClick={true} - disabled={!editMode} - onDrop={this.handleAddAttachment} - > - <React.Fragment> - {editMode - ? <React.Fragment> - <FormGroup controlId="editComment"> - <FormControl - placeholder="Edit Comment" - value={this.state.content} - onChange={this.handleChange} - componentClass="textarea" /> - </FormGroup> - {elAttachments} - </React.Fragment> - : <Well bsSize="large"> - <div className="markdown-static"> - <Markdown source={content} /> - </div> - {elAttachments} - </Well>} - {editMode - ? <div className="controls"> - <a href="#" onClick={this.handleSave}> - <i className="glyphicon glyphicon-ok text-success" /> - <span className="text-success">Save</span> - </a> - <a href="#" onClick={this.handleCancel}> - <i className="glyphicon glyphicon-remove text-warning" /> - <span className="text-warning">Cancel</span> - </a> - </div> - : <div className="controls"> - {user - ? <span className="user"> - <i className="glyphicon glyphicon-user" /> - {user.name} - </span> - : null} - <span className="modified"> - <i className="glyphicon glyphicon-time" title="Last updated" /> - {moment(modified).format("DD MMM, HH:mm")} - </span> - <a href="#" className="text-info" onClick={this.handleEdit}> - <i className="glyphicon glyphicon-pencil text-info" /> - Edit - </a> - <a href="#" className="text-danger" onClick={this.handleDelete}> - <i className="glyphicon glyphicon-trash text-danger" /> - Delete - </a> - </div>} - </React.Fragment> - </Dropzone> - </div>); - } -} -Comment.propTypes = { - content: PropTypes.string, - data: PropTypes.shape({ - id: PropTypes.number, - attachments: PropTypes.arrayOf(PropTypes.shape({ - id: PropTypes.number, - name: PropTypes.string, - path: PropTypes.string - })), - content: PropTypes.string.isRequired, - created: PropTypes.string, - modified: PropTypes.string, - user: PropTypes.shape({ - id: PropTypes.number.isRequired, - name: PropTypes.string.isRequired - }) - }).isRequired, - onAddAttachment: PropTypes.func.isRequired, - onDelete: PropTypes.func.isRequired, - onDownloadAttachment: PropTypes.func.isRequired, - onRemoveAttachment: PropTypes.func.isRequired, - onSaveAttachment: PropTypes.func.isRequired, - onUpdate: PropTypes.func.isRequired -} - - -Comment.New = NewComment; - -export default Comment; - diff --git a/client/src/components/Shared/DefectSelector/DefectSelector.scss b/client/src/components/Shared/DefectSelector/DefectSelector.scss deleted file mode 100644 index 6bee080..0000000 --- a/client/src/components/Shared/DefectSelector/DefectSelector.scss +++ /dev/null @@ -1,15 +0,0 @@ -.defect-selector-modal { - .modal-content { - border: none; - display: grid; - grid-template-rows: 56px minmax(200px, 60vh) 76px - } - .modal-body { - background: none; - padding: 0; - } -} -.defect-selector { - font-size: 1.1rem; - height: 100%; -} diff --git a/client/src/components/Shared/DefectSelector/Selector.js b/client/src/components/Shared/DefectSelector/Selector.js deleted file mode 100644 index 0216f98..0000000 --- a/client/src/components/Shared/DefectSelector/Selector.js +++ /dev/null @@ -1,101 +0,0 @@ -import React from "react"; -import PropTypes from "prop-types"; - -import { Button, Modal } from "react-bootstrap"; - -import GroupMultiSelect from "components/Shared/GroupMultiSelect"; - -import "./DefectSelector.scss"; - -class Selector extends React.Component { - constructor(props) { - super(props); - this.state = { - preSelectedItems: props.selectedItems || [] - }; - this.handleClose = this.handleClose.bind(this); - this.handleSave = this.handleSave.bind(this); - this.handleEnter = this.handleEnter.bind(this); - } - handleClose() { - this.props.onClose(this.state.preSelectedItems); - } - handleEnter() { - this.props.onInit(); - } - handleSave() { - this.props.onSave( - this.props.selectedItems, - this.props.importActionContract - ); - } - render() { - const { - allowAdd, - allowAddFolder, - items, - onDeselect, - onDeselectAll, - onSelect, - onSelectAll, - readOnly, - selectedItems, - show - } = this.props; - - return (<Modal - show={show} - onEnter={this.handleEnter} - className="defect-selector-modal" - onHide={this.handleClose} - > - <Modal.Header closeButton> - <Modal.Title>Select Defects</Modal.Title> - </Modal.Header> - <Modal.Body> - <div className="defect-selector"> - <GroupMultiSelect - items={items} - allowAdd={allowAdd} - allowAddFolder={allowAddFolder} - onChangePath={() => {}} - onDeselectAll={onDeselectAll} - onDeselectItem={onDeselect} - onSelectItem={onSelect} - onSelectAll={onSelectAll} - selectedItems={selectedItems} - search={true} - readOnly={readOnly} /> - </div> - </Modal.Body> - <Modal.Footer> - <Button onClick={this.handleClose}>Cancel</Button> - <Button bsStyle="success" onClick={this.handleSave} disabled={!selectedItems.length}>Ok</Button> - </Modal.Footer> - </Modal>); - } -} -Selector.propTypes = { - allowAdd: PropTypes.bool, - allowAddFolder: PropTypes.bool, - importActionContract: PropTypes.object, - items: PropTypes.arrayOf(PropTypes.shape({ - id: PropTypes.number.isRequired, - name: PropTypes.string.isRequired - })), - onClose: PropTypes.func.isRequired, - onDeselect: PropTypes.func.isRequired, - onDeselectAll: PropTypes.func.isRequired, - onInit: PropTypes.func.isRequired, - onSave: PropTypes.func.isRequired, - onSelect: PropTypes.func.isRequired, - onSelectAll: PropTypes.func.isRequired, - readOnly: PropTypes.bool, - selectedItems: PropTypes.arrayOf(PropTypes.shape({ - id: PropTypes.number.isRequired, - name: PropTypes.string.isRequired - })), - show: PropTypes.bool -}; - -export default Selector; diff --git a/client/src/components/Shared/DefectSelector/index.js b/client/src/components/Shared/DefectSelector/index.js deleted file mode 100644 index ac3163b..0000000 --- a/client/src/components/Shared/DefectSelector/index.js +++ /dev/null @@ -1,68 +0,0 @@ -import { connect } from "react-redux"; -import Selector from "./Selector"; - -import * as actions from "actions/DefectSelector"; - -import { - getAllItems, - getPath, - getSelected -} from "selectors/DefectSelector"; - -import { isLoading } from "selectors/Shared"; - - -const mapStateToProps = state => ({ - isLoading: isLoading(state), - items: getAllItems(state), - path: getPath(state), - selectedItems: getSelected(state) -}); - -const mapDispatchToProps = (dispatch, ownProps) => ({ - onClose(preSelectedItems) { - dispatch(actions.resetSelection(preSelectedItems)); - if(typeof(ownProps.onClose)==="function") - ownProps.onClose(); - }, - onDeselect(item) { - dispatch(actions.deselect(item)); - }, - onDeselectAll() { - dispatch(actions.deselectAll()); - }, - onInit() { - dispatch(actions.reqItems()); - if(typeof(ownProps.onInit)=="function") - ownProps.onInit(); - }, - onSelect(item, path) { - dispatch(actions.select(item, path)); - }, - onSelectAll(items) { - dispatch(actions.selectAll(items)); - }, - onSave(selectedItems) { - if(typeof(ownProps.onSave)=="function") - ownProps.onSave(selectedItems); - } -}); - -const mergeProps = (stateProps, dispatchProps, ownProps) => ({ - ...stateProps, - ...dispatchProps, - show: Boolean(ownProps.show), - allowAdd: ownProps.allowAdd !== undefined - ? ownProps.allowAdd - : true, - allowAddFolder: ownProps.allowAddFolder !== undefined - ? ownProps.allowAddFolder - : true, - readOnly: ownProps.readOnly !== undefined - ? ownProps.readOnly - : false -}); - -const SelectorContainer = connect(mapStateToProps, mapDispatchToProps, mergeProps)(Selector); - -export default SelectorContainer; diff --git a/client/src/components/Shared/Description/Description.scss b/client/src/components/Shared/Description/Description.scss deleted file mode 100644 index 18910f5..0000000 --- a/client/src/components/Shared/Description/Description.scss +++ /dev/null @@ -1,51 +0,0 @@ -.editable-description { - margin-bottom: 10px; - textarea { - box-sizing: padding-box; - font-size: 1.3rem; - margin: 0; - padding: 10px; - resize: none; - - &.static { - border: solid 1px transparent; - cursor: pointer; - outline: none; - &:hover { - background-color: #f2f2f2; - } - } - } - - .controls { - color: #999; - font-size: 1rem; - margin-top: 10px; - padding-left: 10px; - span, a { - display: inline-block; - margin: 0 5px; - - &:first-child { - margin-left: 0; - } - } - } - - .markdown-static { - border: solid 1px transparent; - border-radius: 4px; - cursor: pointer; - font-size: 1.3rem; - outline: none; - padding: 10px; - - &:hover { - background-color: #f2f2f2; - } - - p:last-child { - margin-bottom: 0; - } - } -} diff --git a/client/src/components/Shared/Description/index.js b/client/src/components/Shared/Description/index.js deleted file mode 100644 index c408a31..0000000 --- a/client/src/components/Shared/Description/index.js +++ /dev/null @@ -1,118 +0,0 @@ -import React from "react"; -import PropTypes from "prop-types"; -import TextareaAutosize from 'react-autosize-textarea'; -import Markdown from "react-markdown"; - -import "./Description.scss"; - -class Description extends React.Component { - constructor(props) { - super(props); - this.state = { - value: props.value || "", - editMode: false, - mouseDownInside: false - }; - this.handleCancel = this.handleCancel.bind(this); - this.handleChange = this.handleChange.bind(this); - this.handleEdit = this.handleEdit.bind(this); - this.handleKeyUp = this.handleKeyUp.bind(this); - this.handleMouseDown = this.handleMouseDown.bind(this); - this.handleMouseUp = this.handleMouseUp.bind(this); - this.handleSave = this.handleSave.bind(this); - this.onClickOutside = this.onClickOutside.bind(this); - } - componentDidMount() { - document.addEventListener("mousedown", this.onClickOutside, false); - } - componentWillReceiveProps(nextProps) { - if(nextProps.value != this.props.value) - this.setState({ value: nextProps.value }); - } - componentWillUnmount() { - document.removeEventListener("mousedown", this.onClickOutside); - } - handleCancel(ev) { - ev.preventDefault(); - this.setState({ - value: this.props.value, - editMode: false - }); - } - handleChange(ev) { - this.setState({ value: ev.target.value }); - if(typeof(this.props.onChange)=="function") - this.props.onChange(ev.target.value); - } - handleEdit(ev) { - ev.preventDefault(); - this.setState({ editMode: true }); - } - handleKeyUp(ev) { - if(ev.keyCode == 27) { // ESC - this.handleCancel(ev); - } - } - handleMouseDown() { - this.setState({ mouseDownInside: true }); - } - handleMouseUp() { - this.setState({ mouseDownInside: false }); - } - handleSave(ev) { - if(ev) - ev.preventDefault(); - this.setState({ editMode: false }); - this.props.onUpdate(this.state.value); - } - onClickOutside() { - if(this.state.mouseDownInside) - return; - if(this.state.editMode) { - this.handleSave(); - } - } - render() { - const { value, editMode } = this.state; - const { placeholder="Edit Description" } = this.props; - - return (<div - className="editable-description" - onMouseDown={this.handleMouseDown} - onMouseUp={this.handleMouseUp} - > - {editMode - ? <React.Fragment> - <TextareaAutosize - ref={ref => this.textField = ref} - tabIndex={-1} - className={editMode ? "form-control" : "form-control static"} - placeholder={placeholder} - value={value} - onChange={this.handleChange} - onKeyUp={this.handleKeyUp} /> - <div className="controls"> - <a href="#" onClick={this.handleSave}> - <i className="glyphicon glyphicon-ok text-success" /> - <span className="text-success">Save</span> - </a> - <a href="#" onClick={this.handleCancel}> - <i className="glyphicon glyphicon-remove text-warning" /> - <span className="text-warning">Cancel</span> - </a> - </div> - </React.Fragment> - : <div className="markdown-static" onClick={this.handleEdit}> - <Markdown source={value} /> - </div>} - </div>); - } -} -Description.propTypes = { - onChange: PropTypes.func, - onUpdate: PropTypes.func.isRequired, - placeholder: PropTypes.string, - value: PropTypes.string -}; - -export default Description; diff --git a/client/src/components/Shared/GroupMultiSelect/Breadcrumb.js b/client/src/components/Shared/GroupMultiSelect/Breadcrumb.js deleted file mode 100644 index f2c3faa..0000000 --- a/client/src/components/Shared/GroupMultiSelect/Breadcrumb.js +++ /dev/null @@ -1,72 +0,0 @@ -import React from "react"; -import PropTypes from "prop-types"; -import PathTree from "./PathTree"; - -class Breadcrumb extends React.Component { - constructor(props) { - super(props); - this.state = { - showTree: false - }; - this.handleNav = this.handleNav.bind(this); - this.handleNavUp = this.handleNavUp.bind(this); - this.handleTreeClose = this.handleTreeClose.bind(this); - this.handleTreeOpen = this.handleTreeOpen.bind(this); - this.toggleTree = this.toggleTree.bind(this); - } - handleNav(item) { - this.props.onNav(item); - } - handleNavUp() { - this.props.onNavUp(); - } - handleTreeClose() { - this.setState({ - showTree: false - }); - } - handleTreeOpen() { - this.setState({ - showTree: true - }); - } - toggleTree() { - this.setState({ - showTree: !this.state.showTree - }); - } - render() { - const { path, onNav} = this.props; - const { showTree } = this.state; - return (<ul className="breadcrumb"> - <li> - {path && path.length - ? <a href="javascript:;" onClick={this.handleNav}><i className="glyphicon glyphicon-home" /></a> - : <span><i className="glyphicon glyphicon-home" /></span>} - </li> - {path && path.length ? <li>{path[path.length-1].name}</li> : null} - {path && path.length > 1 - ? (<li className="overflow" onClick={this.toggleTree}> - <i className="fa fa-chevron-down" /> - {showTree ? <PathTree path={path} onClose={this.handleTreeClose} onNav={onNav} /> : null} - </li>) - : null} - {path && path.length - ? <li className="back"><a href="javascript:;" onClick={this.handleNavUp}>Back</a></li> - : null} - </ul>); - } -} -Breadcrumb.propTypes = { - onNav: PropTypes.func.isRequired, - onNavUp: PropTypes.func.isRequired, - path: PropTypes.arrayOf(PropTypes.shape({ - name: PropTypes.string.isRequired, - id: PropTypes.oneOfType([ - PropTypes.string, - PropTypes.number - ]).isRequired - })) -}; - -export default Breadcrumb; diff --git a/client/src/components/Shared/GroupMultiSelect/Filter.js b/client/src/components/Shared/GroupMultiSelect/Filter.js deleted file mode 100644 index bff099e..0000000 --- a/client/src/components/Shared/GroupMultiSelect/Filter.js +++ /dev/null @@ -1,20 +0,0 @@ -import React from "react"; -import PropTypes from "prop-types"; - -const Filter = ({ value, onFilter }) => { - let txtFilter; - return ( - <input className="filter form-control" - type="text" - placeholder="Search" - onChange={() => { onFilter(txtFilter.value); }} - ref={el=>{ txtFilter = el; }} - value={value} /> - ); -}; -Filter.propTypes = { - value: PropTypes.string, - onFilter: PropTypes.func.isRequired -}; - -export default Filter; diff --git a/client/src/components/Shared/GroupMultiSelect/GroupMultiSelect.js b/client/src/components/Shared/GroupMultiSelect/GroupMultiSelect.js deleted file mode 100644 index ccd7f17..0000000 --- a/client/src/components/Shared/GroupMultiSelect/GroupMultiSelect.js +++ /dev/null @@ -1,114 +0,0 @@ -import deepEqual from "deep-equal"; -import React from "react"; -import PropTypes from "prop-types"; - -import ListA from "./ListA"; -import ListB from "./ListB"; - -import "./GroupMultiSelect.scss"; - -class GroupMultiSelect extends React.Component { - componentWillMount() { - this.props.onInit(this.props.cid); - } - componentWillReceiveProps(nextProps) { - if(this.props.path && !deepEqual(nextProps.path, this.props.path)) { - this.props.onChangePath(nextProps.path); - } - } - componentWillUnmount() { - this.props.onDestroy(); - } - render() { - const { - allowAdd=true, - allowAddFolder=true, - filterText, - unselectedItems, - onDeselectAll, - onDeselectItem, - onFilter, - onNav, - onNavDown, - onNavUp, - onSelectAll, - onSelectItem, - path, - readOnly, - selectedItems, - showFilter - } = this.props; - - const listAProps = { - allowAdd, - allowAddFolder, - filterText, - onFilter, - onNav, - onNavDown, - onNavUp, - onSelectAll, - onSelectItem, - path, - readOnly, - showFilter, - unselectedItems - }; - const listBProps = { - onDeselectItem, - onDeselectAll, - readOnly, - selectedItems - }; - return (<div className={`two-way-group-list ${readOnly ? "readonly" : ""}`}> - <ListA {...listAProps} /> - <ListB {...listBProps} /> - </div>); - } -} -GroupMultiSelect.propTypes = { - allowAdd: PropTypes.bool, - allowAddFolder: PropTypes.bool, - cid: PropTypes.oneOfType([ - PropTypes.string, - PropTypes.number - ]).isRequired, - unselectedItems: PropTypes.arrayOf(PropTypes.shape({ - name: PropTypes.string.isRequired, - id: PropTypes.oneOfType([ - PropTypes.string, - PropTypes.number - ]).isRequired - })), - selectedItems: PropTypes.arrayOf(PropTypes.shape({ - id: PropTypes.oneOfType([ - PropTypes.number, - PropTypes.string - ]).isRequired, - name: PropTypes.string.isRequired, - include: PropTypes.bool - })).isRequired, - filterText: PropTypes.string, - onChangePath: PropTypes.func.isRequired, - onInit: PropTypes.func.isRequired, - onNav: PropTypes.func, - onNavDown: PropTypes.func, - onNavUp: PropTypes.func, - onDestroy: PropTypes.func.isRequired, - onFilter: PropTypes.func.isRequired, - onSelectItem: PropTypes.func.isRequired, - onSelectAll: PropTypes.func.isRequired, - onDeselectItem: PropTypes.func.isRequired, - onDeselectAll: PropTypes.func.isRequired, - path: PropTypes.arrayOf(PropTypes.shape({ - name: PropTypes.string.isRequired, - id: PropTypes.oneOfType([ - PropTypes.string, - PropTypes.number - ]).isRequired - })), - readOnly: PropTypes.bool, - showFilter: PropTypes.bool -}; - -export default GroupMultiSelect; diff --git a/client/src/components/Shared/GroupMultiSelect/GroupMultiSelect.scss b/client/src/components/Shared/GroupMultiSelect/GroupMultiSelect.scss deleted file mode 100644 index 9197245..0000000 --- a/client/src/components/Shared/GroupMultiSelect/GroupMultiSelect.scss +++ /dev/null @@ -1,248 +0,0 @@ -.two-way-group-list { - $background: #f2f2f2; - $primary: #f8f5f0; - $border-color: #dfd7ca; - $border-color-active: $primary; - $shadow-color: #999; - $active-color: #404041; - $inactive-bg: #f2f2f2; - $active-bg: #fff; - $hover-bg: #f8f5f0; - $hover-color: #404041; - $breadcrumb-bg: #dfd7ca; - $header-bg: #f8f5f0; - $group-header-bg: lighten($breadcrumb-bg, 5%); - $group-header-hover-bg: #dfd7ca; - $normal-text: #606061; - $icon-color: #325d88; - - - display: grid; - grid-template-columns: 1fr 1fr; - grid-template-rows: 100%; - height: 100%; - position: relative; - background-color: $background; - - .glyphicon { - color: $icon-color; - } - - ul.items { - list-style-type: none; - margin: 0; - padding: 0; - - li { - box-sizing: border-box; - cursor: default; - overflow: hidden; - padding: 10px 25px 10px 10px; - position: relative; - text-overflow: ellipsis; - color: $normal-text; - - &:hover { - background-color: $hover-bg; - color: $hover-color; - .glyphicon { - visibility: visible; - } - } - } - li.group { - background-color: $group-header-bg; - font-weight: 600; - padding: 10px; - - &:hover { - background-color: $group-header-hover-bg; - color: $active-color; - } - } - .glyphicon-plus, - .glyphicon-remove { - cursor: pointer; - float: right; - font-size: 0.8rem; - padding: 5px 8px 5px 5px; - position: absolute; - right: 0; - top: 10px; - visibility: hidden; - } - .glyphicon-folder-close { - margin-right: 10px; - } - } - - .left, - .right { - box-sizing: border-box; - border: solid 1px $border-color; - display: grid; - } - .left { - grid-template-rows: 48px 1fr; - &.has-filter { - grid-template-rows: 100px 1fr; - } - } - .right { - grid-template-rows: 48px 1fr; - border-left: none; - } - .list-header { - background: $header-bg; - position: relative; - li { - height: 20px; - vertical-align: middle; - } - } - li.back a, - .right .list-header .btn-link { - color: $icon-color; - font-size: 1.1rem; - font-weight: 500; - text-transform: uppercase; - } - .breadcrumb { - background: $breadcrumb-bg; - border-radius: 0; - margin: 0; - padding: 12px 10px; - - li.back { - float: right; - a, a:hover, a:focus { - text-decoration: none; - } - } - & > li:nth-child(2) { - max-width: 160px; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - } - li.overflow { - cursor: pointer; - height: 20px; - padding: 0 5px 2px 5px; - - &::before { - display: none; - } - &:hover { - background: transparent; - } - & > .glyphicon { - font-size: 0.7rem; - } - &:hover > .glyphicon { - color: $primary; - } - } - - li:hover { - color: black; - } - } - .list-search, - .right .list-header { - padding: 8px 10px; - box-sizing: border-box; - } - label.excluded { - display: inline-block; - line-height: 32px; - // flex: 3; - } - .list-search { - position: relative; - i.glyphicon-search { - position: absolute; - top: 16px; - left: 20px; - opacity: 0.6; - } - input.filter { - flex: 3; - border-width: 1px; - height: auto; - margin: 0; - font-size: 12px; - border-width: 1px; - padding: 6px 10px 6px 30px; - } - } - .right .list-header { - display: grid; - grid-template-columns: 3fr 2fr; - - .btn-link { - height: 32px; - min-width: 6em; - outline: none; - padding-right: 0; - text-align: right; - - &:hover, - &:focus { - text-decoration: none; - } - } - } - .multiselect-list-wrapper { - border-top: solid 1px lighten($border-color, 5%); - overflow: auto; - } - &.readonly { - .btn-select-all { - display: none; - } - ul li button { - // cursor: default; - &:hover { - // background-color: inherit; - // color: inherit; - .glyphicon-plus, - .glyphicon-remove { - visibility: hidden; - } - } - } - } - - .path-tree { - background: #fff; - box-shadow: 1px 1px 4px $shadow-color; - cursor: default; - font-size: 0.9rem; - left: 2px; - right: 2px; - position: absolute; - top: 50px; - z-index: 10; - - .level { - padding: 10px; - - &:hover { - background-color: $inactive-bg; - } - .glyphicon { - margin-right: 5px; - font-size: 0.8rem; - } - } - .active { - color: $primary; - cursor: pointer; - } - .spacer { - display: inline-block; - width: 10px; - } - } -} diff --git a/client/src/components/Shared/GroupMultiSelect/ListA.js b/client/src/components/Shared/GroupMultiSelect/ListA.js deleted file mode 100644 index 4da0125..0000000 --- a/client/src/components/Shared/GroupMultiSelect/ListA.js +++ /dev/null @@ -1,90 +0,0 @@ -import React from "react"; -import PropTypes from "prop-types"; - -import Breadcrumb from "./Breadcrumb"; -import Filter from "./Filter"; -import UnselectedItem from "./UnselectedItem"; - -class ListA extends React.Component { - constructor(props) { - super(props); - this.handleSelectAll = this.handleSelectAll.bind(this); - this.handleSelectItem = this.handleSelectItem.bind(this); - } - handleSelectAll() { - if(!this.props.readOnly) - this.props.onSelectAll(this.props.unselectedItems); - } - handleSelectItem(item) { - if(!this.props.readOnly) - this.props.onSelectItem(item, this.props.path); - } - render() { - const { - allowAdd, - allowAddFolder, - filterText, - onFilter, - onNav, - onNavDown, - onNavUp, - path, - showFilter, - unselectedItems - } = this.props; - - return (<div className={`left ${showFilter!==false ? "has-filter" : ""}`}> - <div className="list-header"> - <Breadcrumb - onNav={onNav} - onNavUp={onNavUp} - path={path} /> - {showFilter !== false ? <div className="list-search"> - <i className="glyphicon glyphicon-search" /> - <Filter value={filterText} onFilter={onFilter} /> - </div> : null} - </div> - <div className="multiselect-list-wrapper"> - <ul className="items"> - {unselectedItems ? unselectedItems.map(item => (<UnselectedItem - key={item.id} - allowAdd={allowAdd} - allowAddFolder={allowAddFolder} - item={item} - onSelectItem={this.handleSelectItem} - onNavDown={onNavDown} />)) : null} - </ul> - </div> - </div>); - } -} -ListA.propTypes = { - allowAdd: PropTypes.bool, - allowAddFolder: PropTypes.bool, - allowSelectAll: PropTypes.bool, - filterText: PropTypes.string, - onFilter: PropTypes.func.isRequired, - onNav: PropTypes.func.isRequired, - onNavDown: PropTypes.func.isRequired, - onNavUp: PropTypes.func.isRequired, - onSelectItem: PropTypes.func.isRequired, - onSelectAll: PropTypes.func.isRequired, - path: PropTypes.arrayOf(PropTypes.shape({ - name: PropTypes.string.isRequired, - id: PropTypes.oneOfType([ - PropTypes.string, - PropTypes.number - ]).isRequired - })), - readOnly: PropTypes.bool, - showFilter: PropTypes.bool, - unselectedItems: PropTypes.arrayOf(PropTypes.shape({ - name: PropTypes.string.isRequired, - id: PropTypes.oneOfType([ - PropTypes.string, - PropTypes.number - ]).isRequired - })).isRequired -}; - -export default ListA; diff --git a/client/src/components/Shared/GroupMultiSelect/ListB.js b/client/src/components/Shared/GroupMultiSelect/ListB.js deleted file mode 100644 index eee8560..0000000 --- a/client/src/components/Shared/GroupMultiSelect/ListB.js +++ /dev/null @@ -1,51 +0,0 @@ -import React from "react"; -import PropTypes from "prop-types"; - -const ListB = ({ selectedItems, onDeselectItem, onDeselectAll, readOnly }) => { - const leaves = selectedItems ? selectedItems.filter(item => !item.isGroup) : null; - if(readOnly) { - onDeselectAll = () => {}; - onDeselectItem = () => {}; - } - return (<div className="right"> - <div className="list-header"> - {leaves - ? <label className="excluded">{leaves.length===1 - ? "1 item selected" - : leaves.length + " items selected"} - </label> - : null} - {leaves.length - ? <button onClick={onDeselectAll} className="btn-select-all btn-link">Remove All</button> - : null} - </div> - <div className="multiselect-list-wrapper"> - <ul className="items"> - {selectedItems ? selectedItems.map(item => { - if(item.isGroup) { - return (<li className="group" key={item.id}>{item.name}</li>); - } - return (<li key={item.id} onClick={()=>{onDeselectItem(item);}}> - {item.hasChildren ? <i className="glyphicon glyphicon-folder-close" /> : null} - {item.name} - <i className="glyphicon glyphicon-remove" /> - </li>); - }) : null} - </ul> - </div> - </div>); -}; -ListB.propTypes = { - selectedItems: PropTypes.arrayOf(PropTypes.shape({ - name: PropTypes.string.isRequired, - id: PropTypes.oneOfType([ - PropTypes.string, - PropTypes.number - ]).isRequired - })).isRequired, - readOnly: PropTypes.bool, - onDeselectItem: PropTypes.func.isRequired, - onDeselectAll: PropTypes.func.isRequired -}; - -export default ListB; diff --git a/client/src/components/Shared/GroupMultiSelect/PathTree.js b/client/src/components/Shared/GroupMultiSelect/PathTree.js deleted file mode 100644 index bbeb738..0000000 --- a/client/src/components/Shared/GroupMultiSelect/PathTree.js +++ /dev/null @@ -1,84 +0,0 @@ -import React from "react"; -import PropTypes from "prop-types"; - -class PathTree extends React.Component { - constructor(props) { - super(props); - this.state = { - mouseDownInside: false - }; - this.handleClose = this.handleClose.bind(this); - this.handleMouseDown = this.handleMouseDown.bind(this); - this.handleMouseUp = this.handleMouseUp.bind(this); - this.handleNav = this.handleNav.bind(this); - this.onClickOutside = this.onClickOutside.bind(this); - } - componentDidMount() { - window.addEventListener("mousedown", this.onClickOutside, false); - } - componentWillUnmount() { - window.removeEventListener("mousedown", this.onClickOutside); - } - handleClose() { - this.props.onClose(); - } - handleMouseDown() { - this.setState({ mouseDownInside: true }); - } - handleMouseUp() { - this.setState({ mouseDownInside: false }); - } - handleNav(item) { - const { path, onNav } = this.props; - if(path && path.length && item.id !== path[path.length-1].id) - onNav(item); - } - onClickOutside() { - if(this.state.mouseDownInside) - return; - setTimeout(() => this.handleClose(), 100); - // ^ prevents re-opening PathTree if chevron is clicked - // remove setTimeout and click on the chevron to see the difference :) - } - render() { - const { path } = this.props; - return (<div className="path-tree" - onMouseDown={this.handleMouseDown} - onMouseUp={this.handleMouseUp}> - - <div className="level active" onClick={() => this.handleNav({})}> - <i className="glyphicon glyphicon-home" /> - </div> - - {path.map((level, num) => { - const spacers = []; - for(let i=1; i<=num + 1; i++) { - spacers.push(<div className="spacer" key={`tree-level-${num}-spacer-${i}`} />); - } - return (<div - className={`level ${num<path.length-1 ? " active" : ""}`} - key={`tree-level-${num}`} - onClick={() => this.handleNav(level)} - > - {spacers} - {num>=0 && num<path.length-1 ? <i className="glyphicon glyphicon-folder-close" /> : null} - {num==path.length-1 ? <i className="glyphicon glyphicon-menu-right" /> : null} - <span>{level.name}</span> - </div>); - })} - </div>); - } -} -PathTree.propTypes = { - onClose: PropTypes.func.isRequired, - onNav: PropTypes.func.isRequired, - path: PropTypes.arrayOf(PropTypes.shape({ - name: PropTypes.string.isRequired, - id: PropTypes.oneOfType([ - PropTypes.string, - PropTypes.number - ]).isRequired - })) -}; - -export default PathTree; diff --git a/client/src/components/Shared/GroupMultiSelect/UnselectedItem.js b/client/src/components/Shared/GroupMultiSelect/UnselectedItem.js deleted file mode 100644 index 1f1a6ac..0000000 --- a/client/src/components/Shared/GroupMultiSelect/UnselectedItem.js +++ /dev/null @@ -1,66 +0,0 @@ -import React from "react"; -import PropTypes from "prop-types"; - -class UnselectedItem extends React.Component { - constructor(props) { - super(props); - this.handleAdd = this.handleAdd.bind(this); - this.handleClick = this.handleClick.bind(this); - this.hasChildren = this.hasChildren.bind(this); - } - handleAdd(ev) { - ev.stopPropagation(); - if(!this.props.allowAdd) { - return; - } - if(this.hasChildren()) { - if(this.props.allowAddFolder && this.props.item.selectable) { - this.props.onSelectItem(this.props.item); - } - } else { - this.props.onSelectItem(this.props.item); - } - } - handleClick() { - if(this.hasChildren()) { - this.props.onNavDown(this.props.item); - } else if(this.props.allowAdd) { - this.props.onSelectItem(this.props.item); - } - } - hasChildren() { - const { item } = this.props; - return (item.items instanceof Array && item.items.length) || item.hasChildren; - } - render() { - const { item } = this.props; - - return (<li onClick={this.handleClick}> - {this.hasChildren() ? <i className="glyphicon glyphicon-folder-close" /> : null} - {item.name} - - {item.selectable || !this.hasChildren() - ? <i className="glyphicon glyphicon-plus" onClick={this.handleAdd}/> - : null} - </li>); - } -} -UnselectedItem.propTypes = { - allowAdd: PropTypes.bool, - allowAddFolder: PropTypes.bool, - item: PropTypes.shape({ - name: PropTypes.string.isRequired, - id: PropTypes.oneOfType([ - PropTypes.string, - PropTypes.number - ]).isRequired, - items: PropTypes.array, - include: PropTypes.bool, - selectable: PropTypes.bool, - type: PropTypes.string - }), - onNavDown: PropTypes.func, - onSelectItem: PropTypes.func -}; - -export default UnselectedItem; diff --git a/client/src/components/Shared/GroupMultiSelect/index.js b/client/src/components/Shared/GroupMultiSelect/index.js deleted file mode 100644 index b024d79..0000000 --- a/client/src/components/Shared/GroupMultiSelect/index.js +++ /dev/null @@ -1,92 +0,0 @@ -import { connect } from "react-redux"; -import shortId from "shortid"; - -import * as actions from "actions/GroupMultiSelect"; - -import GroupMultiSelect from "./GroupMultiSelect"; -import { - allowFilter, - flattenSelected, - getFilterText, - getPath, - getUnselectedItems -} from "selectors/GroupMultiSelect"; - -function mapStateToProps() { - const cid = shortId.generate(); - return function(state, { items, search }) { - const localState = state.groupMultiSelect[cid] || {}; - return { - cid, - filterText: getFilterText(localState), - unselectedItems: getUnselectedItems(localState, items), - path: getPath(localState), - selectedItems: flattenSelected(localState), - showFilter: search || allowFilter(localState) - }; - }; -} - -function mapDispatchToProps(dispatch, { - items, - selectedItems, - onChangePath, - onSelectItem, - onSelectAll, - onDeselectItem, - onDeselectAll -}) { - let cid; - return function() { - return { - onChangePath(path) { - if(typeof(onChangePath)==="function") { - onChangePath(path); - } - }, - onInit(_cid) { - cid = _cid; - dispatch(actions.init(cid, selectedItems)); - }, - onDestroy() { - dispatch(actions.destroy(cid)); - }, - onFilter(filterText) { - dispatch(actions.filterList(cid, filterText)); - }, - onSelectItem(item, path) { - if(typeof(onSelectItem)==="function") - onSelectItem(item, path); - dispatch(actions.selectItem(cid, item, path)); - }, - onDeselectItem(item) { - if(typeof(onDeselectItem)==="function") - onDeselectItem(item, selectedItems); - dispatch(actions.deselectItem(cid, item, items)); - }, - onSelectAll(currentItems) { - if(typeof(onSelectAll)==="function") - onSelectAll(currentItems); - dispatch(actions.selectAll(cid, currentItems)); - }, - onDeselectAll() { - if(typeof(onDeselectAll)==="function") - onDeselectAll(items); - dispatch(actions.deselectAll(cid, items)); - }, - onNav(item) { - dispatch(actions.navTo(cid, item)); - }, - onNavDown(item) { - dispatch(actions.navDown(cid, item)); - }, - onNavUp() { - dispatch(actions.navUp(cid)); - } - }; - }; -} - -const GroupMultiSelectContainer = connect(mapStateToProps, mapDispatchToProps)(GroupMultiSelect); - -export default GroupMultiSelectContainer; diff --git a/client/src/components/Shared/Header/Header.js b/client/src/components/Shared/Header/Header.js deleted file mode 100644 index 346dde6..0000000 --- a/client/src/components/Shared/Header/Header.js +++ /dev/null @@ -1,76 +0,0 @@ -import React from "react"; -import PropTypes from "prop-types"; -import { Link } from "react-router-dom"; -import { - Nav, - Navbar, - NavItem, - NavDropdown, - MenuItem -} from "react-bootstrap"; - -import Logo from "common/images/TestMan.png"; - -const links = [{ - id: "dashboard", - name: "Dashboard", - url: "/" -}, { - id: "design", - name: "Test Design", - url: "/design" -}, { - id: "execution", - name: "Execution Cycle", - url: "/execution" -}, { - id: "defects", - name: "Defects", - url: "/defects" -}/*, { - id: "docs", - name: "Documents", - url: "/docs" -}*/]; - -class Header extends React.Component { - render() { - const { navId, user } = this.props; - return (<header> - <Navbar> - <Navbar.Header> - <Navbar.Brand> - <img src={Logo} alt="TestMan Logo" className="logo" /> - <Link to="/">TestMan</Link> - </Navbar.Brand> - </Navbar.Header> - <ul className="nav navbar-nav"> - {links.map(link => (<li - role="presentation" - key={link.id} - className={`${navId==link.id ? "active" : ""}`} - > - <Link to={link.url}>{link.name}</Link> - </li>))} - </ul> - <Nav pullRight> - {user - ? <NavDropdown title={user.name} id="basic-nav-dropdown"> - <MenuItem href="/logout">Logout</MenuItem> - </NavDropdown> - : <NavItem href="/auth/google">Login</NavItem>} - </Nav> - </Navbar> - </header>); - } -} -Header.propTypes = { - navId: PropTypes.string, - user: PropTypes.shape({ - id: PropTypes.number, - name: PropTypes.string, - email: PropTypes.string - }) -}; - -export default Header; diff --git a/client/src/components/Shared/Header/index.js b/client/src/components/Shared/Header/index.js deleted file mode 100644 index 1c59c27..0000000 --- a/client/src/components/Shared/Header/index.js +++ /dev/null @@ -1,12 +0,0 @@ -import { connect } from "react-redux"; -import Header from "./Header"; - -import { getUser } from "selectors/Shared"; - -const mapStateToProps = state => ({ - user: getUser(state) -}); - -const HeaderContainer = connect(mapStateToProps)(Header); - -export default HeaderContainer; diff --git a/client/src/components/Shared/LoadingOverlay/LoadingOverlay.scss b/client/src/components/Shared/LoadingOverlay/LoadingOverlay.scss deleted file mode 100644 index e586e68..0000000 --- a/client/src/components/Shared/LoadingOverlay/LoadingOverlay.scss +++ /dev/null @@ -1,34 +0,0 @@ -.loading-overlay { - position: fixed; - bottom: 0; - left: 0; - right: 0; - z-index: 99999; - background: rgba(0, 0, 0, 0.4); - text-align: center; - opacity: 0.01; - transition: opacity 0.5s ease-in; - - &.show { - top: 0; - opacity: 1; - } - - .box { - position: absolute; - top: 50%; - left: 0; - right: 0; - margin-top: -24px; - padding: 16px; - - .fa { - font-size: 48px; - color: #e3e3e3; - } - .message { - margin-top: 10px; - color: #e3e3e3; - } - } -} diff --git a/client/src/components/Shared/LoadingOverlay/index.js b/client/src/components/Shared/LoadingOverlay/index.js deleted file mode 100644 index 79f7996..0000000 --- a/client/src/components/Shared/LoadingOverlay/index.js +++ /dev/null @@ -1,21 +0,0 @@ -import React from "react"; -import PropTypes from "prop-types"; - -import "./LoadingOverlay.scss"; - -const LoadingOverlay = ({ loading, message }) => { - return (<div className={`loading-overlay ${loading ? "show" : ""}`}> - {loading - ? <div className="box"> - <i className="glyphicon glyphicon-repeat spin" /> - <div className="message">{message || "Loading ..."}</div> - </div> - : null} - </div>); -}; -LoadingOverlay.propTypes = { - loading: PropTypes.bool.isRequired, - message: PropTypes.string -}; - -export default LoadingOverlay; diff --git a/client/src/components/Shared/Title/Title.scss b/client/src/components/Shared/Title/Title.scss deleted file mode 100644 index 1633bb3..0000000 --- a/client/src/components/Shared/Title/Title.scss +++ /dev/null @@ -1,17 +0,0 @@ -.editable-title { - margin-bottom: 10px; - input[type="text"] { - font-size: 3rem; - margin: 0; - padding: 10px; - - &.static { - border: solid 1px transparent; - cursor: pointer; - outline: none; - &:hover { - background-color: #f2f2f2; - } - } - } -} diff --git a/client/src/components/Shared/Title/index.js b/client/src/components/Shared/Title/index.js deleted file mode 100644 index 7fb8a3d..0000000 --- a/client/src/components/Shared/Title/index.js +++ /dev/null @@ -1,105 +0,0 @@ -import React from "react"; -import PropTypes from "prop-types"; - -import "./Title.scss"; - -class Title extends React.Component { - constructor(props) { - super(props); - this.state = { - value: props.value || "", - editMode: false, - mouseDownInside: false - }; - this.handleCancel = this.handleCancel.bind(this); - this.handleChange = this.handleChange.bind(this); - this.handleEdit = this.handleEdit.bind(this); - this.handleKeyUp = this.handleKeyUp.bind(this); - this.handleMouseDown = this.handleMouseDown.bind(this); - this.handleMouseUp = this.handleMouseUp.bind(this); - this.handleSave = this.handleSave.bind(this); - this.onClickOutside = this.onClickOutside.bind(this); - } - componentDidMount() { - document.addEventListener("mousedown", this.onClickOutside, false); - } - componentWillReceiveProps(nextProps) { - if(nextProps.value != this.props.value) - this.setState({ value: nextProps.value }); - } - componentWillUnmount() { - document.removeEventListener("mousedown", this.onClickOutside); - } - handleCancel(ev) { - ev.preventDefault(); - this.setState({ - value: this.props.value, - editMode: false - }); - } - handleChange(ev) { - this.setState({ value: ev.target.value }); - if(typeof(this.props.onChange)=="function") - this.props.onChange(ev.target.value); - } - handleEdit(ev) { - ev.preventDefault(); - this.setState({ editMode: true }); - } - handleKeyUp(ev) { - if(ev.keyCode == 13) { // ENTER - this.handleSave(ev); - } - if(ev.keyCode == 27) { // ESC - this.handleCancel(ev); - } - } - handleMouseDown() { - this.setState({ mouseDownInside: true }); - } - handleMouseUp() { - this.setState({ mouseDownInside: false }); - } - handleSave(ev) { - if(ev) - ev.preventDefault(); - this.setState({ editMode: false }); - this.props.onUpdate(this.state.value); - this.textField.blur(); - } - onClickOutside() { - if(this.state.mouseDownInside) - return; - if(this.state.editMode) { - this.handleSave(); - } - } - render() { - const { editMode, value } = this.state; - const { placeholder="Edit Title" } = this.props; - return (<div - className="editable-title" - onMouseDown={this.handleMouseDown} - onMouseUp={this.handleMouseUp} - > - <input - type="text" - ref={ref => this.textField = ref} - tabIndex={-1} - className={editMode ? "form-control" : "form-control static"} - placeholder={placeholder} - value={value} - onFocus={this.handleEdit} - onChange={this.handleChange} - onKeyUp={this.handleKeyUp} /> - </div>); - } -} -Title.propTypes = { - onChange: PropTypes.func, - onUpdate: PropTypes.func.isRequired, - placeholder: PropTypes.string, - value: PropTypes.string -}; - -export default Title; diff --git a/client/src/components/TestDesign/AddEditTestCase.js b/client/src/components/TestDesign/AddEditTestCase.js deleted file mode 100644 index f1977cc..0000000 --- a/client/src/components/TestDesign/AddEditTestCase.js +++ /dev/null @@ -1,255 +0,0 @@ -import Alert from "react-s-alert"; -import React from "react"; -import PropTypes from "prop-types"; -import { - Button, - ButtonToolbar, - Panel, - Table -} from "react-bootstrap"; -import Dropzone from "react-dropzone"; - -import createTempAttachment from "utils/Shared/createTempAttachment"; - -import Attachment from "components/Shared/Attachment"; -import Comment from "components/Shared/Comment"; -import Description from "components/Shared/Description"; -import Title from "components/Shared/Title"; - -import LinkedDefect from "./LinkedDefect"; - -class AddEditTestCase extends React.Component { - constructor(props) { - super(props); - this.state = { - attachments: [] - }; - this.handleAttach = this.handleAttach.bind(this); - this.handleAttachFileToComment = this.handleAttachFileToComment.bind(this); - this.handleCancel = this.handleCancel.bind(this); - this.handleDelete = this.handleDelete.bind(this); - this.handleDeleteAttachment = this.handleDeleteAttachment.bind(this); - this.handleDeleteComment = this.handleDeleteComment.bind(this); - this.handleSave = this.handleSave.bind(this); - this.handleSaveComment = this.handleSaveComment.bind(this); - this.handleUpdateComment = this.handleUpdateComment.bind(this); - } - componentDidMount() { - this.props.onInit(this.props.testId); - } - handleAttach(files) { - if(files && files.length) { - const file = files[0]; - if(!file || !file.name || !file.size) { - Alert.error("Invalid file"); - return; - } - if(this.props.isEditMode) { - this.props.onAttachFile( - file, - this.props.testCase, - this.props.testPlanId - ); - } else { - this.setState({ - attachments: [ - ...this.state.attachments, - createTempAttachment(file) - ] - }); - } - } - } - handleAttachFileToComment(file, comment) { - this.props.onAttachFileToComment( - file, - comment, - this.props.testCase.id, - this.props.testPlanId - ); - } - handleCancel() { - this.props.onCancel(); - } - handleDelete() { - if(confirm("Delete this test case?")) { - this.props.onDelete( - this.props.testCase.id, - this.props.testPlanId - ); - } - } - handleDeleteAttachment(attachment) { - if(this.props.isEditMode) { - this.props.onDeleteAttachment(attachment); - } else { - const index = this.state.attachments.findIndex(a => attachment.id==a.id); - this.setState({ - attachments: [ - ...this.state.attachments.slice(0, index), - ...this.state.attachments.slice(index + 1) - ] - }); - } - } - handleDeleteComment(commentId) { - this.props.onDeleteComment(commentId); - } - handleSave() { - this.props.onSave( - this.props.testPlanId, - this.props.testCase, - this.state.attachments.map(a => a.file), - !this.props.isEditMode - ); - } - handleSaveComment(value, attachments) { - this.props.onSaveComment( - { - content: value, - attachments - }, - this.props.testCase.id - ); - } - handleUpdateComment(comment) { - this.props.onSaveComment( - comment, - this.props.testCase.id - ); - } - render() { - const { - isEditMode, - onChangeDescription, - onChangeName, - onDeleteAttachment, - onDownloadAttachment, - onSaveAttachment, - testId, - testCase - } = this.props; - - const { - description, - comments=[] - } = testCase; - - const attachments = isEditMode - ? description.attachments - : this.state.attachments; - - return (<div className="add-edit-tc"> - <div className="action-bar header-gradient-1"> - <ButtonToolbar> - {!testId || <Button bsSize="small" bsStyle="danger" onClick={this.handleDelete}>Delete</Button>} - <Button bsSize="small" onClick={this.handleCancel}>Close</Button> - <Button bsSize="small" bsStyle="success" onClick={this.handleSave}>Save</Button> - </ButtonToolbar> - {testId ? <h3>Edit Test Case</h3> : <h3>Add Test Case</h3>} - </div> - <div className="container"> - <Panel> - <Dropzone - className="dropzone" - activeClassName="active" - multiple={false} - disableClick={true} - onDrop={this.handleAttach} - > - <Panel.Body> - <Title - value={testCase.name} - placeholder="Name" - onUpdate={onChangeName} /> - <Description - value={testCase.description.value} - placeholder="Describe this test" - onUpdate={onChangeDescription} /> - - <div className="attachments"> - {attachments.map(attachment => <Attachment - key={`attachment-${attachment.name}`} - attachment={attachment} - allowEdit={isEditMode} - allowDownload={isEditMode} - onDelete={this.handleDeleteAttachment} - onDownload={onDownloadAttachment} - onSave={onSaveAttachment} />)} - </div> - </Panel.Body> - </Dropzone> - </Panel> - - {testCase.id - ? <Panel className="defects"> - <Panel.Heading> - <Panel.Title componentClass="h3">Linked Defects</Panel.Title> - </Panel.Heading> - <Panel.Body> - {testCase.defects - ? <Table hover> - <tbody> - {testCase.defects.map(defect => - <LinkedDefect - key={`def-${defect.id}`} - defect={defect} />)} - </tbody> - </Table> - : "No linked defects"} - </Panel.Body> - </Panel> - : null} - - {testCase.id - ? <Panel> - <Panel.Heading> - <Panel.Title componentClass="h3">Comments</Panel.Title> - </Panel.Heading> - <Panel.Body> - <Comment.New onSave={this.handleSaveComment} /> - <hr /> - {comments.map(comment => <Comment - key={comment.id} - data={comment} - onAddAttachment={this.handleAttachFileToComment} - onDelete={this.handleDeleteComment} - onDownloadAttachment={onDownloadAttachment} - onRemoveAttachment={onDeleteAttachment} - onSaveAttachment={onSaveAttachment} - onUpdate={this.handleUpdateComment} />)} - </Panel.Body> - </Panel> - : null} - </div> - </div>); - } -} -AddEditTestCase.propTypes = { - defect: PropTypes.shape({ - id: PropTypes.number.isRequired, - name: PropTypes.string.isRequired - }), - isEditMode: PropTypes.bool.isRequired, - onAttachFile: PropTypes.func.isRequired, - onAttachFileToComment: PropTypes.func.isRequired, - onCancel: PropTypes.func.isRequired, - onChangeDescription: PropTypes.func.isRequired, - onChangeName: PropTypes.func.isRequired, - onDeleteAttachment: PropTypes.func.isRequired, - onDownloadAttachment: PropTypes.func.isRequired, - onDelete: PropTypes.func.isRequired, - onDeleteComment: PropTypes.func.isRequired, - onInit: PropTypes.func.isRequired, - onSave: PropTypes.func.isRequired, - onSaveAttachment: PropTypes.func.isRequired, - onSaveComment: PropTypes.func.isRequired, - testId: PropTypes.number, - testCase: PropTypes.shape({ - id: PropTypes.number, - name: PropTypes.string - }), - testPlanId: PropTypes.number -}; - -export default AddEditTestCase; diff --git a/client/src/components/TestDesign/AddEditTestCaseContainer.js b/client/src/components/TestDesign/AddEditTestCaseContainer.js deleted file mode 100644 index eb2f11d..0000000 --- a/client/src/components/TestDesign/AddEditTestCaseContainer.js +++ /dev/null @@ -1,87 +0,0 @@ -import { connect } from "react-redux"; - -import * as actions from "actions/TestDesign"; - -import AddEditTestCase from "./AddEditTestCase"; - -import { - redirectToTestDesign, - reqAttachToTestCaseComment, - reqDeleteAttachment, - reqDeleteComment, - reqDownloadAttachment, - reqSaveTestCaseComment, - reqUpdateAttachment -} from "actions/Shared"; - -import { getTestCaseAddEditState, isEditMode } from "selectors/TestDesign"; -import { isLoading } from "selectors/Shared"; - - -const mapStateToProps = state => ({ - isEditMode: isEditMode(state), - isLoading: isLoading(state), - testCase: getTestCaseAddEditState(state) -}); - -const mapDispatchToProps = dispatch => ({ - onAttachFile(file, testCase, testPlanId) { - dispatch(actions.reqAttachToTestCase(file, testCase, testPlanId)); - }, - onAttachFileToComment(file, comment, testCaseId) { - dispatch(reqAttachToTestCaseComment(file, comment, testCaseId)); - }, - onCancel() { - dispatch(actions.resetAddEdit()); - dispatch(redirectToTestDesign()); - }, - onChangeDescription(val) { - dispatch(actions.changeTCDescription(val)); - }, - onChangeName(val) { - dispatch(actions.changeTCName(val)); - }, - onDelete(testCaseId, testPlanId) { - dispatch(actions.reqDeleteTestCase(testCaseId, testPlanId)); - }, - onDeleteAttachment(attachment) { - dispatch(reqDeleteAttachment(attachment)); - }, - onDeleteComment(id) { - dispatch(reqDeleteComment(id)); - }, - onDownloadAttachment(attachment) { - dispatch(reqDownloadAttachment(attachment)); - }, - onInit(id) { - dispatch(actions.resetAddEdit()); - if(id) - dispatch(actions.reqTestCase(id)); - }, - onSave(testPlanId, testCase, files, redirect) { - dispatch(actions.reqSaveTestCase( - testPlanId, - testCase, - files, - redirect - )); - }, - onSaveAttachment(attachment) { - dispatch(reqUpdateAttachment(attachment)); - }, - onSaveComment(comment, testCaseId) { - dispatch(reqSaveTestCaseComment(comment, testCaseId)); - } -}); - -const mergeProps = (ownProps, stateProps, dispatchProps) => { - return { - ...ownProps, - ...stateProps, - ...dispatchProps - }; -}; - -const AddEditTestCaseContainer = connect(mapStateToProps, mapDispatchToProps, mergeProps)(AddEditTestCase); - -export default AddEditTestCaseContainer; diff --git a/client/src/components/TestDesign/AddEditTestPlan.js b/client/src/components/TestDesign/AddEditTestPlan.js deleted file mode 100644 index e012a75..0000000 --- a/client/src/components/TestDesign/AddEditTestPlan.js +++ /dev/null @@ -1,81 +0,0 @@ -import React from "react"; -import PropTypes from "prop-types"; - -import { - Button, - ControlLabel, - FormControl, - FormGroup, - Modal -} from "react-bootstrap"; - -class AddEditTestPlan extends React.Component { - constructor(props) { - super(props); - this.state = { - name: "" - }; - this.handleChangeName = this.handleChangeName.bind(this); - this.handleClose = this.handleClose.bind(this); - this.handleSave = this.handleSave.bind(this); - } - componentWillReceiveProps(nextProps) { - if(nextProps.show && !this.props.show) { - this.setState({ - id: nextProps.testPlan && nextProps.testPlan.id, - name: nextProps.testPlan && nextProps.testPlan.name || "" - }); - } - } - handleChangeName(ev) { - this.setState({ - name: ev.target.value - }); - } - handleClose() { - this.props.onClose(); - } - handleSave() { - this.props.onSave({ - id: this.state.id, - name: this.state.name - }); - } - render() { - const { testPlan } = this.props; - const { name } = this.state; - - return (<Modal show={this.props.show} onHide={this.handleClose}> - <Modal.Header closeButton> - <Modal.Title> - {testPlan && testPlan.id ? "Edit " : "Add "} - Test Plan - </Modal.Title> - </Modal.Header> - <Modal.Body> - <FormGroup controlId="name"> - <ControlLabel>Name</ControlLabel> - <FormControl - value={name} - onChange={this.handleChangeName} - type="text" /> - </FormGroup> - </Modal.Body> - <Modal.Footer> - <Button onClick={this.handleClose}>Cancel</Button> - <Button bsStyle="success" onClick={this.handleSave}>Save</Button> - </Modal.Footer> - </Modal>); - } -} -AddEditTestPlan.propTypes = { - show: PropTypes.bool, - testPlan: PropTypes.shape({ - id: PropTypes.number, - name: PropTypes.string - }), - onClose: PropTypes.func.isRequired, - onSave: PropTypes.func.isRequired -}; - -export default AddEditTestPlan; diff --git a/client/src/components/TestDesign/AddEditView.js b/client/src/components/TestDesign/AddEditView.js deleted file mode 100644 index 569b4fe..0000000 --- a/client/src/components/TestDesign/AddEditView.js +++ /dev/null @@ -1,20 +0,0 @@ -import React from "react"; -import PropTypes from "prop-types"; - -import AddEditTestCase from "./AddEditTestCaseContainer"; - - -class AddEditView extends React.Component { - render() { - const { testId, testPlanId } = this.props; - return (<div className="test-design add-edit"> - <AddEditTestCase testPlanId={testPlanId} testId={testId} /> - </div>); - } -} -AddEditView.propTypes = { - testId: PropTypes.number, - testPlanId: PropTypes.number -}; - -export default AddEditView; diff --git a/client/src/components/TestDesign/LinkedDefect.js b/client/src/components/TestDesign/LinkedDefect.js deleted file mode 100644 index f8a88ab..0000000 --- a/client/src/components/TestDesign/LinkedDefect.js +++ /dev/null @@ -1,54 +0,0 @@ -import React from "react"; -import { Link } from "react-router-dom"; -import PropTypes from "prop-types"; -import { Button } from "react-bootstrap"; - - -const LinkedDefect = props => { - const { - allowDelete=false, - defect, - onDelete - } = props; - - const { - id, - name - } = defect; - - const handleDelete = () => typeof(onDelete)=="function" - ? onDelete(defect) - : {}; - - return (<tr className="linked-defect"> - <td className="defect-id"> - <Link - to={`/defects/edit/${id}`} - target="_blank" - className="text-danger" - > - DF-{id} - {" "} - <i className="glyphicon glyphicon-share text-danger" /> - </Link> - </td> - <td className="defect-name">{name}</td> - {allowDelete - ? <td className="del-defect"> - <Button bsStyle="link" className="btn-delete-defect" onClick={handleDelete}> - <i className="glyphicon glyphicon-trash text-danger" /> - </Button> - </td> - : null} - </tr>); -}; -LinkedDefect.propTypes = { - allowDelete: PropTypes.bool, - defect: PropTypes.shape({ - id: PropTypes.number.isRequired, - name: PropTypes.string.isRequired - }), - onDelete: PropTypes.func.isRequired -}; - -export default LinkedDefect; diff --git a/client/src/components/TestDesign/ListView.js b/client/src/components/TestDesign/ListView.js deleted file mode 100644 index f27abe8..0000000 --- a/client/src/components/TestDesign/ListView.js +++ /dev/null @@ -1,23 +0,0 @@ -import React from "react"; -import PropTypes from "prop-types"; - -import TestPlans from "./TestPlansContainer"; -import TestCaseList from "./TestCaseListContainer"; - - -class ListView extends React.Component { - render() { - const { testPlanId } = this.props; - return (<div className="test-design list"> - <TestPlans testPlanId={testPlanId} /> - <div className="test-cases"> - <TestCaseList testPlanId={testPlanId} /> - </div> - </div>); - } -} -ListView.propTypes = { - testPlanId: PropTypes.number -}; - -export default ListView; diff --git a/client/src/components/TestDesign/TestCaseList.js b/client/src/components/TestDesign/TestCaseList.js deleted file mode 100644 index f8034da..0000000 --- a/client/src/components/TestDesign/TestCaseList.js +++ /dev/null @@ -1,100 +0,0 @@ -import React from "react"; -import { Link } from "react-router-dom"; -import PropTypes from "prop-types"; -import { - Table -} from "react-bootstrap"; - -import TestCasesToolbar from "./TestCasesToolbar"; -import UploadTestCases from "./UploadTestCases"; - -class TestCaseList extends React.Component { - constructor(props) { - super(props); - this.state = { - showUploadTestCases: false - }; - this.handleUploadTestCases = this.handleUploadTestCases.bind(this); - this.hideUploadTestCases = this.hideUploadTestCases.bind(this); - this.showUploadTestCases = this.showUploadTestCases.bind(this); - } - componentDidMount() { - if(this.props.testPlan && this.props.testPlanId == this.props.testPlan.id) - this.props.fetchTestCases(this.props.testPlan); - } - componentWillReceiveProps(nextProps) { - if((nextProps.testPlan != this.props.testPlan || - (nextProps.testPlan && this.props.testPlan && nextProps.testPlan.id != this.props.testPlan.id)) - ) { - this.props.fetchTestCases(nextProps.testPlan); - } - } - handleUploadTestCases(file) { - this.props.onUploadTests(this.props.testPlan.id, file); - this.hideUploadTestCases(); - } - hideUploadTestCases() { - this.setState({ showUploadTestCases: false }); - } - showUploadTestCases() { - this.setState({ showUploadTestCases: true }); - } - render() { - const { - testCases, - testPlan - } = this.props; - const { showUploadTestCases } = this.state; - return (<div className="tests-list"> - <TestCasesToolbar testPlan={testPlan} onUpload={this.showUploadTestCases} /> - <Table striped condensed hover className="data-grid "> - <thead> - <tr> - <th className="test-id">ID</th> - <th>Test Case</th> - <th className="stats"></th> - </tr> - </thead> - <tbody> - {testCases.map(tc => (<tr key={`tc-${tc.id}`}> - <td>TC-{tc.id}</td> - <td> - <Link to={`/design/testplan/${testPlan.id}/testcase/edit/${tc.id}`}>{tc.name}</Link> - </td> - <td> - <div className={`test-stat ${!tc.defects.length ? "invisible" : ""}`} title="Defects"> - <i className="glyphicon glyphicon-exclamation-sign text-danger" /> - {" "} - {tc.defects.length} - </div> - <div className={`test-stat ${!tc.comments.length ? "invisible": ""}`} title="Comments"> - <i className="glyphicon glyphicon-comment text-info" /> - {" "} - {tc.comments.length} - </div> - </td> - </tr>))} - </tbody> - </Table> - <UploadTestCases - show={showUploadTestCases} - onCancel={this.hideUploadTestCases} - onUpload={this.handleUploadTestCases} /> - </div>); - } -} -TestCaseList.propTypes = { - testPlan: PropTypes.shape({ - id: PropTypes.number.isRequired, - name: PropTypes.string.isRequired - }), - testPlanId: PropTypes.number, - fetchTestCases: PropTypes.func.isRequired, - onUploadTests: PropTypes.func.isRequired, - testCases: PropTypes.arrayOf(PropTypes.shape({ - id: PropTypes.number.isRequired, - name: PropTypes.string.isRequired - })) -}; - -export default TestCaseList; diff --git a/client/src/components/TestDesign/TestCaseListContainer.js b/client/src/components/TestDesign/TestCaseListContainer.js deleted file mode 100644 index a93efa4..0000000 --- a/client/src/components/TestDesign/TestCaseListContainer.js +++ /dev/null @@ -1,30 +0,0 @@ -import { connect } from "react-redux"; -import TestCaseList from "./TestCaseList"; - -import * as actions from "actions/TestDesign"; - -import { - getTestCases, - getSelectedTestPlan -} from "selectors/TestDesign"; -import { isLoading } from "selectors/Shared"; - -const mapStateToProps = state => ({ - isLoading: isLoading(state), - testPlan: getSelectedTestPlan(state), - testCases: getTestCases(state) -}); - -const mapDispatchToProps = dispatch => ({ - fetchTestCases(testPlan) { - if(testPlan) - dispatch(actions.reqTestCases(testPlan.id)); - }, - onUploadTests(testPlanId, file) { - dispatch(actions.reqUploadTests(testPlanId, file)); - } -}); - -const TestCaseListContainer = connect(mapStateToProps, mapDispatchToProps)(TestCaseList); - -export default TestCaseListContainer; diff --git a/client/src/components/TestDesign/TestCasesToolbar.js b/client/src/components/TestDesign/TestCasesToolbar.js deleted file mode 100644 index 5911600..0000000 --- a/client/src/components/TestDesign/TestCasesToolbar.js +++ /dev/null @@ -1,38 +0,0 @@ -import React from "react"; -import PropTypes from "prop-types"; -import { Link } from "react-router-dom"; - -class TestCasesToolbar extends React.Component { - render() { - const { onUpload, testPlan } = this.props; - return (<div className="toolbar"> - {testPlan - ? <React.Fragment> - <Link to={`/design/testplan/${testPlan.id}/testcase/add`} className="btn btn-link"> - <i className="glyphicon glyphicon-plus text-info" /> - {" "} - <span className="text-info">New</span> - </Link> - <button className="btn btn-link" onClick={onUpload}> - <i className="glyphicon glyphicon-open text-info" /> - {" "} - <span className="text-info">Upload</span> - </button> - </React.Fragment> - : null} - <h3 className="header-gradient-1"> - Test Cases - {testPlan ? " - " + testPlan.name : ""} - </h3> - </div>); - } -} -TestCasesToolbar.propTypes = { - onUpload: PropTypes.func.isRequired, - testPlan: PropTypes.shape({ - id: PropTypes.number.isRequired, - name: PropTypes.string.isRequired - }) -}; - -export default TestCasesToolbar; diff --git a/client/src/components/TestDesign/TestDesign.scss b/client/src/components/TestDesign/TestDesign.scss deleted file mode 100644 index 0fef16e..0000000 --- a/client/src/components/TestDesign/TestDesign.scss +++ /dev/null @@ -1,56 +0,0 @@ -.test-design { - display: grid; - grid-auto-rows: 100%; - &.list { - grid-template-columns: 280px auto; - } - - .attachments { - .attachment { - display: inline-block; - margin: 0 10px 10px 0; - } - } -} -.add-edit-tc { - position: relative; - margin: 64px 24px; - - .action-bar { - position: absolute; - left: -24px; - right: -24px; - top: -64px; - .btn-toolbar { - float: right; - margin: 6px 24px 0 0; - } - } - - .panel.defects { - .panel-body { - padding: 0; - } - .table { - td:first-child { - padding-left: 15px; - width: 80px; - } - } - } -} -.tests-list { - background: #fff; - .test-id, - .stats { - width: 100px; - } - .test-stat { - display: inline-block; - margin: 0 4px; - - &.invisible { - visibility: hidden; - } - } -} diff --git a/client/src/components/TestDesign/TestPlans.js b/client/src/components/TestDesign/TestPlans.js deleted file mode 100644 index e7e74c3..0000000 --- a/client/src/components/TestDesign/TestPlans.js +++ /dev/null @@ -1,95 +0,0 @@ -import React from "react"; -import PropTypes from "prop-types"; -import { Link } from "react-router-dom"; - -import EditTestPlan from "./AddEditTestPlan"; -import TestPlansToolbar from "./TestPlansToolbar"; - -import "./TestPlans.scss"; - -class TestPlans extends React.Component { - constructor(props) { - super(props); - this.state = { - editDialog: false - }; - this.handleSave = this.handleSave.bind(this); - this.hideEditDialog = this.hideEditDialog.bind(this); - this.showEditDialog = this.showEditDialog.bind(this); - } - componentDidMount() { - if(!this.props.testPlanId && this.props.selected && this.props.selected.id) { - this.props.onSelect(this.props.selected.id, true); - } else { - this.props.reqTestPlans(); - } - } - componentWillReceiveProps(nextProps) { - const redirect = Boolean(!this.props.testPlanId && - nextProps.selected && - nextProps.selected.id); - - const newId = nextProps.testPlanId || - nextProps.selected && nextProps.selected.id; - - if(newId) - this.props.onSelect(newId, redirect); - } - handleSave(data) { - this.props.onSave(data); - this.hideEditDialog(); - } - hideEditDialog() { - this.setState({ editDialog: false, testPlan: {} }); - } - showEditDialog(testPlan) { - this.setState({ - testPlan, - editDialog: true - }); - } - render() { - const { testPlans } = this.props; - const { editDialog, testPlan } = this.state; - return (<div className="test-plans"> - <TestPlansToolbar onSave={this.props.onSave} /> - <ul> - {testPlans.map(tp => (<li - className={`${tp.selected ? "bg-success selected" : ""}`} - key={tp.id} - > - <Link to={`/design/testplan/${tp.id}`}> - {tp.name} - <button - className="btn btn-link btn-edit" - onClick={() => this.showEditDialog(tp)} - > - <i className="glyphicon glyphicon-pencil text-info" /> - </button> - </Link> - </li>))} - </ul> - <EditTestPlan - show={editDialog} - testPlan={testPlan} - onClose={this.hideEditDialog} - onSave={this.handleSave} /> - </div>); - } -} -TestPlans.propTypes = { - reqTestPlans: PropTypes.func.isRequired, - onSave: PropTypes.func.isRequired, - onSelect: PropTypes.func.isRequired, - selected: PropTypes.shape({ - id: PropTypes.number, - name: PropTypes.string - }), - testPlanId: PropTypes.number, - testPlans: PropTypes.arrayOf(PropTypes.shape({ - id: PropTypes.number.isRequired, - name: PropTypes.string.isRequired - })) -}; - -export default TestPlans; diff --git a/client/src/components/TestDesign/TestPlans.scss b/client/src/components/TestDesign/TestPlans.scss deleted file mode 100644 index bd5c3ee..0000000 --- a/client/src/components/TestDesign/TestPlans.scss +++ /dev/null @@ -1,38 +0,0 @@ -.test-plans { - border-right: solid 2px #ccc; - ul { - list-style-type: none; - margin: 0; - padding: 0; - font-size: 1.2rem; - - li { - color: #999; - cursor: pointer; - margin: 0; - padding: 0; - - &.selected a { - color: #404041; - font-weight: bold; - } - - a { - display: block; - padding: 8px 16px; - text-decoration: none; - &:hover { - color: #404041; - } - } - - .btn { - box-shadow: none; - float: right; - margin-left: 8px; - outline: none; - padding: 2px; - } - } - } -} diff --git a/client/src/components/TestDesign/TestPlansContainer.js b/client/src/components/TestDesign/TestPlansContainer.js deleted file mode 100644 index d10675f..0000000 --- a/client/src/components/TestDesign/TestPlansContainer.js +++ /dev/null @@ -1,40 +0,0 @@ -import { connect } from "react-redux"; -import TestPlans from "./TestPlans"; - -import * as actions from "actions/TestDesign"; - -import { redirectToTestDesign } from "actions/Shared"; - -import { getSelectedTestPlan, getTestPlans } from "selectors/TestDesign"; -import { isLoading } from "selectors/Shared"; - - -const mapStateToProps = state => ({ - selected: getSelectedTestPlan(state), - isLoading: isLoading(state), - testPlans: getTestPlans(state) -}); - -const mapDispatchToProps = dispatch => ({ - reqTestPlans() { - dispatch(actions.reqTestPlans()); - }, - onSave(testPlan) { - dispatch(actions.reqSaveTestPlan(testPlan)); - }, - onSelect(testPlanId, redirect) { - dispatch(actions.selectTestPlan({ id: testPlanId }, redirect)); - if(redirect) - dispatch(redirectToTestDesign(testPlanId)); - } -}); - -const mergeProps = (stateProps, dispatchProps, ownProps) => ({ - ...stateProps, - ...dispatchProps, - ...ownProps -}); - -const TestPlansContainer = connect(mapStateToProps, mapDispatchToProps, mergeProps)(TestPlans); - -export default TestPlansContainer; diff --git a/client/src/components/TestDesign/TestPlansToolbar.js b/client/src/components/TestDesign/TestPlansToolbar.js deleted file mode 100644 index 5dcff8e..0000000 --- a/client/src/components/TestDesign/TestPlansToolbar.js +++ /dev/null @@ -1,45 +0,0 @@ -import React from "react"; -import PropTypes from "prop-types"; - -import AddEditTestPlan from "./AddEditTestPlan"; - - -class TestPlansToolbar extends React.Component { - constructor(props) { - super(props); - this.state = { show: false }; - this.handleSave = this.handleSave.bind(this); - this.hideDialog = this.hideDialog.bind(this); - this.showDialog = this.showDialog.bind(this); - } - handleSave(data) { - this.props.onSave(data); - this.hideDialog(); - } - hideDialog() { - this.setState({ show: false }); - } - showDialog() { - this.setState({ show: true }); - } - render() { - const { show } = this.state; - return (<div className="toolbar"> - <button className="btn btn-link" onClick={this.showDialog}> - <i className="glyphicon glyphicon-plus text-info" /> - {" "} - <span className="text-info">New</span> - </button> - <h3 className="header-gradient-1">Test Plans</h3> - <AddEditTestPlan - show={show} - onClose={this.hideDialog} - onSave={this.handleSave} /> - </div>); - } -} -TestPlansToolbar.propTypes = { - onSave: PropTypes.func.isRequired -}; - -export default TestPlansToolbar; diff --git a/client/src/components/TestDesign/UploadTestCases.js b/client/src/components/TestDesign/UploadTestCases.js deleted file mode 100644 index 86095a0..0000000 --- a/client/src/components/TestDesign/UploadTestCases.js +++ /dev/null @@ -1,53 +0,0 @@ -import Alert from "react-s-alert"; -import React from "react"; -import PropTypes from "prop-types"; -import Dropzone from "react-dropzone"; -import { Button, Modal } from "react-bootstrap"; - - -const UploadTestCases = props => { - const { - onUpload, - onCancel, - show - } = props; - - const handleDrop = files => { - if(files && files.length) { - const file = files[0]; - if(!file || !file.name || !file.size || !file.name.match(/\.csv$/)) { - Alert.error("Invalid file"); - return; - } - props.onUpload(files[0]); - } - }; - - return (<Modal show={show} className="upload-test-cases-modal"> - <Modal.Header> - <Modal.Title>Upload Test Cases</Modal.Title> - </Modal.Header> - <Modal.Body> - <Dropzone - className="dropzone" - multiple={false} - onDrop={handleDrop} - > - <div> - {"Drop CSV here or click to browse"} - </div> - </Dropzone> - </Modal.Body> - <Modal.Footer> - <Button onClick={onCancel}>Cancel</Button> - <Button bsStyle="success" onClick={onUpload}>Upload</Button> - </Modal.Footer> - </Modal>); -}; -UploadTestCases.propTypes = { - onCancel: PropTypes.func.isRequired, - onUpload: PropTypes.func.isRequired, - show: PropTypes.bool.isRequired -}; - -export default UploadTestCases; diff --git a/client/src/components/TestDesign/index.js b/client/src/components/TestDesign/index.js deleted file mode 100644 index aea6428..0000000 --- a/client/src/components/TestDesign/index.js +++ /dev/null @@ -1,23 +0,0 @@ -import React from "react"; -import PropTypes from "prop-types"; - -import ListView from "./ListView"; -import AddEditView from "./AddEditView"; - -import "./TestDesign.scss"; - -class TestDesign extends React.Component { - render() { - const { mode, testId, testPlanId } = this.props; - return mode=="list" - ? <ListView testPlanId={testPlanId} /> - : <AddEditView testPlanId={testPlanId} testId={testId} />; - } -} -TestDesign.propTypes = { - mode: PropTypes.string, - testId: PropTypes.number, - testPlanId: PropTypes.number -}; - -export default TestDesign; diff --git a/client/src/components/TestSelector/Selector.js b/client/src/components/TestSelector/Selector.js deleted file mode 100644 index 32b098e..0000000 --- a/client/src/components/TestSelector/Selector.js +++ /dev/null @@ -1,116 +0,0 @@ -import React from "react"; -import PropTypes from "prop-types"; - -import { Button, Modal } from "react-bootstrap"; - -import GroupMultiSelect from "components/Shared/GroupMultiSelect"; - -import "./TestCaseSelector.scss"; - -class Selector extends React.Component { - constructor(props) { - super(props); - this.state = { - preSelectedItems: [] - }; - this.handleClose = this.handleClose.bind(this); - this.handleSave = this.handleSave.bind(this); - this.handleEnter = this.handleEnter.bind(this); - } - componentWillMount() { - this.setState({ - preSelectedItems: this.props.selectedItems - }); - } - handleClose() { - this.props.onClose(this.state.preSelectedItems); - } - handleEnter() { - this.props.onInit(); - } - handleSave() { - this.props.onSave( - this.props.selectedItems, - this.props.importActionContract - ); - } - render() { - const { - allowAdd, - allowAddFolder, - items, - onChangePath, - onDeselect, - onDeselectAll, - onSelect, - onSelectAll, - readOnly, - selectedItems, - show - } = this.props; - - return (<Modal - show={show} - onEnter={this.handleEnter} - className="test-case-selector-modal" - onHide={this.handleClose} - > - <Modal.Header closeButton> - <Modal.Title>Import Test Cases</Modal.Title> - </Modal.Header> - <Modal.Body> - <div className="test-case-selector"> - <GroupMultiSelect - items={items} - allowAdd={allowAdd} - allowAddFolder={allowAddFolder} - onChangePath={onChangePath} - onDeselectAll={onDeselectAll} - onDeselectItem={onDeselect} - onSelectItem={onSelect} - onSelectAll={onSelectAll} - selectedItems={selectedItems} - readOnly={readOnly} /> - </div> - </Modal.Body> - <Modal.Footer> - <Button onClick={this.handleClose}>Cancel</Button> - <Button bsStyle="success" onClick={this.handleSave} disabled={!selectedItems.length}>Import</Button> - </Modal.Footer> - </Modal>); - } -} -Selector.propTypes = { - allowAdd: PropTypes.bool, - allowAddFolder: PropTypes.bool, - importActionContract: PropTypes.shape({ - type: PropTypes.string.isRequired, - key: PropTypes.string.isRequired, - extra: PropTypes.shape({ - execCycle: PropTypes.shape({ - id: PropTypes.number, - name: PropTypes.string - }) - }).isRequired - }), - items: PropTypes.arrayOf(PropTypes.shape({ - id: PropTypes.number.isRequired, - name: PropTypes.string.isRequired - })), - onChangePath: PropTypes.func.isRequired, - onClose: PropTypes.func.isRequired, - onDeselect: PropTypes.func.isRequired, - onInit: PropTypes.func.isRequired, - onDeselectAll: PropTypes.func.isRequired, - onSave: PropTypes.func.isRequired, - onSelect: PropTypes.func.isRequired, - onSelectAll: PropTypes.func.isRequired, - readOnly: PropTypes.bool, - selectedItems: PropTypes.arrayOf(PropTypes.shape({ - id: PropTypes.number.isRequired, - name: PropTypes.string.isRequired - })), - show: PropTypes.bool -}; - -export default Selector; diff --git a/client/src/components/TestSelector/TestCaseSelector.scss b/client/src/components/TestSelector/TestCaseSelector.scss deleted file mode 100644 index 9656571..0000000 --- a/client/src/components/TestSelector/TestCaseSelector.scss +++ /dev/null @@ -1,15 +0,0 @@ -.test-case-selector-modal { - .modal-content { - border: none; - display: grid; - grid-template-rows: 56px minmax(200px, 60vh) 76px - } - .modal-body { - background: none; - padding: 0; - } -} -.test-case-selector { - font-size: 1.1rem; - height: 100%; -} diff --git a/client/src/components/TestSelector/index.js b/client/src/components/TestSelector/index.js deleted file mode 100644 index 00e264c..0000000 --- a/client/src/components/TestSelector/index.js +++ /dev/null @@ -1,74 +0,0 @@ -import { connect } from "react-redux"; -import Selector from "./Selector"; - -import * as actions from "actions/TestSelector"; - -import { - getAllItems, - getPath, - getSelected -} from "selectors/TestSelector"; - -import { isLoading } from "selectors/Shared"; - - -const mapStateToProps = state => ({ - isLoading: isLoading(state), - items: getAllItems(state), - path: getPath(state), - selectedItems: getSelected(state) -}); - -const mapDispatchToProps = (dispatch, ownProps) => ({ - onChangePath(path) { - dispatch(actions.changePath(path)); - dispatch(actions.reqItems(undefined, path)); - }, - onClose(preSelectedItems) { - dispatch(actions.resetSelection(preSelectedItems)); - if(typeof(ownProps.onClose)==="function") - ownProps.onClose(); - }, - onDeselect(item) { - dispatch(actions.deselect(item)); - }, - onDeselectAll() { - dispatch(actions.deselectAll()); - }, - onInit() { - dispatch(actions.reqItems()); - if(typeof(ownProps.onInit)=="function") - ownProps.onInit(); - }, - onSelect(item, path) { - dispatch(actions.select(item, path)); - }, - onSelectAll(items) { - dispatch(actions.selectAll(items)); - }, - onSave(selectedItems, importActionContract) { - dispatch(actions.reqImportTests(selectedItems, importActionContract)); - if(typeof(ownProps.onSave)=="function") - ownProps.onSave(selectedItems); - } -}); - -const mergeProps = (stateProps, dispatchProps, ownProps) => ({ - ...stateProps, - ...dispatchProps, - show: Boolean(ownProps.show), - importActionContract: ownProps.importActionContract, - allowAdd: ownProps.allowAdd !== undefined - ? ownProps.allowAdd - : true, - allowAddFolder: ownProps.allowAddFolder !== undefined - ? ownProps.allowAddFolder - : true, - readOnly: ownProps.readOnly !== undefined - ? ownProps.readOnly - : false -}); - -const SelectorContainer = connect(mapStateToProps, mapDispatchToProps, mergeProps)(Selector); - -export default SelectorContainer; diff --git a/client/src/constants/DashboardActions.js b/client/src/constants/DashboardActions.js deleted file mode 100644 index ed9e7aa..0000000 --- a/client/src/constants/DashboardActions.js +++ /dev/null @@ -1,2 +0,0 @@ -export const REQ_SUMMARY = "REQ_SUMMARY"; -export const RCV_SUMMARY = "RCV_SUMMARY"; diff --git a/client/src/constants/DefectSelectorActions.js b/client/src/constants/DefectSelectorActions.js deleted file mode 100644 index 3f767b7..0000000 --- a/client/src/constants/DefectSelectorActions.js +++ /dev/null @@ -1,10 +0,0 @@ -const NAMESPACE = "DEF_SELECTOR_"; - -export const REQ_ITEMS = NAMESPACE + "REQ_ITEMS"; -export const RCV_ITEMS = NAMESPACE + "RCV_ITEMS"; -export const CHANGE_PATH = NAMESPACE + "CHANGE_PATH"; -export const SELECT = NAMESPACE + "SELECT"; -export const SELECT_ALL = NAMESPACE + "SELECT_ALL"; -export const DESELECT = NAMESPACE + "DESELECT"; -export const DESELECT_ALL = NAMESPACE + "DESELECT_ALL"; -export const RESET_SELECT = NAMESPACE + "RESET_SELECT"; diff --git a/client/src/constants/DefectStateColors.js b/client/src/constants/DefectStateColors.js deleted file mode 100644 index 98940a5..0000000 --- a/client/src/constants/DefectStateColors.js +++ /dev/null @@ -1,8 +0,0 @@ -import STATES from "common/constants/DefectStates"; - -export default { - [STATES[0]]: "danger", - [STATES[1]]: "info", - [STATES[2]]: "success", - [STATES[3]]: "default" -}; diff --git a/client/src/constants/DefectsActions.js b/client/src/constants/DefectsActions.js deleted file mode 100644 index 7940b9c..0000000 --- a/client/src/constants/DefectsActions.js +++ /dev/null @@ -1,23 +0,0 @@ -export const REQ_DEFECTS = "REQ_DEFECTS"; -export const RCV_DEFECTS = "RCV_DEFECTS"; -export const REQ_DEFECT = "REQ_DEFECT"; -export const RCV_DEFECT = "RCV_DEFECT"; -export const RESET_DF_ADD_EDIT = "RESET_DF_ADD_EDIT"; -export const CHANGE_DF_ASSIGNEE = "CHANGE_DF_ASSIGNEE"; -export const CHANGE_DF_NAME = "CHANGE_DF_NAME"; -export const CHANGE_DF_DESCR = "CHANGE_DF_DESCR"; -export const CHANGE_DF_STATUS = "CHANGE_DF_STATUS"; -export const REQ_SAVE_DEFECT = "REQ_SAVE_DEFECT"; -export const RCV_SAVE_DEFECT = "RCV_SAVE_DEFECT"; -export const TOGGLE_SELECT_DEF = "TOGGLE_SELECT_DEF"; -export const TOGGLE_SELECT_DEF_ALL = "TOGGLE_SELECT_DEF_ALL"; -export const REQ_DELETE_DEFECT = "REQ_DELETE_DEFECT"; -export const RCV_DELETE_DEFECT = "RCV_DELETE_DEFECT"; -export const REQ_DELETE_DEFECTS = "REQ_DELETE_DEFECTS"; -export const RCV_DELETE_DEFECTS = "RCV_DELETE_DEFECTS"; -export const ADD_TESTS_TO_DEFECT = "ADD_TESTS_TO_DEFECT"; -export const DEL_TEST_FRM_DEFECT = "DEL_TEST_FRM_DEFECT"; -export const REQ_ATTACH_TO_DEFECT = "REQ_ATTACH_TO_DEFECT"; -export const RCV_ATTACH_TO_DEFECT = "RCV_ATTACH_TO_DEFECT"; -export const CHANGE_STATUS_FILTER = "CHANGE_DEF_STATUS_FILTER"; -export const CHANGE_ASSIGNEE_FILTER = "CHANGE_ASSIGNEE_FILTER"; diff --git a/client/src/constants/ExecCyclesActions.js b/client/src/constants/ExecCyclesActions.js deleted file mode 100644 index 56e73fe..0000000 --- a/client/src/constants/ExecCyclesActions.js +++ /dev/null @@ -1,38 +0,0 @@ -export const REQ_EXEC_CYCLES = "REQ_EXEC_CYCLES"; -export const RCV_EXEC_CYCLES = "RCV_EXEC_CYCLES"; -export const REQ_EXEC_CYCLE = "REQ_EXEC_CYCLE"; -export const RCV_EXEC_CYCLE = "RCV_EXEC_CYCLE"; -export const REQ_EC_SAVE = "REQ_EC_SAVE"; -export const RCV_EC_SAVE = "RCV_EC_SAVE"; -export const SELECT_EXEC_CYCLE = "SELECT_EXEC_CYCLE"; -export const RESET_EC_ADD_EDIT = "RESET_EC_ADD_EDIT"; -export const REQ_TEST_RUNS = "REQ_TEST_RUNS"; -export const RCV_TEST_RUNS = "RCV_TEST_RUNS"; -export const REQ_SAVE_TR = "REQ_SAVE_TR"; -export const RCV_SAVE_TR = "RCV_SAVE_TR"; -export const TOGGLE_IMPORT_DLG = "TOGGLE_IMPORT_DLG"; -export const REQ_IMPORT_TESTS = "REQ_IMPORT_TESTS"; -export const RCV_IMPORT_TESTS = "RCV_IMPORT_TESTS"; -export const TOGGLE_SELECT_TR = "TOGGLE_SELECT_TR"; -export const TOGGLE_SELECT_TR_ALL = "TOGGLE_SELECT_TR_ALL"; -export const REQ_DEL_TEST_RUNS = "REQ_DEL_TEST_RUNS"; -export const RCV_DEL_TEST_RUNS = "RCV_DEL_TEST_RUNS"; -export const INIT_EC_EDIT = "INIT_EC_EDIT" -export const REQ_DEL_EC = "REQ_DEL_EC"; -export const RCV_DEL_EC = "RCV_DEL_EC"; -export const REQ_START_EC = "REQ_START_EC"; -export const RCV_START_EC = "RCV_START_EC"; -export const REQ_END_EC = "REQ_END_EC"; -export const RCV_END_EC = "RCV_END_EC"; -export const REQ_TEST_RUN = "REQ_TEST_RUN"; -export const RCV_TEST_RUN = "RCV_TEST_RUN"; -export const RESET_TR_ADD_EDIT = "RESET_TR_ADD_EDIT"; -export const REQ_ADD_NEW_DEFECT = "REQ_ADD_NEW_DEFECT"; -export const RCV_ADD_NEW_DEFECT = "RCV_ADD_NEW_DEFECT"; -export const REQ_LINK_DEFECTS = "REQ_LINK_DEFECTS"; -export const RCV_LINK_DEFECTS = "RCV_LINK_DEFECTS"; -export const REQ_UNLINK_DEFECT = "REQ_UNLINK_DEFECT"; -export const RCV_UNLINK_DEFECT = "RCV_UNLINK_DEFECT"; -export const REQ_CLONE_EC = "REQ_CLONE_EC"; -export const RCV_CLONE_EC = "RCV_CLONE_EC"; -export const CHANGE_STATUS_FILTER = "CHANGE_EC_STATUS_FILTER"; diff --git a/client/src/constants/GroupMultiSelectActions.js b/client/src/constants/GroupMultiSelectActions.js deleted file mode 100644 index d0f83a1..0000000 --- a/client/src/constants/GroupMultiSelectActions.js +++ /dev/null @@ -1,12 +0,0 @@ -const NAMESPACE = "GROUP_MULTI_SELECT."; - -export const DESTROY = NAMESPACE + "DESTROY"; -export const DESELECT_ALL = NAMESPACE + "DESELECT_ALL"; -export const DESELECT_ITEM = NAMESPACE + "DESELECT_ITEM"; -export const FILTER_LIST = NAMESPACE + "FILTER_LIST"; -export const INIT = NAMESPACE + "INITIALIZE"; -export const NAV_DOWN = NAMESPACE + "NAV_DOWN"; -export const NAV_TO = NAMESPACE + "NAV_TO"; -export const NAV_UP = NAMESPACE + "NAV_UP"; -export const SELECT_ITEM = NAMESPACE + "SELECT_ITEM"; -export const SELECT_ALL = NAMESPACE + "SELECT_ALL"; diff --git a/client/src/constants/SharedActions.js b/client/src/constants/SharedActions.js deleted file mode 100644 index c37480f..0000000 --- a/client/src/constants/SharedActions.js +++ /dev/null @@ -1,24 +0,0 @@ -export const IS_LOADING = "IS_LOADING"; -export const SETUP_SESSION_INV = "SETUP_SESSION_INV"; -export const SESSION_INVALIDATE = "SESSION_INVALIDATE"; -export const REQ_LOGIN_STATUS = "REQ_LOGIN_STATUS"; -export const RCV_LOGIN_STATUS = "RCV_LOGIN_STATUS"; -export const REDIRECT_TEST_DESIGN = "REDIRECT_TEST_DESIGN"; -export const REDIRECT_DEFECTS = "REDIRECT_DEFECTS"; -export const REDIRECT_EXEC_CYCLE = "REDIRECT_EXEC_CYCLE"; -export const REDIRECT_LOGIN = "REDIRECT_LOGIN"; -export const REQ_USERS = "REQ_USERS"; -export const RCV_USERS = "RCV_USERS"; -export const REQ_UPLOAD_FILES = "REQ_UPLOAD_FILES"; -export const RCV_UPLOAD_FILES = "RCV_UPLOAD_FILES"; -export const REQ_DOWNLOAD_ATTACH = "REQ_DOWNLOAD_ATTACH"; -export const REQ_DELETE_ATTACH = "REQ_DELETE_ATTACH"; -export const RCV_DELETE_ATTACH = "RCV_DELETE_ATTACH"; -export const REQ_UPDATE_ATTACH = "REQ_UPDATE_ATTACH"; -export const RCV_UPDATE_ATTACH = "RCV_UPDATE_ATTACH"; -export const REQ_ATTACH_TO_COMM = "REQ_ATTACH_TO_COMM"; -export const RCV_ATTACH_TO_COMM = "RCV_ATTACH_TO_COMM"; -export const REQ_SAVE_COMMENT = "REQ_SAVE_COMMENT"; -export const RCV_SAVE_COMMENT = "RCV_SAVE_COMMENT"; -export const REQ_DELETE_COMMENT = "REQ_DELETE_COMMENT"; -export const RCV_DELETE_COMMENT = "RCV_DELETE_COMMENT"; diff --git a/client/src/constants/TestDesignActions.js b/client/src/constants/TestDesignActions.js deleted file mode 100644 index b0b8dd0..0000000 --- a/client/src/constants/TestDesignActions.js +++ /dev/null @@ -1,20 +0,0 @@ -export const REQ_TEST_CASES = "REQ_TEST_CASES"; -export const RCV_TEST_CASES = "RCV_TEST_CASES"; -export const REQ_TEST_PLANS = "REQ_TEST_PLANS"; -export const RCV_TEST_PLANS = "RCV_TEST_PLANS"; -export const RESET_TC_ADD_EDIT = "RESET_TC_ADD_EDIT"; -export const REQ_TEST_CASE = "REQ_TEST_CASE"; -export const RCV_TEST_CASE = "RCV_TEST_CASE"; -export const REQ_TC_SAVE = "REQ_TC_SAVE"; -export const RCV_TC_SAVE = "RCV_TC_SAVE"; -export const SELECT_TEST_PLAN = "SELECT_TEST_PLAN"; -export const CHANGE_TC_DESCR = "CHANGE_TC_DESCR" -export const CHANGE_TC_NAME = "CHANGE_TC_NAME"; -export const REQ_TP_SAVE = "REQ_TP_SAVE"; -export const RCV_TP_SAVE = "RCV_TP_SAVE"; -export const REQ_DEL_TC = "REQ_DEL_TC"; -export const RCV_DEL_TC = "RCV_DEL_TC"; -export const REQ_UPLOAD_TESTS = "REQ_UPLOAD_TESTS"; -export const RCV_UPLOAD_TESTS = "RCV_UPLOAD_TESTS"; -export const REQ_ATTACH_TO_TC = "REQ_ATTACH_TO_TC"; -export const RCV_ATTACH_TO_TC = "RCV_ATTACH_TO_TC"; diff --git a/client/src/constants/TestRunStateColors.js b/client/src/constants/TestRunStateColors.js deleted file mode 100644 index fe24f58..0000000 --- a/client/src/constants/TestRunStateColors.js +++ /dev/null @@ -1,7 +0,0 @@ -import STATES from "common/constants/TestRunStates"; - -export default { - [STATES[0]]: "info", - [STATES[1]]: "success", - [STATES[2]]: "danger" -}; diff --git a/client/src/constants/TestSelectorActions.js b/client/src/constants/TestSelectorActions.js deleted file mode 100644 index 4bfc45d..0000000 --- a/client/src/constants/TestSelectorActions.js +++ /dev/null @@ -1,13 +0,0 @@ -const NAMESPACE = "TC_SELECTOR_"; - -export const REQ_ITEMS = NAMESPACE + "REQ_ITEMS"; -export const RCV_ITEMS = NAMESPACE + "RCV_ITEMS"; -export const CHANGE_PATH = NAMESPACE + "CHANGE_PATH"; -export const SELECT = NAMESPACE + "SELECT"; -export const SELECT_ALL = NAMESPACE + "SELECT_ALL"; -export const DESELECT = NAMESPACE + "DESELECT"; -export const DESELECT_ALL = NAMESPACE + "DESELECT_ALL"; -export const RESET_SELECT = NAMESPACE + "RESET_SELECT"; -export const REQ_IMPORT_TESTS = NAMESPACE + "REQ_IMPORT_TESTS"; -export const RCV_IMPORT_TESTS = NAMESPACE + "RCV_IMPORT_TESTS"; -export const REQ_INIT_DATA = NAMESPACE + "REQ_INIT_DATA"; diff --git a/client/src/initializers/index.js b/client/src/initializers/index.js deleted file mode 100644 index 2bf8d80..0000000 --- a/client/src/initializers/index.js +++ /dev/null @@ -1,50 +0,0 @@ -import React from "react"; -import ReactDOM from "react-dom"; -import { Router, Route, Switch } from "react-router-dom"; -import { Provider } from "react-redux"; -import createMiddleware from "redux-saga"; - -import configureStore from "utils/Shared/store"; // eslint-disable-line import/default -import reducer from "reducers/index"; -import sagas from "sagas/index"; - -import DashboardPage from "pages/Dashboard"; -import DesignPage from "pages/Design"; -import TestPlanPage from "pages/TestPlan"; -import AddTestPage from "pages/AddTest"; -import EditTestPage from "pages/EditTest"; -import ExecCyclesPage from "pages/ExecCycles"; -import ExecCyclePage from "pages/ExecCycle"; -import TestRunPage from "pages/TestRun"; -import DefectsPage from "pages/Defects"; -import AddDefectPage from "pages/AddDefect"; -import EditDefectPage from "pages/EditDefect"; -import DocsPage from "pages/Docs"; - -import history from "utils/Shared/history"; - -import "sass/index.scss"; - -const sagaMiddleware = createMiddleware(); -const store = configureStore(reducer, sagaMiddleware); -sagaMiddleware.run(sagas); - -ReactDOM.render(<Provider store={store}> - <Router history={history}> - <Switch> - <Route path="/" exact component={DashboardPage} /> - <Route path="/design" exact component={DesignPage} /> - <Route path="/design/testplan/:testPlanId" exact component={TestPlanPage} /> - <Route path="/design/testplan/:testPlanId/testcase/add" exact component={AddTestPage} /> - <Route path="/design/testplan/:testPlanId/testcase/edit/:testId" exact component={EditTestPage} /> - <Route path="/execution" exact component={ExecCyclesPage} /> - <Route path="/execution/:execCycleId" exact component={ExecCyclePage} /> - <Route path="/execution/:execCycleId/test/:testRunId" component={TestRunPage} /> - <Route path="/defects" exact component={DefectsPage} /> - <Route path="/defects/add" exact component={AddDefectPage} /> - <Route path="/defects/testcase/:testCaseId/add" exact component={AddDefectPage} /> - <Route path="/defects/edit/:defectID" component={EditDefectPage} /> - <Route path="/docs" component={DocsPage} /> - </Switch> - </Router> -</Provider>, document.getElementById("app-root")); diff --git a/client/src/initializers/login.js b/client/src/initializers/login.js deleted file mode 100644 index 8705a30..0000000 --- a/client/src/initializers/login.js +++ /dev/null @@ -1,8 +0,0 @@ -import React from "react"; -import ReactDOM from "react-dom"; - -import HomePage from "pages/Home"; - -import "sass/login.scss"; - -ReactDOM.render(<HomePage />, document.getElementById("app-root")); diff --git a/client/src/pages/AddDefect.js b/client/src/pages/AddDefect.js deleted file mode 100644 index 1c9f292..0000000 --- a/client/src/pages/AddDefect.js +++ /dev/null @@ -1,13 +0,0 @@ -import React from "react"; -import { hot } from "react-hot-loader"; - -import App from "components/AppContainer"; -import Defects from "components/Defects"; - -const AddDefectPage = () => { - return (<App navId="defects"> - <Defects mode="add" /> - </App>); -}; - -export default hot(module)(AddDefectPage); diff --git a/client/src/pages/AddTest.js b/client/src/pages/AddTest.js deleted file mode 100644 index 4360e06..0000000 --- a/client/src/pages/AddTest.js +++ /dev/null @@ -1,24 +0,0 @@ -import React from "react"; -import PropTypes from "prop-types"; -import { hot } from "react-hot-loader"; - -import App from "components/AppContainer"; -import Design from "components/TestDesign"; - -const AddTestPage = props => { - const { testPlanId } = props.match.params; - return (<App navId="design"> - <Design - mode="add" - testPlanId={parseInt(testPlanId)} /> - </App>); -}; -AddTestPage.propTypes = { - match: PropTypes.shape({ - params: PropTypes.shape({ - testPlanId: PropTypes.string.isRequired - }).isRequired - }).isRequired -}; - -export default hot(module)(AddTestPage); diff --git a/client/src/pages/Dashboard.js b/client/src/pages/Dashboard.js deleted file mode 100644 index f5c41c1..0000000 --- a/client/src/pages/Dashboard.js +++ /dev/null @@ -1,13 +0,0 @@ -import React from "react"; -import { hot } from "react-hot-loader"; - -import App from "components/AppContainer"; -import Dashboard from "components/Dashboard"; - -const DashboardPage = () => { - return (<App navId="dashboard"> - <Dashboard /> - </App>); -}; - -export default hot(module)(DashboardPage); diff --git a/client/src/pages/Defects.js b/client/src/pages/Defects.js deleted file mode 100644 index cb952d7..0000000 --- a/client/src/pages/Defects.js +++ /dev/null @@ -1,13 +0,0 @@ -import React from "react"; -import { hot } from "react-hot-loader"; - -import App from "components/AppContainer"; -import Defects from "components/Defects"; - -const DefectsPage = () => { - return (<App navId="defects"> - <Defects mode="list" /> - </App>); -}; - -export default hot(module)(DefectsPage); diff --git a/client/src/pages/Design.js b/client/src/pages/Design.js deleted file mode 100644 index ccf8e03..0000000 --- a/client/src/pages/Design.js +++ /dev/null @@ -1,13 +0,0 @@ -import React from "react"; -import { hot } from "react-hot-loader"; - -import App from "components/AppContainer"; -import Design from "components/TestDesign"; - -const DesignPage = () => { - return (<App navId="design"> - <Design mode="list" /> - </App>); -}; - -export default hot(module)(DesignPage); diff --git a/client/src/pages/Docs.js b/client/src/pages/Docs.js deleted file mode 100644 index 0a55cf7..0000000 --- a/client/src/pages/Docs.js +++ /dev/null @@ -1,12 +0,0 @@ -import React from "react"; - -import App from "components/AppContainer"; -import Documents from "components/Documents"; - -const DocsPage = () => { - return (<App navId="docs"> - <Documents /> - </App>); -}; - -export default DocsPage; diff --git a/client/src/pages/EditDefect.js b/client/src/pages/EditDefect.js deleted file mode 100644 index 14d95a0..0000000 --- a/client/src/pages/EditDefect.js +++ /dev/null @@ -1,23 +0,0 @@ -import React from "react"; -import PropTypes from "prop-types"; -import { hot } from "react-hot-loader"; - -import App from "components/AppContainer"; -import Defects from "components/Defects"; - -const EditDefectPage = props => { - const defectID = parseInt(props.match.params.defectID); - return (<App navId="defects"> - <Defects mode="edit" defectID={defectID} /> - </App>); -}; -EditDefectPage.propTypes = { - match: PropTypes.shape({ - params: PropTypes.shape({ - defectID: PropTypes.string.isRequired - }).isRequired - }).isRequired -}; - - -export default hot(module)(EditDefectPage); diff --git a/client/src/pages/EditTest.js b/client/src/pages/EditTest.js deleted file mode 100644 index d054b09..0000000 --- a/client/src/pages/EditTest.js +++ /dev/null @@ -1,26 +0,0 @@ -import React from "react"; -import PropTypes from "prop-types"; -import { hot } from "react-hot-loader"; - -import App from "components/AppContainer"; -import Design from "components/TestDesign"; - -const EditTestPage = props => { - const { testPlanId, testId } = props.match.params; - return (<App navId="design"> - <Design - mode="edit" - testPlanId={parseInt(testPlanId)} - testId={parseInt(testId)} /> - </App>); -}; -EditTestPage.propTypes = { - match: PropTypes.shape({ - params: PropTypes.shape({ - testId: PropTypes.string.isRequired, - testPlanId: PropTypes.string.isRequired - }).isRequired - }).isRequired -}; - -export default hot(module)(EditTestPage); diff --git a/client/src/pages/ExecCycle.js b/client/src/pages/ExecCycle.js deleted file mode 100644 index 2e4d7ed..0000000 --- a/client/src/pages/ExecCycle.js +++ /dev/null @@ -1,23 +0,0 @@ -import React from "react"; -import PropTypes from "prop-types"; -import { hot } from "react-hot-loader"; - -import App from "components/AppContainer"; -import ExecCycle from "components/ExecCycle"; - -const ExecCyclePage = props => { - const { execCycleId } = props.match.params; - return (<App navId="execution"> - <ExecCycle mode="list" execCycleId={parseInt(execCycleId)} /> - </App>); -}; -ExecCyclePage.propTypes = { - match: PropTypes.shape({ - params: PropTypes.shape({ - execCycleId: PropTypes.string.isRequired - }).isRequired - }).isRequired -}; - - -export default hot(module)(ExecCyclePage); diff --git a/client/src/pages/ExecCycles.js b/client/src/pages/ExecCycles.js deleted file mode 100644 index ce3c3c4..0000000 --- a/client/src/pages/ExecCycles.js +++ /dev/null @@ -1,13 +0,0 @@ -import React from "react"; -import { hot } from "react-hot-loader"; - -import App from "components/AppContainer"; -import ExecCycle from "components/ExecCycle"; - -const ExecCyclesPage = () => { - return (<App navId="execution"> - <ExecCycle mode="list" /> - </App>); -}; - -export default hot(module)(ExecCyclesPage); diff --git a/client/src/pages/Home.js b/client/src/pages/Home.js deleted file mode 100644 index c0d95a2..0000000 --- a/client/src/pages/Home.js +++ /dev/null @@ -1,10 +0,0 @@ -import React from "react"; -import { hot } from "react-hot-loader"; - -import Home from "components/Home"; - -const HomePage = () => { - return (<Home />); -}; - -export default hot(module)(HomePage); diff --git a/client/src/pages/TestPlan.js b/client/src/pages/TestPlan.js deleted file mode 100644 index a50f16c..0000000 --- a/client/src/pages/TestPlan.js +++ /dev/null @@ -1,23 +0,0 @@ -import React from "react"; -import PropTypes from "prop-types"; -import { hot } from "react-hot-loader"; - -import App from "components/AppContainer"; -import Design from "components/TestDesign"; - -const TestPlanPage = props => { - const { testPlanId } = props.match.params; - return (<App navId="design"> - <Design mode="list" testPlanId={parseInt(testPlanId)} /> - </App>); -}; -TestPlanPage.propTypes = { - match: PropTypes.shape({ - params: PropTypes.shape({ - testPlanId: PropTypes.string.isRequired - }).isRequired - }).isRequired -}; - - -export default hot(module)(TestPlanPage); diff --git a/client/src/pages/TestRun.js b/client/src/pages/TestRun.js deleted file mode 100644 index c36c522..0000000 --- a/client/src/pages/TestRun.js +++ /dev/null @@ -1,26 +0,0 @@ -import React from "react"; -import PropTypes from "prop-types"; -import { hot } from "react-hot-loader"; - -import App from "components/AppContainer"; -import ExecCycle from "components/ExecCycle"; - -const TestRunPage = props => { - const { execCycleId, testRunId } = props.match.params; - return (<App navId="execution"> - <ExecCycle - mode="testrun" - execCycleId={parseInt(execCycleId)} - testRunId={parseInt(testRunId)} /> - </App>); -}; -TestRunPage.propTypes = { - match: PropTypes.shape({ - params: PropTypes.shape({ - execCycleId: PropTypes.string.isRequired, - testRunId: PropTypes.string.isRequired - }).isRequired - }).isRequired -}; - -export default hot(module)(TestRunPage); diff --git a/client/src/reducers/Dashboard/index.js b/client/src/reducers/Dashboard/index.js deleted file mode 100644 index ac02606..0000000 --- a/client/src/reducers/Dashboard/index.js +++ /dev/null @@ -1,20 +0,0 @@ -import * as ACTIONS from "constants/DashboardActions"; - -const initialState = { - recentComments: [], - defects: [], - execCycles: [] -}; - -const dashboardReducer = (state=initialState, action) => { - switch(action.type) { - case ACTIONS.RCV_SUMMARY: - return { - ...initialState, - ...action.summary - }; - } - return state; -}; - -export default dashboardReducer; diff --git a/client/src/reducers/DefectSelector/index.js b/client/src/reducers/DefectSelector/index.js deleted file mode 100644 index 642ef69..0000000 --- a/client/src/reducers/DefectSelector/index.js +++ /dev/null @@ -1,88 +0,0 @@ -import * as ACTIONS from "constants/DefectSelectorActions"; - -import isChildOf from "utils/GroupMultiSelect/isChildOf"; - -const initialState = { - all: [], - isReady: false, - path: [], - selected: [], - flatList: [] -}; - -const selectorReducer = (state=initialState, action) => { - const { type } = action; - let index; - let selectedItems; - switch(type) { - case ACTIONS.CHANGE_PATH: - return { - ...state, - path: action.path - }; - - case ACTIONS.RCV_ITEMS: - return { - ...state, - all: action.items, - selected: action.selectedItems.length - ? action.selectedItems - : state.selected, - isReady: true - }; - - case ACTIONS.RESET_SELECT: - return { - ...state, - selected: action.selectedItems - }; - - case ACTIONS.DESELECT: - index = state.selected.findIndex(t => t.id === action.item.id); - if(index >= 0) { - return { - ...state, - selected: [ - ...state.selected.slice(0, index), - ...state.selected.slice(index + 1) - ] - }; - } - break; - - case ACTIONS.DESELECT_ALL: - return { - ...state, - selected: [] - }; - - case ACTIONS.SELECT: { - const selectedItem = { - ...action.item, - path: action.path - }; - return { - ...state, - selected: [ - selectedItem, - ...state.selected.filter(s => !isChildOf(s, selectedItem)) - ] - }; - } - - case ACTIONS.SELECT_ALL: - selectedItems = [ ...state.selected ]; - action.items.forEach(t => { - if(!selectedItems.find(st => st.id===t.id)) - selectedItems.push(t); - }); - return { - ...state, - selected: selectedItems - }; - - } - return state; -}; - -export default selectorReducer; diff --git a/client/src/reducers/Defects/addEdit.js b/client/src/reducers/Defects/addEdit.js deleted file mode 100644 index ed72f7d..0000000 --- a/client/src/reducers/Defects/addEdit.js +++ /dev/null @@ -1,214 +0,0 @@ -import * as ACTIONS from "constants/DefectsActions"; -import { - RCV_DELETE_ATTACH, - RCV_DELETE_COMMENT, - RCV_UPDATE_ATTACH, - RCV_SAVE_COMMENT -} from "constants/SharedActions"; - -const initialState = { - assignee: null, - name: "", - description: { - value: "", - attachments: [] - }, - comments: [], - testCases: [] -}; - -const addEditDefect = (state=initialState, action) => { - const { type } = action; - switch(type) { - case ACTIONS.ADD_TESTS_TO_DEFECT: { - const { testCases } = action; - const newTests = []; - testCases.forEach(testCase => { - const index = state.testCases.findIndex(tc => tc.id==testCase.id); - if(index == -1) { - newTests.push({ - id: testCase.id, - name: testCase.name, - testPlan: testCase.testPlan - }); - } - }); - return { - ...state, - testCases: [ - ...state.testCases, - ...newTests - ] - }; - } - - case ACTIONS.CHANGE_DF_ASSIGNEE: - return { - ...state, - assignee: { - ...action.value - } - }; - - case ACTIONS.CHANGE_DF_DESCR: - return { - ...state, - description: { - ...state.description, - value: action.value - } - }; - - case ACTIONS.CHANGE_DF_NAME: - return { - ...state, - name: action.value - }; - - case ACTIONS.CHANGE_DF_STATUS: - return { - ...state, - status: action.value - }; - - - case ACTIONS.DEL_TEST_FRM_DEFECT: { - const { id } = action; - const index = state.testCases.findIndex(tc => tc.id==id); - if(index != -1) { - return { - ...state, - testCases: [ - ...state.testCases.slice(0, index), - ...state.testCases.slice(index + 1) - ] - }; - } - break; - } - - case RCV_SAVE_COMMENT: { - - const { defect, comment } = action; - if(!defect) - break; - const index = state.comments.findIndex(c => c.id==comment.id); - if(index == -1) { - return { - ...state, - comments: [ - comment, - ...state.comments, - ] - }; - } - return { - ...state, - comments: [ - ...state.comments.slice(0, index), - comment, - ...state.comments.slice(index + 1) - ] - }; - } - - case RCV_DELETE_COMMENT: { - const { id } = action; - const index = state.comments.findIndex(c => c.id==id); - if(index != -1) { - return { - ...state, - comments: [ - ...state.comments.slice(0, index), - ...state.comments.slice(index + 1) - ] - }; - } - break; - } - - case RCV_DELETE_ATTACH: { - const { attachment } = action; - const index = state.description.attachments.findIndex(a => a.id==attachment.id); - if(index != -1) { - return { - ...state, - description: { - ...state.description, - attachments: [ - ...state.description.attachments.slice(0, index), - ...state.description.attachments.slice(index + 1), - ] - } - }; - } - // check all comment attachments - const comments = state.comments.map(comment => { - const index = comment.attachments.findIndex(a => a.id==attachment.id); - if(index != -1) { - return { - ...comment, - attachments: [ - ...comment.attachments.slice(0, index), - ...comment.attachments.slice(index + 1), - ] - }; - } - return comment; - }); - return { - ...state, - comments - }; - } - - case RCV_UPDATE_ATTACH: { - const { attachment } = action; - const index = state.description.attachments.findIndex(a => a.id==attachment.id); - if(index != -1) { - return { - ...state, - description: { - ...state.description, - attachments: [ - ...state.description.attachments.slice(0, index), - attachment, - ...state.description.attachments.slice(index + 1), - ] - } - }; - } - // check all comment attachments - const comments = state.comments.map(comment => { - const index = comment.attachments.findIndex(a => a.id==attachment.id); - if(index != -1) { - return { - ...comment, - attachments: [ - ...comment.attachments.slice(0, index), - attachment, - ...comment.attachments.slice(index + 1), - ] - }; - } - return comment; - }); - return { - ...state, - comments - }; - } - - case ACTIONS.RESET_DF_ADD_EDIT: - return initialState; - - case ACTIONS.RCV_DEFECT: - return { - ...initialState, - ...action.defect - }; - } - return state; -}; - -export default addEditDefect; diff --git a/client/src/reducers/Defects/index.js b/client/src/reducers/Defects/index.js deleted file mode 100644 index 5fcc824..0000000 --- a/client/src/reducers/Defects/index.js +++ /dev/null @@ -1,11 +0,0 @@ -import { combineReducers } from "redux"; - -import list from "./list"; -import addEdit from "./addEdit"; - -const defectsReducer = combineReducers({ - list, - addEdit -}); - -export default defectsReducer; diff --git a/client/src/reducers/Defects/list.js b/client/src/reducers/Defects/list.js deleted file mode 100644 index 157ec48..0000000 --- a/client/src/reducers/Defects/list.js +++ /dev/null @@ -1,91 +0,0 @@ -import * as ACTIONS from "constants/DefectsActions"; - -const initialState = { - all: [], - selected: [], - assigneeFilter: null, - statusFilter: null -}; - -const defectsList = (state=initialState, action) => { - const { type } = action; - switch(type) { - case ACTIONS.RCV_DEFECTS: - return { - ...state, - all: action.defects - } - - case ACTIONS.TOGGLE_SELECT_DEF: { - const { defect, status } = action; - const index = state.selected.findIndex(s => s.id == defect.id); - if(status && index == -1) { - return { - ...state, - selected: [ - ...state.selected, - defect - ] - }; - } - if(!status && index !== -1) { - return { - ...state, - selected: [ - ...state.selected.slice(0, index), - ...state.selected.slice(index + 1), - ] - }; - } - break; - } - - case ACTIONS.RCV_DELETE_DEFECT: { - const { id } = action; - const all = state.all.filter(d => d.id!=id); - const selected = state.selected.filter(d => d.id==id); - return { - ...state, - all, - selected - }; - } - - case ACTIONS.RCV_DELETE_DEFECTS: { - const { idArr } = action; - const all = state.all.filter(d => !idArr.find(id => d.id==id)); - const selected = state.selected.filter(d => !idArr.find(id => d.id==id)); - return { - ...state, - all, - selected - }; - } - - case ACTIONS.TOGGLE_SELECT_DEF_ALL: { - const { status } = action; - return { - ...state, - selected: status - ? [ ...state.all ] - : [] - }; - } - - case ACTIONS.CHANGE_ASSIGNEE_FILTER: - return { - ...state, - assigneeFilter: action.value - }; - - case ACTIONS.CHANGE_STATUS_FILTER: - return { - ...state, - statusFilter: action.value - }; - - } - return state; -}; - -export default defectsList; diff --git a/client/src/reducers/ExecCycle/addEdit.js b/client/src/reducers/ExecCycle/addEdit.js deleted file mode 100644 index 3baef60..0000000 --- a/client/src/reducers/ExecCycle/addEdit.js +++ /dev/null @@ -1,42 +0,0 @@ -import * as ACTIONS from "constants/ExecCyclesActions"; - -const initialState = { - name: "", - startDate: null, - endDate: null, - testRuns: [], - status: "New" -}; - -const addEditExecCycle = (state=initialState, action) => { - const { type } = action; - switch(type) { - case ACTIONS.RESET_EC_ADD_EDIT: - return initialState; - - case ACTIONS.INIT_EC_EDIT: { - const { execCycle } = action; - if(!execCycle) - return initialState; - return { - ...state, - name: execCycle.name, - startDate: execCycle.startDate, - endDate: execCycle.endDate, - testRuns: [ ...execCycle.testRuns ], - status: execCycle.status - }; - } - - case ACTIONS.RCV_EXEC_CYCLE: - case ACTIONS.SELECT_EXEC_CYCLE: - return { - ...initialState, - ...action.execCycle - }; - - } - return state; -}; - -export default addEditExecCycle; diff --git a/client/src/reducers/ExecCycle/importTestDialog.js b/client/src/reducers/ExecCycle/importTestDialog.js deleted file mode 100644 index c39e42e..0000000 --- a/client/src/reducers/ExecCycle/importTestDialog.js +++ /dev/null @@ -1,17 +0,0 @@ -import * as ACTIONS from "constants/ExecCyclesActions"; - -const initialState = false; - -const importTestDialog = (state=initialState, action) => { - const { type } = action; - switch(type) { - case ACTIONS.TOGGLE_IMPORT_DLG: - return action.show; - - case ACTIONS.RCV_IMPORT_TESTS: - return initialState; - } - return state; -}; - -export default importTestDialog; diff --git a/client/src/reducers/ExecCycle/index.js b/client/src/reducers/ExecCycle/index.js deleted file mode 100644 index 5a85934..0000000 --- a/client/src/reducers/ExecCycle/index.js +++ /dev/null @@ -1,17 +0,0 @@ -import { combineReducers } from "redux"; - -import addEdit from "./addEdit"; -import importTestDialog from "./importTestDialog"; -import list from "./list"; -import testRun from "./testRun"; -import testRuns from "./testRuns"; - -const execCycleReducer = combineReducers({ - addEdit, - importTestDialog, - list, - testRun, - testRuns -}); - -export default execCycleReducer; diff --git a/client/src/reducers/ExecCycle/list.js b/client/src/reducers/ExecCycle/list.js deleted file mode 100644 index 59b5e6e..0000000 --- a/client/src/reducers/ExecCycle/list.js +++ /dev/null @@ -1,152 +0,0 @@ -import * as ACTIONS from "constants/ExecCyclesActions"; - -const initialState = { - all: [], - selected: null, - statusFilter: null, - _tempSelectedId: null -}; - -const execCycleList = (state=initialState, action) => { - const { type } = action; - switch(type) { - case ACTIONS.RCV_EXEC_CYCLES: { - const { execCycles } = action; - if(state._tempSelectedId) { - const match = execCycles.find(ec => ec.id == state._tempSelectedId); - if(match) { - return { - ...state, - all: execCycles, - selected: match, - _tempSelectedId: null - }; - } - } - return { - ...state, - all: execCycles, - selected: state.selected - ? state.selected - : execCycles.length - ? execCycles[0] - : null - }; - } - - case ACTIONS.RCV_CLONE_EC: { - const { execCycle } = action; - return { - ...state, - all: [ - ...state.all, - execCycle - ] - }; - } - - case ACTIONS.SELECT_EXEC_CYCLE: { - const { execCycle } = action; - const match = state.all.find(ec => ec.id == execCycle.id); - if(!match) { - return { - ...state, - selected: null, - _tempSelectedId: execCycle.id - }; - } - return { - ...state, - selected: match - }; - } - - case ACTIONS.RCV_EC_SAVE: { - const { execCycle } = action; - let all = state.all; - let selected = state.selected; - const index = state.all.findIndex(ec => ec.id == execCycle.id); - if(index != -1) { - all = [ - ...state.all.slice(0, index), - execCycle, - ...state.all.slice(index + 1) - ]; - } else { - all = [ - ...state.all, - execCycle - ]; - } - if(selected && selected.id == execCycle.id) - selected = execCycle; - return { - ...state, - all, - selected - }; - } - - case ACTIONS.RCV_DEL_EC: { - const { execCycle } = action; - let all = state.all; - let selected = state.selected; - const index = state.all.findIndex(ec => ec.id == execCycle.id); - if(index != -1) { - all = [ - ...state.all.slice(0, index), - ...state.all.slice(index + 1) - ]; - } - if(selected && selected.id == execCycle.id) - selected = null; - return { - ...state, - all, - selected - }; - } - - case ACTIONS.RCV_END_EC: - case ACTIONS.RCV_START_EC: { - const { execCycle } = action; - let all = state.all; - let selected = state.selected; - const index = state.all.findIndex(ec => ec.id == execCycle.id); - if(index != -1) { - all = [ - ...state.all.slice(0, index), - { - ...state.all[index], - status: execCycle.status - }, - ...state.all.slice(index + 1) - ]; - } else { - all = [ - ...state.all, - execCycle - ]; - } - if(selected && selected.id == execCycle.id) - selected = { - ...selected, - status: execCycle.status - }; - return { - ...state, - all, - selected - }; - } - - case ACTIONS.CHANGE_STATUS_FILTER: - return { - ...state, - statusFilter: action.value - }; - } - return state; -}; - -export default execCycleList; diff --git a/client/src/reducers/ExecCycle/testRun.js b/client/src/reducers/ExecCycle/testRun.js deleted file mode 100644 index 1e0b6ab..0000000 --- a/client/src/reducers/ExecCycle/testRun.js +++ /dev/null @@ -1,115 +0,0 @@ -import * as ACTIONS from "constants/ExecCyclesActions"; -import { RCV_DELETE_DEFECT } from "constants/DefectsActions"; -import { RCV_SAVE_COMMENT, RCV_DELETE_COMMENT } from "constants/SharedActions"; - -const initialState = { - name: "", - testCase: {}, - defects: [], - comments: [], - status: "New" -}; - -const addEditExecCycle = (state=initialState, action) => { - const { type } = action; - switch(type) { - case ACTIONS.RCV_TEST_RUN: - case ACTIONS.RCV_SAVE_TR: - return { - ...initialState, - ...action.testRun, - name: action.testRun.testCase.name, - defects: action.testRun.defects || [] - }; - - case ACTIONS.RCV_ADD_NEW_DEFECT: { - const { defect } = action; - if(!state.defects.find(d => d.id==defect.id)) { - return { - ...state, - defects: [ - ...state.defects, - defect - ] - }; - } - break; - } - - case ACTIONS.RCV_LINK_DEFECTS: { - const { defects } = action; - const newDefects = []; - defects.forEach(defect => { - if(!state.defects.find(d => d.id==defect.id)) { - newDefects.push(defect) - } - }); - return { - ...state, - defects: [ - ...state.defects, - ...newDefects - ] - }; - } - - case RCV_DELETE_DEFECT: - case ACTIONS.RCV_UNLINK_DEFECT: { - const { defect } = action; - const index = state.defects.findIndex(d => d.id==defect.id); - if(index != -1) { - return { - ...state, - defects: [ - ...state.defects.slice(0, index), - ...state.defects.slice(index + 1) - ] - }; - } - break; - } - - case RCV_SAVE_COMMENT: { - const { comment } = action; - const index = state.comments.findIndex(c => c.id==comment.id); - if(index == -1) { - return { - ...state, - comments: [ - comment, - ...state.comments, - ] - }; - } - return { - ...state, - comments: [ - ...state.comments.slice(0, index), - comment, - ...state.comments.slice(index + 1) - ] - }; - } - - case RCV_DELETE_COMMENT: { - const { id } = action; - const index = state.comments.findIndex(c => c.id==id); - if(index != -1) { - return { - ...state, - comments: [ - ...state.comments.slice(0, index), - ...state.comments.slice(index + 1) - ] - }; - } - break; - } - - case ACTIONS.RESET_TR_ADD_EDIT: - return initialState; - } - return state; -}; - -export default addEditExecCycle; diff --git a/client/src/reducers/ExecCycle/testRuns.js b/client/src/reducers/ExecCycle/testRuns.js deleted file mode 100644 index aa0d263..0000000 --- a/client/src/reducers/ExecCycle/testRuns.js +++ /dev/null @@ -1,148 +0,0 @@ -import * as ACTIONS from "constants/ExecCyclesActions"; - -// testRuns are stored as { [execCycleId]: { all: testRunArr, selected: selectedArr } } -const initialState = {}; - -const testRuns = (state=initialState, action) => { - const { type } = action; - switch(type) { - case ACTIONS.RCV_TEST_RUNS: - return { - ...state, - [action.execCycleId]: { - all: action.testRuns, - selected: [] - } - }; - - case ACTIONS.RCV_DEL_TEST_RUNS: { - const { idArr } = action; - const execCycleIds = Object.keys(state); - return execCycleIds.reduce((acc, execCycleId) => { - const testRuns = state[execCycleId].all - .filter(tr => !idArr.find(id => id==tr.id)); - return { - ...acc, - [execCycleId]: { - all: testRuns, - selected: [] - } - }; - }, {}); - } - - case ACTIONS.TOGGLE_SELECT_TR: { - const testRuns = state[action.execCycleId]; - if(testRuns && testRuns.selected) { - const index = testRuns.selected.findIndex(tr => tr.id==action.testRun.id); - if(action.status && index == -1) { - return { - ...state, - [action.execCycleId]: { - ...state[action.execCycleId], - selected: [ - ...state[action.execCycleId].selected, - action.testRun - ] - } - }; - } - if(!action.status && index !== -1) { - return { - ...state, - [action.execCycleId]: { - ...state[action.execCycleId], - selected: [ - ...state[action.execCycleId].selected.slice(0, index), - ...state[action.execCycleId].selected.slice(index + 1), - ] - } - }; - } - } - break; - } - - case ACTIONS.RCV_SAVE_TR: { - const { testRun } = action; - const testRuns = state[testRun.execCycle.id]; - const testRunSummary = { - ...testRun, - name: testRun.testCase.name, - testCase: testRun.testCase.id, - execCycle: testRun.execCycle.id - }; - if(testRuns) { - let all = testRuns.all; - let selected = testRuns.selected; - const indexA = all.findIndex(tr => tr.id==testRun.id); - if(indexA != -1) { - all = [ - ...all.slice(0, indexA), - testRunSummary, - ...all.slice(indexA + 1) - ]; - } - const indexS = selected.findIndex(tr => tr.id==testRun.id); - if(indexS != -1) { - selected = [ - ...selected.slice(0, indexS), - testRunSummary, - ...selected.slice(indexS + 1) - ]; - } - return { - ...state, - [testRun.execCycle.id]: { - ...testRuns, - all, - selected - } - }; - } - break; - } - - case ACTIONS.TOGGLE_SELECT_TR_ALL: { - const testRuns = state[action.execCycleId]; - if(testRuns) { - if(action.status) { - return { - ...state, - [action.execCycleId]: { - ...testRuns, - selected: testRuns.all - } - }; - } else { - return { - ...state, - [action.execCycleId]: { - ...testRuns, - selected: [] - } - }; - } - } - break; - } - - case ACTIONS.RCV_EC_SAVE: { - const testRuns = action.execCycle.testRuns.map(tr => ({ - ...tr, - defects: tr.defects.length - })); - return { - ...state, - [action.execCycle.id]: { - all: testRuns, - selected: [] - } - }; - } - - } - return state; -}; - -export default testRuns; diff --git a/client/src/reducers/Shared/GroupMultiSelect/filterText.js b/client/src/reducers/Shared/GroupMultiSelect/filterText.js deleted file mode 100644 index 6abbfdf..0000000 --- a/client/src/reducers/Shared/GroupMultiSelect/filterText.js +++ /dev/null @@ -1,17 +0,0 @@ -import { FILTER_LIST, NAV_DOWN, NAV_UP, NAV_TO } from "constants/GroupMultiSelectActions"; - -const filterReducer = (state="", actions) => { - const { type } = actions; - switch(type) { - case FILTER_LIST: - return actions.filterText; - - case NAV_DOWN: - case NAV_UP: - case NAV_TO: - return ""; - } - return state; -}; - -export default filterReducer; diff --git a/client/src/reducers/Shared/GroupMultiSelect/index.js b/client/src/reducers/Shared/GroupMultiSelect/index.js deleted file mode 100644 index 8617859..0000000 --- a/client/src/reducers/Shared/GroupMultiSelect/index.js +++ /dev/null @@ -1,59 +0,0 @@ -import { combineReducers } from "redux"; - -import { INIT, DESTROY } from "constants/GroupMultiSelectActions"; - -import filterText from "./filterText"; -import path from "./path"; -import selectedItems from "./selectedItems"; - -const reducer = combineReducers({ - filterText, - path, - selectedItems -}); - -const initialState = {}; - -const rootReducer = (state=initialState, action) => { - const { type } = action; - let newState; - switch(type) { - case INIT: - if(!state[action.cid]) { - return { - ...state, - ...{ - [action.cid]: reducer({ - selectedItems: action.selectedItems - }, action) - } - }; - } else if(action.cid in state) { - return { - ...state, - ...{ [action.cid]: reducer(state[action.cid], action) } - }; - } - break; - - case DESTROY: - if(action.cid in state) { - newState = { ...state }; - delete newState[action.cid]; - return newState; - } - break; - - default: - if(action.cid in state) { - return { - ...state, - ...{ [action.cid]: reducer(state[action.cid], action) } - }; - } - break; - } - return state; -}; - -export default rootReducer; diff --git a/client/src/reducers/Shared/GroupMultiSelect/path.js b/client/src/reducers/Shared/GroupMultiSelect/path.js deleted file mode 100644 index ce196f4..0000000 --- a/client/src/reducers/Shared/GroupMultiSelect/path.js +++ /dev/null @@ -1,34 +0,0 @@ -import { NAV_DOWN, NAV_TO, NAV_UP } from "constants/GroupMultiSelectActions"; - -const initialState = []; - -const pathReducer = (state=initialState, action={}) => { - const { type } = action; - let index; - switch(type) { - case NAV_DOWN: - return [ - ...state, - action.item - ]; - - case NAV_TO: - if(!action.item || !action.item.id) - return initialState; - index = -1; - state.forEach((level, i) => { - if(level.id === action.item.id) - index = i; - }); - if(index != -1) { - return state.slice(0, index + 1); - } - break; - - case NAV_UP: - return state.slice(0, state.length-1); - } - return state; -}; - -export default pathReducer; diff --git a/client/src/reducers/Shared/GroupMultiSelect/selectedItems.js b/client/src/reducers/Shared/GroupMultiSelect/selectedItems.js deleted file mode 100644 index 8551386..0000000 --- a/client/src/reducers/Shared/GroupMultiSelect/selectedItems.js +++ /dev/null @@ -1,35 +0,0 @@ -import * as ACTIONS from "constants/GroupMultiSelectActions"; -import flatten from "utils/GroupMultiSelect/flatten"; -import isChildOf from "utils/GroupMultiSelect/isChildOf"; - -const selectedItemsReducer = (state=[], action) => { - const { type } = action; - let deselectedItem; - switch(type) { - case ACTIONS.DESELECT_ITEM: - // remove item from selectedItems[] - deselectedItem = action.item; - return state.filter(item => item.id != deselectedItem.id); - - case ACTIONS.SELECT_ITEM: - return [ - ...state.filter(s => !isChildOf(s, action.item)), - action.item - ]; - - case ACTIONS.SELECT_ALL: - // add everything from the current level - return [ - ...state, - ...flatten({ - items: action.items - }) - ]; - - case ACTIONS.DESELECT_ALL: - return []; - } - return state; -}; - -export default selectedItemsReducer; diff --git a/client/src/reducers/Shared/isLoading.js b/client/src/reducers/Shared/isLoading.js deleted file mode 100644 index 12cd37c..0000000 --- a/client/src/reducers/Shared/isLoading.js +++ /dev/null @@ -1,28 +0,0 @@ -import { IS_LOADING } from "constants/SharedActions"; - -const initialState = []; - -const loadingReducer = (state=initialState, action) => { - const {type} = action; - let id, status, index; - switch(type) { - case IS_LOADING: - id = action.id; - status = action.status; - index = state.findIndex(taskId => taskId===id); - if(status) { - return [ - ...state, - id - ]; - } else if(index!==-1) { - return [ - ...state.slice(0, index), - ...state.slice(index + 1) - ]; - } - } - return state; -}; - -export default loadingReducer; diff --git a/client/src/reducers/Shared/session.js b/client/src/reducers/Shared/session.js deleted file mode 100644 index 50de475..0000000 --- a/client/src/reducers/Shared/session.js +++ /dev/null @@ -1,19 +0,0 @@ -import { RCV_LOGIN_STATUS } from "constants/SharedActions"; - -const initialState = { - user: null -}; - -const loginReducer = (state=initialState, action) => { - const { type } = action; - switch(type) { - case RCV_LOGIN_STATUS: - return { - ...state, - user: action.user || null - }; - } - return state; -}; - -export default loginReducer; diff --git a/client/src/reducers/Shared/users.js b/client/src/reducers/Shared/users.js deleted file mode 100644 index 8461c6d..0000000 --- a/client/src/reducers/Shared/users.js +++ /dev/null @@ -1,19 +0,0 @@ -import * as ACTIONS from "constants/SharedActions"; - -const initialState = { - all: [] -}; - -const usersReducer = (state=initialState, action) => { - const { type } = action; - switch(type) { - case ACTIONS.RCV_USERS: - return { - ...state, - all: action.users - }; - } - return state; -}; - -export default usersReducer; diff --git a/client/src/reducers/TestDesign/addEdit.js b/client/src/reducers/TestDesign/addEdit.js deleted file mode 100644 index 067a051..0000000 --- a/client/src/reducers/TestDesign/addEdit.js +++ /dev/null @@ -1,162 +0,0 @@ -import * as ACTIONS from "constants/TestDesignActions"; -import { - RCV_DELETE_ATTACH, - RCV_DELETE_COMMENT, - RCV_UPDATE_ATTACH, - RCV_SAVE_COMMENT -} from "constants/SharedActions"; - -const initialState = { - id: null, - name: "", - description: { - value: "", - attachments: [] - }, - comments: [], - defects: [] -}; - -const testCases = (state=initialState, action) => { - const { type } = action; - switch(type) { - case ACTIONS.CHANGE_TC_DESCR: - return { - ...state, - description: { - ...state.description, - value: action.value - } - }; - - case ACTIONS.CHANGE_TC_NAME: - return { - ...state, - name: action.value - }; - - case RCV_SAVE_COMMENT: { - - const { testCase, comment } = action; - if(!testCase) - break; - const index = state.comments.findIndex(c => c.id==comment.id); - if(index == -1) { - return { - ...state, - comments: [ - comment, - ...state.comments, - ] - }; - } - return { - ...state, - comments: [ - ...state.comments.slice(0, index), - comment, - ...state.comments.slice(index + 1) - ] - }; - } - - case RCV_DELETE_COMMENT: { - const { id } = action; - const index = state.comments.findIndex(c => c.id==id); - if(index != -1) { - return { - ...state, - comments: [ - ...state.comments.slice(0, index), - ...state.comments.slice(index + 1) - ] - }; - } - break; - } - - case RCV_DELETE_ATTACH: { - const { attachment } = action; - const index = state.description.attachments.findIndex(a => a.id==attachment.id); - if(index != -1) { - return { - ...state, - description: { - ...state.description, - attachments: [ - ...state.description.attachments.slice(0, index), - ...state.description.attachments.slice(index + 1), - ] - } - }; - } - // check all comment attachments - const comments = state.comments.map(comment => { - const index = comment.attachments.findIndex(a => a.id==attachment.id); - if(index != -1) { - return { - ...comment, - attachments: [ - ...comment.attachments.slice(0, index), - ...comment.attachments.slice(index + 1), - ] - }; - } - return comment; - }); - return { - ...state, - comments - }; - } - - case RCV_UPDATE_ATTACH: { - const { attachment } = action; - const index = state.description.attachments.findIndex(a => a.id==attachment.id); - if(index != -1) { - return { - ...state, - description: { - ...state.description, - attachments: [ - ...state.description.attachments.slice(0, index), - attachment, - ...state.description.attachments.slice(index + 1), - ] - } - }; - } - // check all comment attachments - const comments = state.comments.map(comment => { - const index = comment.attachments.findIndex(a => a.id==attachment.id); - if(index != -1) { - return { - ...comment, - attachments: [ - ...comment.attachments.slice(0, index), - attachment, - ...comment.attachments.slice(index + 1), - ] - }; - } - return comment; - }); - return { - ...state, - comments - }; - } - - case ACTIONS.RESET_TC_ADD_EDIT: - return initialState; - - case ACTIONS.RCV_TEST_CASE: - return { - ...initialState, - ...action.testCase - }; - } - return state; -}; - -export default testCases; diff --git a/client/src/reducers/TestDesign/index.js b/client/src/reducers/TestDesign/index.js deleted file mode 100644 index 5cf4e58..0000000 --- a/client/src/reducers/TestDesign/index.js +++ /dev/null @@ -1,11 +0,0 @@ -import { combineReducers } from "redux"; - -import testPlans from "./testPlans"; -import addEdit from "./addEdit"; - -const designReducer = combineReducers({ - testPlans, - addEdit -}); - -export default designReducer; diff --git a/client/src/reducers/TestDesign/testPlans.js b/client/src/reducers/TestDesign/testPlans.js deleted file mode 100644 index c1d376d..0000000 --- a/client/src/reducers/TestDesign/testPlans.js +++ /dev/null @@ -1,147 +0,0 @@ -import * as ACTIONS from "constants/TestDesignActions"; - -const initialState = { - all: [], - selected: null, - testCases: {}, - _tempSelectedId: null -}; - -const testCases = (state=initialState, action) => { - const { type } = action; - switch(type) { - case ACTIONS.RCV_TEST_PLANS: { - const { testPlans } = action; - if(state._tempSelectedId) { - const match = testPlans.find(tp => tp.id == state._tempSelectedId); - if(match) { - return { - ...state, - all: testPlans, - selected: match, - _tempSelectedId: null - }; - } - } - return { - ...state, - all: testPlans, - selected: state.selected - ? state.selected - : testPlans.length - ? testPlans[0] - : null - }; - } - - case ACTIONS.RCV_TP_SAVE: { - const { testPlan } = action; - const index = state.all.findIndex(tp => tp.id==testPlan.id); - if(index==-1) { - return { - ...state, - all: [ - ...state.all, - testPlan - ] - }; - } - return { - ...state, - all: [ - ...state.all.slice(0, index), - testPlan, - ...state.all.slice(index + 1) - ] - }; - } - - case ACTIONS.SELECT_TEST_PLAN: { - const { testPlan } = action; - const match = state.all.find(tp => tp.id == testPlan.id); - if(!match) { - return { - ...state, - selected: null, - _tempSelectedId: testPlan.id - }; - } - return { - ...state, - selected: match - }; - } - - case ACTIONS.RCV_TEST_CASES: - return { - ...state, - testCases: { - ...state.testCases, - [action.testPlanId]: action.testCases - } - }; - - case ACTIONS.RCV_TC_SAVE: { - const { testPlanId, testCase } = action; - let testCases = state.testCases[testPlanId] || []; - const index = testCases.findIndex(tc => tc.id == testCase.id); - if(index == -1) { - testCases = [ - ...testCases, - testCase - ]; - } else { - testCases = [ - ...testCases.slice(0, index), - testCase, - ...testCases.slice(index + 1) - ]; - } - return { - ...state, - testCases: { - ...state.testCases, - [testPlanId]: testCases - } - }; - } - - case ACTIONS.RCV_DEL_TC: { - const { id, testPlanId } = action; - let testCases = state.testCases[testPlanId] || []; - const index = testCases.findIndex(tc => tc.id == id); - if(index !== -1) { - testCases = [ - ...testCases.slice(0, index), - ...testCases.slice(index + 1) - ]; - } - return { - ...state, - testCases: { - ...state.testCases, - [testPlanId]: testCases - } - }; - } - - case ACTIONS.RCV_UPLOAD_TESTS: { - const { testPlanId, testCases } = action; - const existing = state.testCases[testPlanId] || []; - return { - ...state, - testCases: { - ...state.testCases, - [testPlanId]: [ - ...existing, - ...testCases - ] - } - }; - } - - } - return state; -}; - -export default testCases; diff --git a/client/src/reducers/TestSelector/index.js b/client/src/reducers/TestSelector/index.js deleted file mode 100644 index 31120d2..0000000 --- a/client/src/reducers/TestSelector/index.js +++ /dev/null @@ -1,94 +0,0 @@ -import * as ACTIONS from "constants/TestSelectorActions"; - -import isChildOf from "utils/GroupMultiSelect/isChildOf"; - -const initialState = { - all: [], - isReady: false, - path: [], - selected: [], - flatList: [] -}; - -const selectorReducer = (state=initialState, action) => { - const { type } = action; - let index; - let selectedItems; - switch(type) { - case ACTIONS.CHANGE_PATH: - return { - ...state, - path: action.path - }; - - case ACTIONS.RCV_ITEMS: - return { - ...state, - all: action.items, - selected: action.selectedItems.length - ? action.selectedItems - : state.selected, - isReady: true - }; - - case ACTIONS.RESET_SELECT: - return { - ...state, - selected: action.selectedItems - }; - - case ACTIONS.DESELECT: - index = state.selected.findIndex(t => t.id === action.item.id); - if(index >= 0) { - return { - ...state, - selected: [ - ...state.selected.slice(0, index), - ...state.selected.slice(index + 1) - ] - }; - } - break; - - case ACTIONS.DESELECT_ALL: - return { - ...state, - selected: [] - }; - - case ACTIONS.SELECT: { - const selectedItem = { - ...action.item, - path: action.path - }; - return { - ...state, - selected: [ - selectedItem, - ...state.selected.filter(s => !isChildOf(s, selectedItem)) - ] - }; - } - - case ACTIONS.SELECT_ALL: - selectedItems = [ ...state.selected ]; - action.items.forEach(t => { - if(!selectedItems.find(st => st.id===t.id)) - selectedItems.push(t); - }); - return { - ...state, - selected: selectedItems - }; - - case ACTIONS.RCV_IMPORT_TESTS: - return { - ...initialState, - flatList: [ ...action.tests ] - }; - - } - return state; -}; - -export default selectorReducer; diff --git a/client/src/reducers/index.js b/client/src/reducers/index.js deleted file mode 100644 index 8af515e..0000000 --- a/client/src/reducers/index.js +++ /dev/null @@ -1,29 +0,0 @@ -import { combineReducers } from "redux"; - -import dashboard from "./Dashboard"; -import defects from "./Defects"; -import defectSelector from "./DefectSelector"; -import execCycle from "./ExecCycle"; -import isLoading from "./Shared/isLoading"; -import session from "./Shared/session"; -import testDesign from "./TestDesign"; -import testSelector from "./TestSelector"; -import users from "./Shared/users"; - -import groupMultiSelect from "reducers/Shared/GroupMultiSelect"; - -const rootReducer = combineReducers({ - dashboard, - defects, - defectSelector, - execCycle, - isLoading, - session, - testDesign, - testSelector, - users, - - groupMultiSelect -}); - -export default rootReducer; diff --git a/client/src/sagas/Dashboard/getSummary.js b/client/src/sagas/Dashboard/getSummary.js deleted file mode 100644 index fd57b2f..0000000 --- a/client/src/sagas/Dashboard/getSummary.js +++ /dev/null @@ -1,28 +0,0 @@ -import Alert from "react-s-alert"; -import { call, put, takeEvery } from "redux-saga/effects"; - -import request from "utils/Shared/request"; - -import { REQ_SUMMARY } from "constants/DashboardActions"; -import { rcvSummary } from "actions/Dashboard"; -import { setLoading } from "actions/Shared"; - -function* getSummary() { - yield put(setLoading(REQ_SUMMARY, true)); - try { - const response = yield call(request, { - url: "/api/dashboard", - type: "get", - dataType: "json" - }); - yield put(rcvSummary(response.json)); - } catch(ex) { - console.log(ex); // eslint-disable-line no-console - Alert.error("Failed to fetch dashboard data"); - } - yield put(setLoading(REQ_SUMMARY, false)); -} - -export default function* () { - yield takeEvery(REQ_SUMMARY, getSummary); -}; diff --git a/client/src/sagas/Dashboard/index.js b/client/src/sagas/Dashboard/index.js deleted file mode 100644 index 55ce7ac..0000000 --- a/client/src/sagas/Dashboard/index.js +++ /dev/null @@ -1,9 +0,0 @@ -import { spawn } from "redux-saga/effects"; - -import getSummary from "./getSummary"; - -function* dashboardSaga() { - yield spawn(getSummary); -} - -export default dashboardSaga; diff --git a/client/src/sagas/DefectSelector/index.js b/client/src/sagas/DefectSelector/index.js deleted file mode 100644 index b85af10..0000000 --- a/client/src/sagas/DefectSelector/index.js +++ /dev/null @@ -1,9 +0,0 @@ -import { spawn } from "redux-saga/effects"; - -import reqSelectorItems from "./reqSelectorItems"; - -function* defectSelectorSaga() { - yield spawn(reqSelectorItems); -} - -export default defectSelectorSaga; diff --git a/client/src/sagas/DefectSelector/reqSelectorItems.js b/client/src/sagas/DefectSelector/reqSelectorItems.js deleted file mode 100644 index 9321541..0000000 --- a/client/src/sagas/DefectSelector/reqSelectorItems.js +++ /dev/null @@ -1,25 +0,0 @@ -import { put, take, takeEvery } from "redux-saga/effects"; - -import { REQ_ITEMS } from "constants/DefectSelectorActions"; -import { RCV_DEFECTS } from "constants/DefectsActions"; -import { rcvItems } from "actions/DefectSelector"; -import { reqDefects } from "actions/Defects"; -import { setLoading } from "actions/Shared"; - -import parseSelectorData from "utils/DefectSelector/parseSelectorData"; -import cache from "utils/DefectSelector/DefectSelectorCache"; - -function* getItems(action) { - const { path } = action; - let { selectedItems } = action; - yield put(reqDefects()); - const { defects } = yield take(RCV_DEFECTS); - cache.save(parseSelectorData(defects, action)); - if(selectedItems.length) - selectedItems = cache.addDetails(selectedItems); - yield put(rcvItems(cache.getByPath(path), selectedItems)); - yield put(setLoading(REQ_ITEMS, false)); -} -export default function* () { - yield takeEvery(REQ_ITEMS, getItems); -} diff --git a/client/src/sagas/Defects/attachFile.js b/client/src/sagas/Defects/attachFile.js deleted file mode 100644 index 6d8f399..0000000 --- a/client/src/sagas/Defects/attachFile.js +++ /dev/null @@ -1,26 +0,0 @@ -import { put, take, takeEvery } from "redux-saga/effects"; - -import { REQ_ATTACH_TO_DEFECT, RCV_SAVE_DEFECT } from "constants/DefectsActions"; -import { RCV_UPLOAD_FILES } from "constants/SharedActions"; -import { rcvAttachToDefect, reqSaveDefect } from "actions/Defects"; -import { reqUploadFiles, setLoading } from "actions/Shared"; - - -function* attachToDefect(action) { - const { defect, file } = action; - yield put(setLoading(REQ_ATTACH_TO_DEFECT, true)); - // upload file - yield put(reqUploadFiles([ file ])); - const uploadResult = yield take(RCV_UPLOAD_FILES); - // add to defect - defect.description.attachments.push(uploadResult.files[0]); - // save defect - yield put(reqSaveDefect(defect, null, false)); - const result = yield take(RCV_SAVE_DEFECT); - yield put(rcvAttachToDefect(uploadResult.files[0], result.defect)); - yield put(setLoading(REQ_ATTACH_TO_DEFECT, false)); -} - -export default function* () { - yield takeEvery(REQ_ATTACH_TO_DEFECT, attachToDefect); -}; diff --git a/client/src/sagas/Defects/deleteDefect.js b/client/src/sagas/Defects/deleteDefect.js deleted file mode 100644 index 23f5857..0000000 --- a/client/src/sagas/Defects/deleteDefect.js +++ /dev/null @@ -1,31 +0,0 @@ -import Alert from "react-s-alert"; -import { call, put, takeEvery } from "redux-saga/effects"; - -import request from "utils/Shared/request"; - -import { REQ_DELETE_DEFECT } from "constants/DefectsActions"; -import { rcvDeleteDefect } from "actions/Defects"; -import { redirectToDefects, setLoading } from "actions/Shared"; - -function* delDefect(action) { - const { id, redirect } = action; - yield put(setLoading(REQ_DELETE_DEFECT, true)); - try { - const response = yield call(request, { - url: `/api/defect/${id}`, - type: "delete" - }); - Alert.success("Deleted"); - yield put(rcvDeleteDefect(response.json)); - if(redirect) - yield put(redirectToDefects()); - } catch(ex) { - console.log(ex); // eslint-disable-line no-console - Alert.error("Failed to delete defect. " + (ex && ex.text || "")); - } - yield put(setLoading(REQ_DELETE_DEFECT, false)); -} - -export default function* () { - yield takeEvery(REQ_DELETE_DEFECT, delDefect); -}; diff --git a/client/src/sagas/Defects/deleteDefects.js b/client/src/sagas/Defects/deleteDefects.js deleted file mode 100644 index 14318fe..0000000 --- a/client/src/sagas/Defects/deleteDefects.js +++ /dev/null @@ -1,35 +0,0 @@ -import Alert from "react-s-alert"; -import { call, put, takeEvery } from "redux-saga/effects"; - -import request from "utils/Shared/request"; - -import { REQ_DELETE_DEFECTS } from "constants/DefectsActions"; -import { rcvDeleteDefects } from "actions/Defects"; -import { setLoading } from "actions/Shared"; - -function* delDefects(action) { - const { idArr } = action; - if(!idArr.length) - return; - yield put(setLoading(REQ_DELETE_DEFECTS, true)); - try { - const response = yield call(request, { - url: "/api/defect", - type: "delete", - data: { - ids: idArr - }, - dataType: "json" - }); - Alert.success(`Deleted ${idArr.length} defects`); - yield put(rcvDeleteDefects(response.json)); - } catch(ex) { - console.log(ex); // eslint-disable-line no-console - Alert.error("Failed to delete defects. " + (ex && ex.text || "")); - } - yield put(setLoading(REQ_DELETE_DEFECTS, false)); -} - -export default function* () { - yield takeEvery(REQ_DELETE_DEFECTS, delDefects); -}; diff --git a/client/src/sagas/Defects/index.js b/client/src/sagas/Defects/index.js deleted file mode 100644 index c387bdc..0000000 --- a/client/src/sagas/Defects/index.js +++ /dev/null @@ -1,19 +0,0 @@ -import { spawn } from "redux-saga/effects"; - -import attachFile from "./attachFile"; -import deleteDefect from "./deleteDefect"; -import deleteDefects from "./deleteDefects"; -import reqDefect from "./reqDefect"; -import reqDefects from "./reqDefects"; -import saveDefect from "./saveDefect"; - -function* defectsSaga() { - yield spawn(attachFile); - yield spawn(deleteDefect); - yield spawn(deleteDefects); - yield spawn(reqDefect); - yield spawn(reqDefects); - yield spawn(saveDefect); -} - -export default defectsSaga; diff --git a/client/src/sagas/Defects/reqDefect.js b/client/src/sagas/Defects/reqDefect.js deleted file mode 100644 index a4054e3..0000000 --- a/client/src/sagas/Defects/reqDefect.js +++ /dev/null @@ -1,33 +0,0 @@ -import Alert from "react-s-alert"; -import { call, put, takeEvery } from "redux-saga/effects"; - -import request from "utils/Shared/request"; - -import { REQ_DEFECT } from "constants/DefectsActions"; -import { rcvDefect } from "actions/Defects"; -import { setLoading } from "actions/Shared"; - -function* getDefect(action) { - const { id } = action; - if(!id) { - Alert.error("Defect ID is required"); - return; - } - yield put(setLoading(REQ_DEFECT, true)); - try { - const response = yield call(request, { - url: `/api/defect/${id}`, - type: "get", - dataType: "json" - }); - yield put(rcvDefect(response.json)); - } catch(ex) { - console.log(ex); // eslint-disable-line no-console - Alert.error("Failed to fetch defect. " + (ex && ex.text || "")); - } - yield put(setLoading(REQ_DEFECT, false)); -} - -export default function* () { - yield takeEvery(REQ_DEFECT, getDefect); -}; diff --git a/client/src/sagas/Defects/reqDefects.js b/client/src/sagas/Defects/reqDefects.js deleted file mode 100644 index 336aff7..0000000 --- a/client/src/sagas/Defects/reqDefects.js +++ /dev/null @@ -1,28 +0,0 @@ -import Alert from "react-s-alert"; -import { call, put, takeEvery } from "redux-saga/effects"; - -import request from "utils/Shared/request"; - -import { REQ_DEFECTS } from "constants/DefectsActions"; -import { rcvDefects } from "actions/Defects"; -import { setLoading } from "actions/Shared"; - -function* getDefects() { - yield put(setLoading(REQ_DEFECTS, true)); - try { - const response = yield call(request, { - url: "/api/defect", - type: "get", - dataType: "json" - }); - yield put(rcvDefects(response.json)); - } catch(ex) { - console.log(ex); // eslint-disable-line no-console - Alert.error("Failed to fetch defects. " + (ex && ex.text || "")); - } - yield put(setLoading(REQ_DEFECTS, false)); -} - -export default function* () { - yield takeEvery(REQ_DEFECTS, getDefects); -}; diff --git a/client/src/sagas/Defects/saveDefect.js b/client/src/sagas/Defects/saveDefect.js deleted file mode 100644 index 8b85e9a..0000000 --- a/client/src/sagas/Defects/saveDefect.js +++ /dev/null @@ -1,54 +0,0 @@ -import Alert from "react-s-alert"; -import { call, put, take, takeEvery } from "redux-saga/effects"; - -import request from "utils/Shared/request"; - -import { REQ_SAVE_DEFECT } from "constants/DefectsActions"; -import { RCV_UPLOAD_FILES } from "constants/SharedActions"; - -import { rcvSaveDefect } from "actions/Defects"; -import { redirectToDefects, reqUploadFiles, setLoading } from "actions/Shared"; - -import buildDefect from "utils/Defects/buildDefect"; - - -function* saveDefect(action) { - const { defect, files, redirect } = action; - yield put(setLoading(REQ_SAVE_DEFECT, true)); - try { - let response; - if(defect.id) { - response = yield call(request, { - url: `/api/defect/${defect.id}`, - type: "put", - data: buildDefect(defect), - dataType: "json" - }); - Alert.success("Saved"); - } else { - // upload files, if any - if(files.length) { - yield put(reqUploadFiles(files)); - const uploadResult = yield take(RCV_UPLOAD_FILES); - defect.description.attachments = uploadResult.files; - } - response = yield call(request, { - url: "/api/defect", - type: "post", - data: buildDefect(defect), - dataType: "json" - }); - } - yield put(rcvSaveDefect(response.json)); - if(redirect) - yield put(redirectToDefects()); - } catch(ex) { - console.log(ex); // eslint-disable-line no-console - Alert.error("Failed to save defect. " + (ex && ex.text || "")); - } - yield put(setLoading(REQ_SAVE_DEFECT, false)); -} - -export default function* () { - yield takeEvery(REQ_SAVE_DEFECT, saveDefect); -}; diff --git a/client/src/sagas/ExecCycle/addNewDefect.js b/client/src/sagas/ExecCycle/addNewDefect.js deleted file mode 100644 index ced0b69..0000000 --- a/client/src/sagas/ExecCycle/addNewDefect.js +++ /dev/null @@ -1,31 +0,0 @@ -import { put, take, takeEvery } from "redux-saga/effects"; - -import { REQ_ADD_NEW_DEFECT } from "constants/ExecCyclesActions"; -import { RCV_SAVE_DEFECT } from "constants/DefectsActions"; -import { rcvAddNewDefect } from "actions/ExecCycle"; -import { reqSaveDefect } from "actions/Defects"; - -import { setLoading } from "actions/Shared"; - -function* addNewDefect(action) { - const { defect, files, testRun } = action; - yield put(setLoading(REQ_ADD_NEW_DEFECT, true)); - yield put(reqSaveDefect( - { - ...defect, - testCases: [{ - id: testRun.testCase.id - }], - testRuns: [ testRun ] - }, - files, - false - )); - const result = yield take(RCV_SAVE_DEFECT); - yield put(rcvAddNewDefect(result.defect)); - yield put(setLoading(REQ_ADD_NEW_DEFECT, false)); -} - -export default function* () { - yield takeEvery(REQ_ADD_NEW_DEFECT, addNewDefect); -}; diff --git a/client/src/sagas/ExecCycle/cloneExecCycle.js b/client/src/sagas/ExecCycle/cloneExecCycle.js deleted file mode 100644 index 2c132dc..0000000 --- a/client/src/sagas/ExecCycle/cloneExecCycle.js +++ /dev/null @@ -1,34 +0,0 @@ -import Alert from "react-s-alert"; -import { call, put, takeEvery } from "redux-saga/effects"; - -import request from "utils/Shared/request"; - -import { REQ_CLONE_EC } from "constants/ExecCyclesActions"; -import { rcvCloneExecCycle } from "actions/ExecCycle"; -import { setLoading } from "actions/Shared"; - - -function* cloneExecCycle(action) { - const { id, cloneType } = action; - yield put(setLoading(REQ_CLONE_EC, true)); - try { - const response = yield call(request, { - url: `/api/exec/${id}/clone`, - type: "post", - data: { - type: cloneType - }, - dataType: "json" - }); - Alert.success("Saved"); - yield put(rcvCloneExecCycle(response.json)); - } catch(ex) { - console.log(ex); // eslint-disable-line no-console - Alert.error("Failed to clone exec cycle"); - } - yield put(setLoading(REQ_CLONE_EC, false)); -} - -export default function* () { - yield takeEvery(REQ_CLONE_EC, cloneExecCycle); -}; diff --git a/client/src/sagas/ExecCycle/importTests.js b/client/src/sagas/ExecCycle/importTests.js deleted file mode 100644 index ae1c1fc..0000000 --- a/client/src/sagas/ExecCycle/importTests.js +++ /dev/null @@ -1,34 +0,0 @@ -import Alert from "react-s-alert"; -import { put, take, takeEvery } from "redux-saga/effects"; - -import { REQ_IMPORT_TESTS, RCV_EC_SAVE } from "constants/ExecCyclesActions"; -import { reqSaveExecCycle, rcvImportTests } from "actions/ExecCycle"; - -function* importTests(action) { - const { execCycle, tests } = action; - if(!execCycle || !execCycle.id) - return; - const testRuns = [ ...execCycle.testRuns ]; - const testCases = testRuns.map(tr => ({ - id: tr.testCase - })); - tests.forEach(testCase => { - if(!find(testCases.find(tc => tc.id==testCase.id))) { - testCases.push({ - id: testCase.id - }); - } - }); - yield put(reqSaveExecCycle({ - ...execCycle, - testRuns: undefined, - testCases - })); - yield take(RCV_EC_SAVE); - Alert.success(`${tests.length} tests imported`); - yield put(rcvImportTests(tests)); -} - -export default function* () { - yield takeEvery(REQ_IMPORT_TESTS, importTests); -}; diff --git a/client/src/sagas/ExecCycle/index.js b/client/src/sagas/ExecCycle/index.js deleted file mode 100644 index e65dd21..0000000 --- a/client/src/sagas/ExecCycle/index.js +++ /dev/null @@ -1,35 +0,0 @@ -import { spawn } from "redux-saga/effects"; - -import addNewDefect from "./addNewDefect"; -import cloneExecCycle from "./cloneExecCycle"; -import importTests from "./importTests"; -import linkDefects from "./linkDefects"; -import reqDeleteExecCycle from "./reqDeleteExecCycle"; -import reqDeleteTestRuns from "./reqDeleteTestRuns"; -import reqExecCycle from "./reqExecCycle"; -import reqExecCycles from "./reqExecCycles"; -import reqTestRun from "./reqTestRun"; -import reqTestRuns from "./reqTestRuns"; -import saveExecCycle from "./saveExecCycle"; -import saveTestRun from "./saveTestRun"; -import startStopExec from "./startStopExec"; -import unlinkDefect from "./unlinkDefect"; - -function* execCyclesSaga() { - yield spawn(addNewDefect); - yield spawn(cloneExecCycle); - yield spawn(importTests); - yield spawn(linkDefects); - yield spawn(reqDeleteExecCycle); - yield spawn(reqDeleteTestRuns); - yield spawn(reqExecCycle); - yield spawn(reqExecCycles); - yield spawn(reqTestRun); - yield spawn(reqTestRuns); - yield spawn(saveExecCycle); - yield spawn(saveTestRun); - yield spawn(startStopExec); - yield spawn(unlinkDefect); -} - -export default execCyclesSaga; diff --git a/client/src/sagas/ExecCycle/linkDefects.js b/client/src/sagas/ExecCycle/linkDefects.js deleted file mode 100644 index 7534652..0000000 --- a/client/src/sagas/ExecCycle/linkDefects.js +++ /dev/null @@ -1,33 +0,0 @@ -import Alert from "react-s-alert"; -import { call, put, takeEvery } from "redux-saga/effects"; - -import request from "utils/Shared/request"; - -import { REQ_LINK_DEFECTS } from "constants/ExecCyclesActions"; -import { rcvLinkDefects } from "actions/ExecCycle"; -import { setLoading } from "actions/Shared"; - -function* linkDefects(action) { - const { testRun, defects } = action; - - yield put(setLoading(REQ_LINK_DEFECTS, true)); - try { - yield call(request, { - url: `/api/testrun/${testRun.id}/defect`, - type: "post", - data: { - defects: defects.map(d => d.id) - }, - dataType: "json" - }); - yield put(rcvLinkDefects(defects, testRun)); - } catch(ex) { - console.log(ex); // eslint-disable-line no-console - Alert.error("Failed to link defects: " + (ex && ex.text || "")); - } - yield put(setLoading(REQ_LINK_DEFECTS, false)); -} - -export default function* () { - yield takeEvery(REQ_LINK_DEFECTS, linkDefects); -}; diff --git a/client/src/sagas/ExecCycle/reqDeleteExecCycle.js b/client/src/sagas/ExecCycle/reqDeleteExecCycle.js deleted file mode 100644 index 8998297..0000000 --- a/client/src/sagas/ExecCycle/reqDeleteExecCycle.js +++ /dev/null @@ -1,31 +0,0 @@ -import Alert from "react-s-alert"; -import { call, put, takeEvery } from "redux-saga/effects"; - -import request from "utils/Shared/request"; - -import { REQ_DEL_EC } from "constants/ExecCyclesActions"; -import { rcvDeleteExecCycle } from "actions/ExecCycle"; -import { redirectToExecCycle, setLoading } from "actions/Shared"; - -function* deleteExecCycles(action) { - const { execCycle } = action; - yield put(setLoading(REQ_DEL_EC, true)); - try { - const response = yield call(request, { - url: `/api/exec/${execCycle.id}`, - type: "delete" - }); - yield put(rcvDeleteExecCycle(response.json)); - if(execCycle.selected) { - yield put(redirectToExecCycle()); - } - } catch(ex) { - console.log(ex); // eslint-disable-line no-console - Alert.error("Failed to delete execution cycle. " + (ex && ex.text || "")); - } - yield put(setLoading(REQ_DEL_EC, false)); -} - -export default function* () { - yield takeEvery(REQ_DEL_EC, deleteExecCycles); -}; diff --git a/client/src/sagas/ExecCycle/reqDeleteTestRuns.js b/client/src/sagas/ExecCycle/reqDeleteTestRuns.js deleted file mode 100644 index 0272f0e..0000000 --- a/client/src/sagas/ExecCycle/reqDeleteTestRuns.js +++ /dev/null @@ -1,35 +0,0 @@ -import Alert from "react-s-alert"; -import { call, put, takeEvery } from "redux-saga/effects"; - -import request from "utils/Shared/request"; - -import { REQ_DEL_TEST_RUNS } from "constants/ExecCyclesActions"; -import { rcvDeleteTestRuns } from "actions/ExecCycle"; -import { setLoading } from "actions/Shared"; - -function* delTestRuns(action) { - const { idArr } = action; - if(!idArr.length) - return; - yield put(setLoading(REQ_DEL_TEST_RUNS, true)); - try { - const response = yield call(request, { - url: "/api/testrun", - type: "delete", - data: { - ids: idArr - }, - dataType: "json" - }); - Alert.success(`Deleted ${idArr.length} tests`); - yield put(rcvDeleteTestRuns(response.json)); - } catch(ex) { - console.log(ex); // eslint-disable-line no-console - Alert.error("Failed to delete tests. " + (ex && ex.text || "")); - } - yield put(setLoading(REQ_DEL_TEST_RUNS, false)); -} - -export default function* () { - yield takeEvery(REQ_DEL_TEST_RUNS, delTestRuns); -}; diff --git a/client/src/sagas/ExecCycle/reqExecCycle.js b/client/src/sagas/ExecCycle/reqExecCycle.js deleted file mode 100644 index 50247a8..0000000 --- a/client/src/sagas/ExecCycle/reqExecCycle.js +++ /dev/null @@ -1,28 +0,0 @@ -import Alert from "react-s-alert"; -import { call, put, takeEvery } from "redux-saga/effects"; - -import request from "utils/Shared/request"; - -import { REQ_EXEC_CYCLE } from "constants/ExecCyclesActions"; - -import { rcvExecCycle } from "actions/ExecCycle"; -import { setLoading } from "actions/Shared"; - -function* getExecCycle(action) { - const { id } = action; - yield put(setLoading(REQ_EXEC_CYCLE, true)); - try { - const response = yield call(request, { - url: `/api/exec/${id}` - }); - yield put(rcvExecCycle(response.json)); - } catch(ex) { - console.log(ex); // eslint-disable-line no-console - Alert.error("Failed to fetch execution cycle"); - } - yield put(setLoading(REQ_EXEC_CYCLE, false)); -} - -export default function* () { - yield takeEvery(REQ_EXEC_CYCLE, getExecCycle); -}; diff --git a/client/src/sagas/ExecCycle/reqExecCycles.js b/client/src/sagas/ExecCycle/reqExecCycles.js deleted file mode 100644 index b203f98..0000000 --- a/client/src/sagas/ExecCycle/reqExecCycles.js +++ /dev/null @@ -1,27 +0,0 @@ -import Alert from "react-s-alert"; -import { call, put, takeEvery } from "redux-saga/effects"; - -import request from "utils/Shared/request"; - -import { REQ_EXEC_CYCLES } from "constants/ExecCyclesActions"; -import { rcvExecCycles } from "actions/ExecCycle"; -import { setLoading } from "actions/Shared"; - -function* getExecCycles() { - yield put(setLoading(REQ_EXEC_CYCLES, true)); - try { - const response = yield call(request, { - url: "/api/exec", - dataType: "json" - }); - yield put(rcvExecCycles(response.json)); - } catch(ex) { - console.log(ex); // eslint-disable-line no-console - Alert.error("Failed to fetch execution cycles. " + (ex && ex.text || "")); - } - yield put(setLoading(REQ_EXEC_CYCLES, false)); -} - -export default function* () { - yield takeEvery(REQ_EXEC_CYCLES, getExecCycles); -}; diff --git a/client/src/sagas/ExecCycle/reqTestRun.js b/client/src/sagas/ExecCycle/reqTestRun.js deleted file mode 100644 index 0e128da..0000000 --- a/client/src/sagas/ExecCycle/reqTestRun.js +++ /dev/null @@ -1,30 +0,0 @@ -import Alert from "react-s-alert"; -import { call, put, takeEvery } from "redux-saga/effects"; - -import request from "utils/Shared/request"; - -import { REQ_TEST_RUN } from "constants/ExecCyclesActions"; -import { rcvTestRun } from "actions/ExecCycle"; -import { setLoading } from "actions/Shared"; - -function* getTestRun(action) { - const { id } = action; - if(!id) - return; - yield put(setLoading(REQ_TEST_RUN, true)); - try { - const response = yield call(request, { - url: `/api/testrun/${id}`, - type: "get" - }); - yield put(rcvTestRun(response.json)); - } catch(ex) { - console.log(ex); // eslint-disable-line no-console - Alert.error("Failed to fetch test. " + (ex && ex.text || "")); - } - yield put(setLoading(REQ_TEST_RUN, false)); -} - -export default function* () { - yield takeEvery(REQ_TEST_RUN, getTestRun); -}; diff --git a/client/src/sagas/ExecCycle/reqTestRuns.js b/client/src/sagas/ExecCycle/reqTestRuns.js deleted file mode 100644 index 1e039d2..0000000 --- a/client/src/sagas/ExecCycle/reqTestRuns.js +++ /dev/null @@ -1,34 +0,0 @@ -import Alert from "react-s-alert"; -import { call, put, takeEvery } from "redux-saga/effects"; - -import request from "utils/Shared/request"; - -import { REQ_TEST_RUNS } from "constants/ExecCyclesActions"; -import { rcvTestRuns } from "actions/ExecCycle"; -import { setLoading } from "actions/Shared"; - -function* getTestRuns(action) { - const { execCycleId } = action; - if(!execCycleId) - return; - yield put(setLoading(REQ_TEST_RUNS, true)); - try { - const response = yield call(request, { - url: "/api/testrun", - type: "get", - data: { - execCycle: execCycleId - }, - dataType: "json" - }); - yield put(rcvTestRuns(execCycleId, response.json)); - } catch(ex) { - console.log(ex); // eslint-disable-line no-console - Alert.error("Failed to fetch tests. " + (ex && ex.text || "")); - } - yield put(setLoading(REQ_TEST_RUNS, false)); -} - -export default function* () { - yield takeEvery(REQ_TEST_RUNS, getTestRuns); -}; diff --git a/client/src/sagas/ExecCycle/saveExecCycle.js b/client/src/sagas/ExecCycle/saveExecCycle.js deleted file mode 100644 index 4d335ac..0000000 --- a/client/src/sagas/ExecCycle/saveExecCycle.js +++ /dev/null @@ -1,49 +0,0 @@ -import Alert from "react-s-alert"; -import { call, put, takeEvery } from "redux-saga/effects"; - -import request from "utils/Shared/request"; - -import { REQ_EC_SAVE } from "constants/ExecCyclesActions"; -import { rcvSaveExecCycle } from "actions/ExecCycle"; -import { setLoading } from "actions/Shared"; - -import validateExecCycle from "utils/ExecCycle/validateExecCycle"; -import buildExecCycle from "utils/ExecCycle/buildExecCycle"; - -function* saveExecCycle(action) { - const { execCycle } = action; - const validationResult = validateExecCycle(execCycle); - if(!validationResult.valid) { - Alert.error(validationResult.error); - return; - } - yield put(setLoading(REQ_EC_SAVE, true)); - try { - let response; - if(execCycle.id) { - response = yield call(request, { - url: `/api/exec/${execCycle.id}`, - type: "put", - data: buildExecCycle(execCycle), - dataType: "json" - }); - } else { - response = yield call(request, { - url: "/api/exec", - type: "post", - data: buildExecCycle(execCycle), - dataType: "json" - }); - } - Alert.success("Saved"); - yield put(rcvSaveExecCycle(response.json)); - } catch(ex) { - console.log(ex); // eslint-disable-line no-console - Alert.error("Failed to save exec cycle"); - } - yield put(setLoading(REQ_EC_SAVE, false)); -} - -export default function* () { - yield takeEvery(REQ_EC_SAVE, saveExecCycle); -}; diff --git a/client/src/sagas/ExecCycle/saveTestRun.js b/client/src/sagas/ExecCycle/saveTestRun.js deleted file mode 100644 index d64605c..0000000 --- a/client/src/sagas/ExecCycle/saveTestRun.js +++ /dev/null @@ -1,53 +0,0 @@ -import Alert from "react-s-alert"; -import { call, put, takeEvery } from "redux-saga/effects"; - -import request from "utils/Shared/request"; - -import { REQ_SAVE_TR } from "constants/ExecCyclesActions"; -import { rcvSaveTestRun } from "actions/ExecCycle"; -import { setLoading } from "actions/Shared"; - -import validateTestRun from "utils/ExecCycle/validateTestRun"; -import buildTestRun from "utils/ExecCycle/buildTestRun"; - -function* saveTestRun(action) { - const { testRun } = action; - const execCycleId = testRun.execCycle.id; - if(!testRun.id && !execCycleId) { - throw new Error("execCycleId required to create new TestRun"); - } - const validationResult = validateTestRun(testRun); - if(!validationResult.valid) { - Alert.error(validationResult.error); - return; - } - yield put(setLoading(REQ_SAVE_TR, true)); - const data = buildTestRun(testRun); - try { - let response; - if(testRun.id) { - response = yield call(request, { - url: `/api/testrun/${testRun.id}`, - type: "put", - data, - dataType: "json" - }); - } else { - response = yield call(request, { - url: `/api/testrun?execCycleId=${execCycleId}`, - type: "post", - data, - dataType: "json" - }); - } - yield put(rcvSaveTestRun(response.json)); - } catch(ex) { - console.log(ex); // eslint-disable-line no-console - Alert.error("Failed to save test. " + (ex && ex.text || "")); - } - yield put(setLoading(REQ_SAVE_TR, false)); -} - -export default function* () { - yield takeEvery(REQ_SAVE_TR, saveTestRun); -}; diff --git a/client/src/sagas/ExecCycle/startStopExec.js b/client/src/sagas/ExecCycle/startStopExec.js deleted file mode 100644 index 0e24dac..0000000 --- a/client/src/sagas/ExecCycle/startStopExec.js +++ /dev/null @@ -1,48 +0,0 @@ -import Alert from "react-s-alert"; -import { call, put, spawn, takeEvery } from "redux-saga/effects"; - -import request from "utils/Shared/request"; - -import { REQ_END_EC, REQ_START_EC } from "constants/ExecCyclesActions"; -import { rcvEndExecCycle, rcvStartExecCycle } from "actions/ExecCycle"; -import { setLoading } from "actions/Shared"; - - -function* start(action) { - const { execCycle } = action; - yield put(setLoading(REQ_START_EC, true)); - try { - const response = yield call(request, { - url: `/api/exec/${execCycle.id}/start`, - type: "post" - }); - Alert.success("Execution Cycle Started"); - yield put(rcvStartExecCycle(response.json)); - } catch(ex) { - console.log(ex); // eslint-disable-line no-console - Alert.error("Failed to start execution"); - } - yield put(setLoading(REQ_START_EC, false)); -} - -function* end(action) { - const { execCycle } = action; - yield put(setLoading(REQ_END_EC, true)); - try { - const response = yield call(request, { - url: `/api/exec/${execCycle.id}/end`, - type: "post" - }); - Alert.success("Execution Cycle Complete"); - yield put(rcvEndExecCycle(response.json)); - } catch(ex) { - console.log(ex); // eslint-disable-line no-console - Alert.error(ex.text || "Failed to stop execution"); - } - yield put(setLoading(REQ_END_EC, false)); -} - -export default function* () { - yield spawn(takeEvery, REQ_END_EC, end); - yield spawn(takeEvery, REQ_START_EC, start); -}; diff --git a/client/src/sagas/ExecCycle/unlinkDefect.js b/client/src/sagas/ExecCycle/unlinkDefect.js deleted file mode 100644 index 85d4f41..0000000 --- a/client/src/sagas/ExecCycle/unlinkDefect.js +++ /dev/null @@ -1,29 +0,0 @@ -import Alert from "react-s-alert"; -import { call, put, takeEvery } from "redux-saga/effects"; - -import request from "utils/Shared/request"; - -import { REQ_UNLINK_DEFECT } from "constants/ExecCyclesActions"; -import { rcvUnlinkDefect } from "actions/ExecCycle"; -import { setLoading } from "actions/Shared"; - -function* unlinkDefect(action) { - const { testRun, defect } = action; - - yield put(setLoading(REQ_UNLINK_DEFECT, true)); - try { - yield call(request, { - url: `/api/testrun/${testRun.id}/defect/${defect.id}`, - type: "delete" - }); - yield put(rcvUnlinkDefect(testRun, defect)); - } catch(ex) { - console.log(ex); // eslint-disable-line no-console - Alert.error("Failed to unlink defect: " + (ex && ex.text || "")); - } - yield put(setLoading(REQ_UNLINK_DEFECT, false)); -} - -export default function* () { - yield takeEvery(REQ_UNLINK_DEFECT, unlinkDefect); -}; diff --git a/client/src/sagas/Shared/attachFileToComment.js b/client/src/sagas/Shared/attachFileToComment.js deleted file mode 100644 index 6b1ba65..0000000 --- a/client/src/sagas/Shared/attachFileToComment.js +++ /dev/null @@ -1,50 +0,0 @@ -import { put, take, takeEvery } from "redux-saga/effects"; - -import { - REQ_ATTACH_TO_COMM, - RCV_SAVE_COMMENT, - RCV_UPLOAD_FILES -} from "constants/SharedActions"; - -import { - rcvAttachToComment, - reqSaveComment, - reqUploadFiles, - setLoading -} from "actions/Shared"; - - -function* attachToComment(action) { - const { file, comment, entity, entityId } = action; - let result; - yield put(setLoading(REQ_ATTACH_TO_COMM, true)); - // upload file - yield put(reqUploadFiles([ file ])); - const uploadResult = yield take(RCV_UPLOAD_FILES); - if(comment && comment.id) { - // add attachment and save comment - yield put(reqSaveComment( - { - ...comment, - attachments: [ - ...comment.attachments, - uploadResult.files[0] - ] - }, - entity, - entityId - )); - result = yield take(RCV_SAVE_COMMENT); - } - yield put(rcvAttachToComment( - uploadResult.files[0], - result && result.comment, - entity, - entityId - )); - yield put(setLoading(REQ_ATTACH_TO_COMM, false)); -} - -export default function* () { - yield takeEvery(REQ_ATTACH_TO_COMM, attachToComment); -}; diff --git a/client/src/sagas/Shared/checkLogin.js b/client/src/sagas/Shared/checkLogin.js deleted file mode 100644 index 85204ed..0000000 --- a/client/src/sagas/Shared/checkLogin.js +++ /dev/null @@ -1,43 +0,0 @@ -import Cookies from "js-cookie"; -import { call, put, takeEvery } from "redux-saga/effects"; - -import request from "utils/Shared/request"; - -import { REQ_LOGIN_STATUS } from "constants/SharedActions"; -import { rcvLoginStatus, setLoading } from "actions/Shared"; - -const COOKIE_NAME = "testman_session"; - -function* checkLogin() { - const sessionCookie = Cookies.get(COOKIE_NAME); - if(sessionCookie) { - try { - const data = JSON.parse(sessionCookie); - if(data && data.user) { - yield put(rcvLoginStatus(data.user)); - return; - } - } catch(ex) { - console.log(ex); // eslint-disable-line no-console - } - } - yield put(setLoading(REQ_LOGIN_STATUS, true)); - try { - const response = yield call(request, { - url: "/api/login", - type: "get" - }); - Cookies.set(COOKIE_NAME, { - user: response.json - }); - yield put(rcvLoginStatus(response.json)); - } catch(ex) { - console.log(ex); // eslint-disable-line no-console - yield put(rcvLoginStatus(null)); - } - yield put(setLoading(REQ_LOGIN_STATUS, false)); -} - -export default function* () { - yield takeEvery(REQ_LOGIN_STATUS, checkLogin); -}; diff --git a/client/src/sagas/Shared/deleteAttachment.js b/client/src/sagas/Shared/deleteAttachment.js deleted file mode 100644 index 7868f1a..0000000 --- a/client/src/sagas/Shared/deleteAttachment.js +++ /dev/null @@ -1,27 +0,0 @@ -import Alert from "react-s-alert"; -import { call, put, takeEvery } from "redux-saga/effects"; - -import request from "utils/Shared/request"; - -import { REQ_DELETE_ATTACH } from "constants/SharedActions"; -import { rcvDeleteAttachment, setLoading } from "actions/Shared"; - -function* deleteAttachment(action) { - const { attachment } = action; - yield put(setLoading(REQ_DELETE_ATTACH, true)); - try { - const response = yield call(request, { - url: `/api/attachment/${attachment.id}`, - type: "delete" - }); - yield put(rcvDeleteAttachment(response.json)); - } catch(ex) { - console.log(ex); // eslint-disable-line no-console - Alert.error(ex && ex.text || "Unable to save"); - } - yield put(setLoading(REQ_DELETE_ATTACH, false)); -} - -export default function* () { - yield takeEvery(REQ_DELETE_ATTACH, deleteAttachment); -}; diff --git a/client/src/sagas/Shared/deleteComment.js b/client/src/sagas/Shared/deleteComment.js deleted file mode 100644 index 6303464..0000000 --- a/client/src/sagas/Shared/deleteComment.js +++ /dev/null @@ -1,27 +0,0 @@ -import Alert from "react-s-alert"; -import { call, put, takeEvery } from "redux-saga/effects"; - -import request from "utils/Shared/request"; - -import { REQ_DELETE_COMMENT } from "constants/SharedActions"; -import { rcvDeleteComment, setLoading } from "actions/Shared"; - -function* deleteComment(action) { - const { id } = action; - yield put(setLoading(REQ_DELETE_COMMENT, true)); - try { - const response = yield call(request, { - url: `/api/comment/${id}`, - type: "delete" - }); - yield put(rcvDeleteComment(response.json)); - } catch(ex) { - console.log(ex); // eslint-disable-line no-console - Alert.error(ex && ex.text || "Unable to delete"); - } - yield put(setLoading(REQ_DELETE_COMMENT, false)); -} - -export default function* () { - yield takeEvery(REQ_DELETE_COMMENT, deleteComment); -}; diff --git a/client/src/sagas/Shared/downloadAttachment.js b/client/src/sagas/Shared/downloadAttachment.js deleted file mode 100644 index 7762676..0000000 --- a/client/src/sagas/Shared/downloadAttachment.js +++ /dev/null @@ -1,20 +0,0 @@ -import { put, takeEvery } from "redux-saga/effects"; - -import { REQ_DOWNLOAD_ATTACH } from "constants/SharedActions"; -import { setLoading } from "actions/Shared"; - -function* downloadAttachment(action) { - const { attachment } = action; - yield put(setLoading(REQ_DOWNLOAD_ATTACH, true)); - - const form = document.createElement("form"); - form.action = `/api/attachment/${attachment.id}/download`; - document.body.appendChild(form); - form.submit(); - - yield put(setLoading(REQ_DOWNLOAD_ATTACH, false)); -} - -export default function* () { - yield takeEvery(REQ_DOWNLOAD_ATTACH, downloadAttachment); -}; diff --git a/client/src/sagas/Shared/index.js b/client/src/sagas/Shared/index.js deleted file mode 100644 index 70cff92..0000000 --- a/client/src/sagas/Shared/index.js +++ /dev/null @@ -1,30 +0,0 @@ -import { spawn } from "redux-saga/effects"; - -import setupInvalidateSession from "./setupInvalidateSession"; -import checkLogin from "./checkLogin"; - -import attachFileToComment from "./attachFileToComment"; -import deleteAttachment from "./deleteAttachment"; -import deleteComment from "./deleteComment"; -import downloadAttachment from "./downloadAttachment"; -import redirectSaga from "./redirects"; -import reqUsers from "./reqUsers"; -import saveComment from "./saveComment"; -import updateAttachment from "./updateAttachment"; -import uploadFiles from "./uploadFiles"; - -function* sharedSaga() { - yield spawn(setupInvalidateSession); - yield spawn(attachFileToComment); - yield spawn(checkLogin); - yield spawn(deleteAttachment); - yield spawn(deleteComment); - yield spawn(downloadAttachment); - yield spawn(redirectSaga); - yield spawn(reqUsers); - yield spawn(saveComment); - yield spawn(uploadFiles); - yield spawn(updateAttachment); -} - -export default sharedSaga; diff --git a/client/src/sagas/Shared/redirects.js b/client/src/sagas/Shared/redirects.js deleted file mode 100644 index b2213b4..0000000 --- a/client/src/sagas/Shared/redirects.js +++ /dev/null @@ -1,36 +0,0 @@ -import { spawn, takeEvery } from "redux-saga/effects"; - -import hashHistory from "utils/Shared/history"; - -import * as ACTIONS from "constants/SharedActions"; - -function redirectToTestDesign(action) { - const { id } = action; - if(id) - hashHistory.push(`/design/testplan/${id}`); - else - hashHistory.push("/design"); -} - -function redirectToExecCycle(action) { - const { id } = action; - if(id) - hashHistory.push(`/execution/${id}`); - else - hashHistory.push("/execution"); -} - -function redirectToDefects() { - hashHistory.push("/defects"); -} - -function redirectToLogin() { - window.location.href = "/login"; -} - -export default function* () { - yield spawn(takeEvery, ACTIONS.REDIRECT_TEST_DESIGN, redirectToTestDesign); - yield spawn(takeEvery, ACTIONS.REDIRECT_EXEC_CYCLE, redirectToExecCycle); - yield spawn(takeEvery, ACTIONS.REDIRECT_DEFECTS, redirectToDefects); - yield spawn(takeEvery, ACTIONS.REDIRECT_LOGIN, redirectToLogin); -}; diff --git a/client/src/sagas/Shared/reqUsers.js b/client/src/sagas/Shared/reqUsers.js deleted file mode 100644 index 6993ab5..0000000 --- a/client/src/sagas/Shared/reqUsers.js +++ /dev/null @@ -1,27 +0,0 @@ -import Alert from "react-s-alert"; -import { call, put, takeEvery } from "redux-saga/effects"; - -import request from "utils/Shared/request"; - -import { REQ_USERS } from "constants/SharedActions"; -import { rcvUsers, setLoading } from "actions/Shared"; - - -function* getUsers() { - yield put(setLoading(REQ_USERS, true)); - try { - const response = yield call(request, { - url: "/api/user", - type: "get" - }); - yield put(rcvUsers(response.json)); - } catch(ex) { - console.log(ex); // eslint-disable-line no-console - Alert.error("Failed to fetch users"); - } - yield put(setLoading(REQ_USERS, false)); -} - -export default function* () { - yield takeEvery(REQ_USERS, getUsers); -}; diff --git a/client/src/sagas/Shared/saveComment.js b/client/src/sagas/Shared/saveComment.js deleted file mode 100644 index b085738..0000000 --- a/client/src/sagas/Shared/saveComment.js +++ /dev/null @@ -1,52 +0,0 @@ -import Alert from "react-s-alert"; -import { call, put, take, takeEvery } from "redux-saga/effects"; - -import request from "utils/Shared/request"; - -import { REQ_SAVE_COMMENT, RCV_UPLOAD_FILES } from "constants/SharedActions"; -import { rcvSaveComment, reqUploadFiles, setLoading } from "actions/Shared"; - -import buildComment from "utils/Shared/buildComment"; - -function* saveComment(action) { - const { comment, entity, entityId } = action; - if(!entity) - throw new Error("NO ENTTITY saveComment") - const data = buildComment(comment); - - yield put(setLoading(REQ_SAVE_COMMENT, true)); - try { - let response; - if(comment.id) { - response = yield call(request, { - url: `/api/comment/${comment.id}`, - type: "put", - data, - dataType: "json" - }); - } else if(entity && entityId) { - data[entity] = entityId; - if(data.files && data.files.length) { - // upload the files - yield put(reqUploadFiles(data.files)); - const { files } = yield take(RCV_UPLOAD_FILES); - data.content.attachments = files.map(f => f.id); - } - response = yield call(request, { - url: "/api/comment", - type: "post", - data, - dataType: "json" - }); - } - yield put(rcvSaveComment(response.json, entity, entityId)); - } catch(ex) { - console.log(ex); // eslint-disable-line no-console - Alert.error("Failed to save comment"); - } - yield put(setLoading(REQ_SAVE_COMMENT, false)); -} - -export default function* () { - yield takeEvery(REQ_SAVE_COMMENT, saveComment); -}; diff --git a/client/src/sagas/Shared/setupInvalidateSession.js b/client/src/sagas/Shared/setupInvalidateSession.js deleted file mode 100644 index 7aad766..0000000 --- a/client/src/sagas/Shared/setupInvalidateSession.js +++ /dev/null @@ -1,40 +0,0 @@ -import Cookies from "js-cookie"; -import { eventChannel } from "redux-saga"; -import { call, put, take, takeEvery } from "redux-saga/effects"; - -import { - SETUP_SESSION_INV, - SESSION_INVALIDATE -} from "constants/SharedActions"; - -import { redirectToLogin } from "actions/Shared"; - -import request from "utils/Shared/request"; - -const createChannel = () => { - return eventChannel(emit => { - const onError = resp => { - if(resp && resp.status==401 || resp.status==403) { - Cookies.remove("testman_session"); - emit({ type: SESSION_INVALIDATE }); - } - }; - request.on("error", onError); - return () => { - request.off("error", onError); - }; - }); -} - - -function* setupInvalidateSession() { - const channel = yield call(createChannel); - while(true) { - yield take(channel); - yield put(redirectToLogin()); - } -} - -export default function* () { - yield takeEvery(SETUP_SESSION_INV, setupInvalidateSession); -}; diff --git a/client/src/sagas/Shared/updateAttachment.js b/client/src/sagas/Shared/updateAttachment.js deleted file mode 100644 index 31abbb0..0000000 --- a/client/src/sagas/Shared/updateAttachment.js +++ /dev/null @@ -1,31 +0,0 @@ -import Alert from "react-s-alert"; -import { call, put, takeEvery } from "redux-saga/effects"; - -import request from "utils/Shared/request"; - -import { REQ_UPDATE_ATTACH } from "constants/SharedActions"; -import { rcvUpdateAttachment, setLoading } from "actions/Shared"; - -function* updateAttachment(action) { - const { attachment } = action; - yield put(setLoading(REQ_UPDATE_ATTACH, true)); - try { - const response = yield call(request, { - url: `/api/attachment/${attachment.id}`, - type: "put", - data: { - name: attachment.name - }, - dataType: "json" - }); - yield put(rcvUpdateAttachment(response.json)); - } catch(ex) { - console.log(ex); // eslint-disable-line no-console - Alert.error(ex && ex.text || "Unable to save"); - } - yield put(setLoading(REQ_UPDATE_ATTACH, false)); -} - -export default function* () { - yield takeEvery(REQ_UPDATE_ATTACH, updateAttachment); -}; diff --git a/client/src/sagas/Shared/uploadFiles.js b/client/src/sagas/Shared/uploadFiles.js deleted file mode 100644 index 4e4881e..0000000 --- a/client/src/sagas/Shared/uploadFiles.js +++ /dev/null @@ -1,34 +0,0 @@ -import Alert from "react-s-alert"; -import { all, put, takeEvery } from "redux-saga/effects"; - -import request from "utils/Shared/request"; - -import { REQ_UPLOAD_FILES } from "constants/SharedActions"; -import { rcvUploadFiles, setLoading } from "actions/Shared"; - -function* uploadFiles(action) { - const { files } = action; - - yield put(setLoading(REQ_UPLOAD_FILES, true)); - try { - const pArr = files.map(file => { - const data = new FormData(); - data.append("file", file); - return request({ - url: `/api/attachment/`, - type: "post", - data - }); - }); - const results = yield all(pArr); - yield put(rcvUploadFiles(results.map(response => response.json))); - } catch(ex) { - console.log(ex); // eslint-disable-line no-console - Alert.error(ex && ex.text || "Upload failed"); - } - yield put(setLoading(REQ_UPLOAD_FILES, false)); -} - -export default function* () { - yield takeEvery(REQ_UPLOAD_FILES, uploadFiles); -}; diff --git a/client/src/sagas/TestDesign/attachFile.js b/client/src/sagas/TestDesign/attachFile.js deleted file mode 100644 index 6918213..0000000 --- a/client/src/sagas/TestDesign/attachFile.js +++ /dev/null @@ -1,26 +0,0 @@ -import { put, take, takeEvery } from "redux-saga/effects"; - -import { REQ_ATTACH_TO_TC, RCV_TC_SAVE } from "constants/TestDesignActions"; -import { RCV_UPLOAD_FILES } from "constants/SharedActions"; -import { rcvAttachToTestCase, reqSaveTestCase } from "actions/TestDesign"; -import { reqUploadFiles, setLoading } from "actions/Shared"; - - -function* attachToTestCase(action) { - const { testPlanId, testCase, file } = action; - yield put(setLoading(REQ_ATTACH_TO_TC, true)); - // upload file - yield put(reqUploadFiles([ file ])); - const uploadResult = yield take(RCV_UPLOAD_FILES); - // add to testCase - testCase.description.attachments.push(uploadResult.files[0]); - // save testCase - yield put(reqSaveTestCase(testPlanId, testCase)); - const result = yield take(RCV_TC_SAVE); - yield put(rcvAttachToTestCase(uploadResult.files[0], result.testCase)); - yield put(setLoading(REQ_ATTACH_TO_TC, false)); -} - -export default function* () { - yield takeEvery(REQ_ATTACH_TO_TC, attachToTestCase); -}; diff --git a/client/src/sagas/TestDesign/deleteTestCase.js b/client/src/sagas/TestDesign/deleteTestCase.js deleted file mode 100644 index d0632ff..0000000 --- a/client/src/sagas/TestDesign/deleteTestCase.js +++ /dev/null @@ -1,32 +0,0 @@ -import Alert from "react-s-alert"; -import { call, put, takeEvery } from "redux-saga/effects"; - -import request from "utils/Shared/request"; - -import { REQ_DEL_TC } from "constants/TestDesignActions"; -import { rcvDeleteTestCase } from "actions/TestDesign"; -import { redirectToTestDesign, setLoading } from "actions/Shared"; - - -function* deleteTestCase(action) { - const { id, testPlanId } = action; - - yield put(setLoading(REQ_DEL_TC, true)); - try { - yield call(request, { - url: `/api/testcase/${id}`, - type: "delete" - }); - Alert.success("Deleted"); - yield put(rcvDeleteTestCase(id, testPlanId)); - yield put(redirectToTestDesign()); - } catch(ex) { - console.log(ex); // eslint-disable-line no-console - Alert.error(ex.text || "Failed to delete test case"); - } - yield put(setLoading(REQ_DEL_TC, false)); -} - -export default function* () { - yield takeEvery(REQ_DEL_TC, deleteTestCase); -}; diff --git a/client/src/sagas/TestDesign/index.js b/client/src/sagas/TestDesign/index.js deleted file mode 100644 index 89c9342..0000000 --- a/client/src/sagas/TestDesign/index.js +++ /dev/null @@ -1,23 +0,0 @@ -import { spawn } from "redux-saga/effects"; - -import attachFile from "./attachFile"; -import deleteTestCase from "./deleteTestCase"; -import reqTestCase from "./reqTestCase"; -import reqTestCases from "./reqTestCases"; -import reqTestPlans from "./reqTestPlans"; -import saveTestCase from "./saveTestCase"; -import saveTestPlan from "./saveTestPlan"; -import uploadCSV from "./uploadCSV"; - -function* testDesignSaga() { - yield spawn(attachFile); - yield spawn(deleteTestCase); - yield spawn(reqTestCase); - yield spawn(reqTestCases); - yield spawn(reqTestPlans); - yield spawn(saveTestCase); - yield spawn(saveTestPlan); - yield spawn(uploadCSV); -} - -export default testDesignSaga; diff --git a/client/src/sagas/TestDesign/reqTestCase.js b/client/src/sagas/TestDesign/reqTestCase.js deleted file mode 100644 index 9a25a82..0000000 --- a/client/src/sagas/TestDesign/reqTestCase.js +++ /dev/null @@ -1,30 +0,0 @@ -import Alert from "react-s-alert"; -import { call, put, takeEvery } from "redux-saga/effects"; - -import request from "utils/Shared/request"; - -import { REQ_TEST_CASE } from "constants/TestDesignActions"; -import { rcvTestCase } from "actions/TestDesign"; -import { setLoading } from "actions/Shared"; -import parseTestCase from "utils/TestDesign/parseTestCase"; - -function* getTestCases(action) { - const { id } = action; - yield put(setLoading(REQ_TEST_CASE, true)); - try { - const response = yield call(request, { - url: `/api/testcase/${id}`, - dataType: "json" - }); - const testCase = response.json; - yield put(rcvTestCase(parseTestCase(testCase))); - } catch(ex) { - console.log(ex); // eslint-disable-line no-console - Alert.error("Failed to fetch tests"); - } - yield put(setLoading(REQ_TEST_CASE, false)); -} - -export default function* () { - yield takeEvery(REQ_TEST_CASE, getTestCases); -}; diff --git a/client/src/sagas/TestDesign/reqTestCases.js b/client/src/sagas/TestDesign/reqTestCases.js deleted file mode 100644 index 1b6cdcc..0000000 --- a/client/src/sagas/TestDesign/reqTestCases.js +++ /dev/null @@ -1,34 +0,0 @@ -import Alert from "react-s-alert"; -import { call, put, takeEvery } from "redux-saga/effects"; - -import request from "utils/Shared/request"; - -import { REQ_TEST_CASES } from "constants/TestDesignActions"; -import { rcvTestCases } from "actions/TestDesign"; -import { setLoading } from "actions/Shared"; - -function* getTestCases(action) { - const { testPlanId } = action; - if(!testPlanId) - return; - yield put(setLoading(REQ_TEST_CASES, true)); - try { - const response = yield call(request, { - url: "/api/testcase", - type: "get", - data: { - testplan: testPlanId - }, - dataType: "json" - }); - yield put(rcvTestCases(testPlanId, response.json)); - } catch(ex) { - console.log(ex); // eslint-disable-line no-console - Alert.error("Failed to fetch tests. " + (ex && ex.text || "")); - } - yield put(setLoading(REQ_TEST_CASES, false)); -} - -export default function* () { - yield takeEvery(REQ_TEST_CASES, getTestCases); -}; diff --git a/client/src/sagas/TestDesign/reqTestPlans.js b/client/src/sagas/TestDesign/reqTestPlans.js deleted file mode 100644 index 174a785..0000000 --- a/client/src/sagas/TestDesign/reqTestPlans.js +++ /dev/null @@ -1,27 +0,0 @@ -import Alert from "react-s-alert"; -import { call, put, takeEvery } from "redux-saga/effects"; - -import request from "utils/Shared/request"; - -import { REQ_TEST_PLANS } from "constants/TestDesignActions"; -import { rcvTestPlans } from "actions/TestDesign"; -import { setLoading } from "actions/Shared"; - -function* getTestPlans() { - yield put(setLoading(REQ_TEST_PLANS, true)); - try { - const response = yield call(request, { - url: "/api/testplan", - dataType: "json" - }); - yield put(rcvTestPlans(response.json)); - } catch(ex) { - console.log(ex); // eslint-disable-line no-console - Alert.error("Failed to fetch test plans. " + (ex && ex.text || "")); - } - yield put(setLoading(REQ_TEST_PLANS, false)); -} - -export default function* () { - yield takeEvery(REQ_TEST_PLANS, getTestPlans); -}; diff --git a/client/src/sagas/TestDesign/saveTestCase.js b/client/src/sagas/TestDesign/saveTestCase.js deleted file mode 100644 index 0501b5d..0000000 --- a/client/src/sagas/TestDesign/saveTestCase.js +++ /dev/null @@ -1,65 +0,0 @@ -import Alert from "react-s-alert"; -import { call, put, take, takeEvery } from "redux-saga/effects"; - -import request from "utils/Shared/request"; - -import { REQ_TC_SAVE } from "constants/TestDesignActions"; -import { RCV_UPLOAD_FILES } from "constants/SharedActions"; - -import { rcvSaveTestCase } from "actions/TestDesign"; -import { redirectToTestDesign, reqUploadFiles, setLoading } from "actions/Shared"; - -import validateTestCase from "utils/TestDesign/validateTestCase"; -import buildTestCase from "utils/TestDesign/buildTestCase"; -import parseTestCase from "utils/TestDesign/parseTestCase"; - - -function* saveTestCase(action) { - const { testPlanId, testCase, files, redirect } = action; - if(!testPlanId) { - Alert.error("testPlanId not found"); - return; - } - const validationResult = validateTestCase(testCase); - if(!validationResult.valid) { - Alert.error(validationResult.error); - return; - } - yield put(setLoading(REQ_TC_SAVE, true)); - try { - let response; - if(testCase.id) { - response = yield call(request, { - url: `/api/testcase/${testCase.id}`, - type: "put", - data: buildTestCase(testCase), - dataType: "json" - }); - } else { - // upload files, if any - if(files.length) { - yield put(reqUploadFiles(files)); - const uploadResult = yield take(RCV_UPLOAD_FILES); - testCase.description.attachments = uploadResult.files; - } - response = yield call(request, { - url: `/api/testcase?testplan=${testPlanId}`, - type: "post", - data: buildTestCase(testCase), - dataType: "json" - }); - } - Alert.success("Saved"); - yield put(rcvSaveTestCase(testPlanId, parseTestCase(response.json))); - if(redirect) - yield put(redirectToTestDesign()); - } catch(ex) { - console.log(ex); // eslint-disable-line no-console - Alert.error("Failed to save test case"); - } - yield put(setLoading(REQ_TC_SAVE, false)); -} - -export default function* () { - yield takeEvery(REQ_TC_SAVE, saveTestCase); -}; diff --git a/client/src/sagas/TestDesign/saveTestPlan.js b/client/src/sagas/TestDesign/saveTestPlan.js deleted file mode 100644 index 7e73fcd..0000000 --- a/client/src/sagas/TestDesign/saveTestPlan.js +++ /dev/null @@ -1,50 +0,0 @@ -import Alert from "react-s-alert"; -import { call, put, takeEvery } from "redux-saga/effects"; - -import request from "utils/Shared/request"; - -import { REQ_TP_SAVE } from "constants/TestDesignActions"; -import { rcvSaveTestPlan } from "actions/TestDesign"; -// import { redirectToTestDesign } from "actions/Shared"; -import { setLoading } from "actions/Shared"; - -import validateTestPlan from "utils/TestDesign/validateTestPlan"; -import buildTestPlan from "utils/TestDesign/buildTestPlan"; - -function* saveTestPlan(action) { - const { testPlan } = action; - const validationResult = validateTestPlan(testPlan); - if(!validationResult.valid) { - Alert.error(validationResult.error); - return; - } - yield put(setLoading(REQ_TP_SAVE, true)); - try { - let response; - if(testPlan.id) { - response = yield call(request, { - url: `/api/testplan/${testPlan.id}`, - type: "put", - data: buildTestPlan(testPlan), - dataType: "json" - }); - } else { - response = yield call(request, { - url: "/api/testplan", - type: "post", - data: buildTestPlan(testPlan), - dataType: "json" - }); - } - Alert.success("Saved"); - yield put(rcvSaveTestPlan(response.json)); - } catch(ex) { - console.log(ex); // eslint-disable-line no-console - Alert.error("Failed to save test plan"); - } - yield put(setLoading(REQ_TP_SAVE, false)); -} - -export default function* () { - yield takeEvery(REQ_TP_SAVE, saveTestPlan); -}; diff --git a/client/src/sagas/TestDesign/uploadCSV.js b/client/src/sagas/TestDesign/uploadCSV.js deleted file mode 100644 index 9b26ec5..0000000 --- a/client/src/sagas/TestDesign/uploadCSV.js +++ /dev/null @@ -1,37 +0,0 @@ -import Alert from "react-s-alert"; -import request from "utils/Shared/request"; -import { call, put, takeEvery } from "redux-saga/effects"; - -import { REQ_UPLOAD_TESTS } from "constants/TestDesignActions"; -import { rcvUploadTests } from "actions/TestDesign"; -import { setLoading } from "actions/Shared"; - -export function* uploadTests(action) { - const { testPlanId, file } = action; - if(!testPlanId) { - Alert.error("testPlanId not found"); - return; - } - const handle = Alert.info("Uploading"); - const data = new FormData(); - data.append("file", file); - try { - yield put(setLoading(REQ_UPLOAD_TESTS, true)); - const response = yield call(request, { - url: `/api/testcase/upload?testplan=${testPlanId}`, - type: "post", - data - }); - Alert.success("Upload complete"); - yield put(rcvUploadTests(testPlanId, response.json)); - } catch (ex) { - console.log("Failed to upload tests: ", ex); // eslint-disable-line no-console - Alert.error(ex.errors && ex.errors[0] || ex.statusText || "Unable to upload CSV"); - } - Alert.close(handle); - yield put(setLoading(REQ_UPLOAD_TESTS, false)); -} - -export default function* () { - yield takeEvery(REQ_UPLOAD_TESTS, uploadTests); -}; diff --git a/client/src/sagas/TestSelector/importTestCases.js b/client/src/sagas/TestSelector/importTestCases.js deleted file mode 100644 index a09f24f..0000000 --- a/client/src/sagas/TestSelector/importTestCases.js +++ /dev/null @@ -1,49 +0,0 @@ -import Alert from "react-s-alert"; -import { put, take, takeEvery } from "redux-saga/effects"; - -import { REQ_IMPORT_TESTS } from "constants/TestSelectorActions"; -import { RCV_TEST_CASES } from "constants/TestDesignActions"; - -import { rcvImportTests } from "actions/TestSelector"; -import { reqTestCases } from "actions/TestDesign"; -import { setLoading } from "actions/Shared"; - -function* importTests(action) { - const { selectedItems, importActionContract } = action; - yield put(setLoading(REQ_IMPORT_TESTS, true)); - const tests = []; - try { - for(let i=0; i<selectedItems.length; i++) { - const item = selectedItems[i]; - if(item.hasChildren) { - // testplan - yield put(reqTestCases(item.id)); - const { testPlanId, testCases } = yield take(RCV_TEST_CASES); - if(item.id == testPlanId) { - testCases.forEach(tc => tests.push(tc)); - } - } else { - // single test case - tests.push(item); - } - } - yield put(setLoading(REQ_IMPORT_TESTS, false)); - if(importActionContract) { - yield put({ - type: importActionContract.type, - [importActionContract.key]: tests, - ...importActionContract.extra - }); - } - yield put(rcvImportTests(tests)); - // Alert.success(`Imported ${tests.length} tests`); - } catch(ex) { - console.log(ex); // eslint-disable-line no-console - Alert.error("Failed to import test. " + (ex && ex.text || "")); - yield put(setLoading(REQ_IMPORT_TESTS, false)); - } -} - -export default function* () { - yield takeEvery(REQ_IMPORT_TESTS, importTests); -}; diff --git a/client/src/sagas/TestSelector/index.js b/client/src/sagas/TestSelector/index.js deleted file mode 100644 index b08e2e1..0000000 --- a/client/src/sagas/TestSelector/index.js +++ /dev/null @@ -1,12 +0,0 @@ -import { spawn } from "redux-saga/effects"; - -import importTestCases from "./importTestCases"; -import reqSelectorItems from "./reqSelectorItems"; - -function* testSelectorSaga() { - yield spawn(importTestCases); - // yield spawn(reqInitialSelectorData); - yield spawn(reqSelectorItems); -} - -export default testSelectorSaga; diff --git a/client/src/sagas/TestSelector/reqSelectorItems.js b/client/src/sagas/TestSelector/reqSelectorItems.js deleted file mode 100644 index 5915d45..0000000 --- a/client/src/sagas/TestSelector/reqSelectorItems.js +++ /dev/null @@ -1,30 +0,0 @@ -import { put, take, takeEvery } from "redux-saga/effects"; - -import { REQ_ITEMS } from "constants/TestSelectorActions"; -import { RCV_TEST_PLANS, RCV_TEST_CASES } from "constants/TestDesignActions"; -import { rcvItems } from "actions/TestSelector"; -import { reqTestPlans, reqTestCases } from "actions/TestDesign"; -import { setLoading } from "actions/Shared"; -import parseSelectorData from "utils/TestSelector/parseSelectorData"; -import cache from "utils/TestSelector/TestSelectorCache"; - -function* getItems(action) { - const { path } = action; - let { selectedItems } = action; - if(path.length==0) { - yield put(reqTestPlans()); - const { testPlans } = yield take(RCV_TEST_PLANS); - cache.save(parseSelectorData(testPlans, path)); - } else { - yield put(reqTestCases(path[0].id)); - const { testCases } = yield take(RCV_TEST_CASES); - cache.save(parseSelectorData(testCases, path), path); - } - if(selectedItems.length) - selectedItems = cache.addDetails(selectedItems); - yield put(rcvItems(cache.getByPath(path), selectedItems)); - yield put(setLoading(REQ_ITEMS, false)); -} -export default function* () { - yield takeEvery(REQ_ITEMS, getItems); -} diff --git a/client/src/sagas/index.js b/client/src/sagas/index.js deleted file mode 100644 index 7248814..0000000 --- a/client/src/sagas/index.js +++ /dev/null @@ -1,21 +0,0 @@ -import { spawn } from "redux-saga/effects"; - -import dashboardSaga from "./Dashboard"; -import defectsSaga from "./Defects"; -import defectSelectorSaga from "./DefectSelector"; -import execCyclesSaga from "./ExecCycle"; -import sharedSaga from "sagas/Shared"; -import testDesignSaga from "./TestDesign"; -import testSelectorSaga from "./TestSelector"; - -function* rootSaga() { - yield spawn(dashboardSaga); - yield spawn(defectsSaga); - yield spawn(defectSelectorSaga); - yield spawn(execCyclesSaga); - yield spawn(sharedSaga); - yield spawn(testDesignSaga); - yield spawn(testSelectorSaga); -} - -export default rootSaga; diff --git a/client/src/sass/_bootstrap_variables.scss b/client/src/sass/_bootstrap_variables.scss deleted file mode 100644 index ddc1545..0000000 --- a/client/src/sass/_bootstrap_variables.scss +++ /dev/null @@ -1,870 +0,0 @@ -$bootstrap-sass-asset-helper: false !default; -// Sandstone 3.3.7 -// Variables -// -------------------------------------------------- - - -//== Colors -// -//## Gray and brand colors for use across Bootstrap. - -$gray-base: #000 !default; -$gray-darker: #3E3F3A !default; -$gray-dark: #8E8C84 !default; -$gray: #98978B !default; -$gray-light: #DFD7CA !default; -$gray-lighter: #F8F5F0 !default; - -$brand-primary: #325D88 !default; -$brand-success: #93C54B !default; -$brand-info: #29ABE0 !default; -$brand-warning: #F47C3C !default; -$brand-danger: #d9534f !default; - - -//== Scaffolding -// -//## Settings for some of the most global styles. - -//** Background color for `<body>`. -$body-bg: #fff !default; -//** Global text color on `<body>`. -$text-color: #3E3F3A !default; - -//** Global textual link color. -$link-color: $brand-success !default; -//** Link hover color set via `darken()` function. -$link-hover-color: darken($link-color, 10%) !default; -//** Link hover decoration. -$link-hover-decoration: underline !default; - - -//== Typography -// -//## Font, line-height, and color for body text, headings, and more. - -$font-family-sans-serif: "Roboto", "Helvetica Neue", Helvetica, Arial, sans-serif !default; -$font-family-serif: Georgia, "Times New Roman", Times, serif !default; -//** Default monospace fonts for `<code>`, `<kbd>`, and `<pre>`. -$font-family-monospace: Menlo, Monaco, Consolas, "Courier New", monospace !default; -$font-family-base: $font-family-sans-serif !default; - -$font-size-base: 14px !default; -$font-size-large: ceil(($font-size-base * 1.25)) !default; // ~18px -$font-size-small: ceil(($font-size-base * 0.85)) !default; // ~12px - -$font-size-h1: floor(($font-size-base * 2.6)) !default; // ~36px -$font-size-h2: floor(($font-size-base * 2.15)) !default; // ~30px -$font-size-h3: ceil(($font-size-base * 1.7)) !default; // ~24px -$font-size-h4: ceil(($font-size-base * 1.25)) !default; // ~18px -$font-size-h5: $font-size-base !default; -$font-size-h6: ceil(($font-size-base * 0.85)) !default; // ~12px - -//** Unit-less `line-height` for use in components like buttons. -$line-height-base: 1.428571429 !default; // 20/14 -//** Computed "line-height" (`font-size` * `line-height`) for use with `margin`, `padding`, etc. -$line-height-computed: floor(($font-size-base * $line-height-base)) !default; // ~20px - -//** By default, this inherits from the `<body>`. -$headings-font-family: inherit !default; -$headings-font-weight: 400 !default; -$headings-line-height: 1.1 !default; -$headings-color: inherit !default; - - -//== Iconography -// -//## Specify custom location and filename of the included Glyphicons icon font. Useful for those including Bootstrap via Bower. - -//** Load fonts from this directory. -$icon-font-path: if($bootstrap-sass-asset-helper, "bootstrap/", "../fonts/bootstrap/") !default; -//** File name for all font files. -$icon-font-name: "glyphicons-halflings-regular" !default; -//** Element ID within SVG icon file. -$icon-font-svg-id: "glyphicons_halflingsregular" !default; - - -//== Components -// -//## Define common padding and border radius sizes and more. Values based on 14px text and 1.428 line-height (~20px to start). - -$padding-base-vertical: 12px !default; -$padding-base-horizontal: 16px !default; - -$padding-large-vertical: 20px !default; -$padding-large-horizontal: 30px !default; - -$padding-small-vertical: 5px !default; -$padding-small-horizontal: 10px !default; - -$padding-xs-vertical: 1px !default; -$padding-xs-horizontal: 5px !default; - -$line-height-large: 1.3333333 !default; // extra decimals for Win 8.1 Chrome -$line-height-small: 1.5 !default; - -$border-radius-base: 4px !default; -$border-radius-large: 6px !default; -$border-radius-small: 3px !default; - -//** Global color for active items (e.g., navs or dropdowns). -$component-active-color: $gray !default; -//** Global background color for active items (e.g., navs or dropdowns). -$component-active-bg: $gray-lighter !default; - -//** Width of the `border` for generating carets that indicate dropdowns. -$caret-width-base: 4px !default; -//** Carets increase slightly in size for larger components. -$caret-width-large: 5px !default; - - -//== Tables -// -//## Customizes the `.table` component with basic values, each used across all table variations. - -//** Padding for `<th>`s and `<td>`s. -$table-cell-padding: 8px !default; -//** Padding for cells in `.table-condensed`. -$table-condensed-cell-padding: 5px !default; - -//** Default background color used for all tables. -$table-bg: transparent !default; -//** Background color used for `.table-striped`. -$table-bg-accent: $gray-lighter !default; -//** Background color used for `.table-hover`. -$table-bg-hover: $gray-lighter !default; -$table-bg-active: $table-bg-hover !default; - -//** Border color for table and cell borders. -$table-border-color: $gray-light !default; - - -//== Buttons -// -//## For each of Bootstrap's buttons, define text, background and border color. - -$btn-font-weight: normal !default; - -$btn-default-color: #fff !default; -$btn-default-bg: $gray-darker !default; -$btn-default-border: transparent !default; - -$btn-primary-color: #fff !default; -$btn-primary-bg: $brand-primary !default; -$btn-primary-border: transparent !default; - -$btn-success-color: #fff !default; -$btn-success-bg: $brand-success !default; -$btn-success-border: transparent !default; - -$btn-info-color: #fff !default; -$btn-info-bg: $brand-info !default; -$btn-info-border: transparent !default; - -$btn-warning-color: #fff !default; -$btn-warning-bg: $brand-warning !default; -$btn-warning-border: transparent !default; - -$btn-danger-color: #fff !default; -$btn-danger-bg: $brand-danger !default; -$btn-danger-border: transparent !default; - -$btn-link-disabled-color: $gray-light !default; - -// Allows for customizing button radius independently from global border radius -$btn-border-radius-base: $border-radius-base !default; -$btn-border-radius-large: $border-radius-large !default; -$btn-border-radius-small: $border-radius-small !default; - - -//== Forms -// -//## - -//** `<input>` background color -$input-bg: #fff !default; -//** `<input disabled>` background color -$input-bg-disabled: $gray-lighter !default; - -//** Text color for `<input>`s -$input-color: $text-color !default; -//** `<input>` border color -$input-border: $gray-light !default; - -// TODO: Rename `$input-border-radius` to `$input-border-radius-base` in v4 -//** Default `.form-control` border radius -// This has no effect on `<select>`s in some browsers, due to the limited stylability of `<select>`s in CSS. -$input-border-radius: $border-radius-base !default; -//** Large `.form-control` border radius -$input-border-radius-large: $border-radius-large !default; -//** Small `.form-control` border radius -$input-border-radius-small: $border-radius-small !default; - -//** Border color for inputs on focus -$input-border-focus: transparent !default; - -//** Placeholder text color -$input-color-placeholder: $gray-light !default; - -//** Default `.form-control` height -$input-height-base: ($line-height-computed + ($padding-base-vertical * 2) + 2) !default; -//** Large `.form-control` height -$input-height-large: (ceil($font-size-large * $line-height-large) + ($padding-large-vertical * 2) + 2) !default; -//** Small `.form-control` height -$input-height-small: (floor($font-size-small * $line-height-small) + ($padding-small-vertical * 2) + 2) !default; - -//** `.form-group` margin -$form-group-margin-bottom: 15px !default; - -$legend-color: $headings-color !default; -$legend-border-color: transparent !default; - -//** Background color for textual input addons -$input-group-addon-bg: $gray-lighter !default; -//** Border color for textual input addons -$input-group-addon-border-color: $input-border !default; - -//** Disabled cursor for form controls and buttons. -$cursor-disabled: not-allowed !default; - - -//== Dropdowns -// -//## Dropdown menu container and contents. - -//** Background for the dropdown menu. -$dropdown-bg: #fff !default; -//** Dropdown menu `border-color`. -$dropdown-border: $gray-light !default; -//** Dropdown menu `border-color` **for IE8**. -$dropdown-fallback-border: $gray-light !default; -//** Divider color for between dropdown items. -$dropdown-divider-bg: $gray-lighter !default; - -//** Dropdown link text color. -$dropdown-link-color: $gray !default; -//** Hover color for dropdown links. -$dropdown-link-hover-color: $dropdown-link-color !default; -//** Hover background for dropdown links. -$dropdown-link-hover-bg: $gray-lighter !default; - -//** Active dropdown menu item text color. -$dropdown-link-active-color: $component-active-color !default; -//** Active dropdown menu item background color. -$dropdown-link-active-bg: $component-active-bg !default; - -//** Disabled dropdown menu item background color. -$dropdown-link-disabled-color: $gray-light !default; - -//** Text color for headers within dropdown menus. -$dropdown-header-color: $gray-light !default; - -//** Deprecated `$dropdown-caret-color` as of v3.1.0 -$dropdown-caret-color: #000 !default; - - -//-- Z-index master list -// -// Warning: Avoid customizing these values. They're used for a bird's eye view -// of components dependent on the z-axis and are designed to all work together. -// -// Note: These variables are not generated into the Customizer. - -$zindex-navbar: 1000 !default; -$zindex-dropdown: 1000 !default; -$zindex-popover: 1060 !default; -$zindex-tooltip: 1070 !default; -$zindex-navbar-fixed: 1030 !default; -$zindex-modal-background: 1040 !default; -$zindex-modal: 1050 !default; - - -//== Media queries breakpoints -// -//## Define the breakpoints at which your layout will change, adapting to different screen sizes. - -// Extra small screen / phone -//** Deprecated `$screen-xs` as of v3.0.1 -$screen-xs: 480px !default; -//** Deprecated `$screen-xs-min` as of v3.2.0 -$screen-xs-min: $screen-xs !default; -//** Deprecated `$screen-phone` as of v3.0.1 -$screen-phone: $screen-xs-min !default; - -// Small screen / tablet -//** Deprecated `$screen-sm` as of v3.0.1 -$screen-sm: 768px !default; -$screen-sm-min: $screen-sm !default; -//** Deprecated `$screen-tablet` as of v3.0.1 -$screen-tablet: $screen-sm-min !default; - -// Medium screen / desktop -//** Deprecated `$screen-md` as of v3.0.1 -$screen-md: 992px !default; -$screen-md-min: $screen-md !default; -//** Deprecated `$screen-desktop` as of v3.0.1 -$screen-desktop: $screen-md-min !default; - -// Large screen / wide desktop -//** Deprecated `$screen-lg` as of v3.0.1 -$screen-lg: 1200px !default; -$screen-lg-min: $screen-lg !default; -//** Deprecated `$screen-lg-desktop` as of v3.0.1 -$screen-lg-desktop: $screen-lg-min !default; - -// So media queries don't overlap when required, provide a maximum -$screen-xs-max: ($screen-sm-min - 1) !default; -$screen-sm-max: ($screen-md-min - 1) !default; -$screen-md-max: ($screen-lg-min - 1) !default; - - -//== Grid system -// -//## Define your custom responsive grid. - -//** Number of columns in the grid. -$grid-columns: 12 !default; -//** Padding between columns. Gets divided in half for the left and right. -$grid-gutter-width: 30px !default; -// Navbar collapse -//** Point at which the navbar becomes uncollapsed. -$grid-float-breakpoint: $screen-sm-min !default; -//** Point at which the navbar begins collapsing. -$grid-float-breakpoint-max: ($grid-float-breakpoint - 1) !default; - - -//== Container sizes -// -//## Define the maximum width of `.container` for different screen sizes. - -// Small screen / tablet -$container-tablet: (720px + $grid-gutter-width) !default; -//** For `$screen-sm-min` and up. -$container-sm: $container-tablet !default; - -// Medium screen / desktop -$container-desktop: (940px + $grid-gutter-width) !default; -//** For `$screen-md-min` and up. -$container-md: $container-desktop !default; - -// Large screen / wide desktop -$container-large-desktop: (1140px + $grid-gutter-width) !default; -//** For `$screen-lg-min` and up. -$container-lg: $container-large-desktop !default; - - -//== Navbar -// -//## - -// Basics of a navbar -$navbar-height: 60px !default; -$navbar-margin-bottom: $line-height-computed !default; -$navbar-border-radius: $border-radius-base !default; -$navbar-padding-horizontal: floor(($grid-gutter-width / 2)) !default; -$navbar-padding-vertical: (($navbar-height - $line-height-computed) / 2) !default; -$navbar-collapse-max-height: 340px !default; - -$navbar-default-color: $gray-dark !default; -$navbar-default-bg: $gray-darker !default; -$navbar-default-border: $gray-darker !default; - -// Navbar links -$navbar-default-link-color: $gray !default; -$navbar-default-link-hover-color: #fff !default; -$navbar-default-link-hover-bg: transparent !default; -$navbar-default-link-active-color: #fff !default; -$navbar-default-link-active-bg: darken($navbar-default-bg, 2%) !default; -$navbar-default-link-disabled-color: #ccc !default; -$navbar-default-link-disabled-bg: transparent !default; - -// Navbar brand label -$navbar-default-brand-color: #fff !default; -$navbar-default-brand-hover-color: #fff !default; -$navbar-default-brand-hover-bg: transparent !default; - -// Navbar toggle -$navbar-default-toggle-hover-bg: $navbar-default-link-active-bg !default; -$navbar-default-toggle-icon-bar-bg: $navbar-default-link-color !default; -$navbar-default-toggle-border-color: transparent !default; - - -//=== Inverted navbar -// Reset inverted navbar basics -$navbar-inverse-color: $gray-light !default; -$navbar-inverse-bg: $brand-success !default; -$navbar-inverse-border: $brand-success !default; - -// Inverted navbar links -$navbar-inverse-link-color: darken($brand-success, 15%) !default; -$navbar-inverse-link-hover-color: #fff !default; -$navbar-inverse-link-hover-bg: transparent !default; -$navbar-inverse-link-active-color: $navbar-inverse-link-hover-color !default; -$navbar-inverse-link-active-bg: darken($navbar-inverse-bg, 4%) !default; -$navbar-inverse-link-disabled-color: #444 !default; -$navbar-inverse-link-disabled-bg: transparent !default; - -// Inverted navbar brand label -$navbar-inverse-brand-color: #fff !default; -$navbar-inverse-brand-hover-color: #fff !default; -$navbar-inverse-brand-hover-bg: transparent !default; - -// Inverted navbar toggle -$navbar-inverse-toggle-hover-bg: $navbar-inverse-link-active-bg !default; -$navbar-inverse-toggle-icon-bar-bg: $navbar-inverse-link-color !default; -$navbar-inverse-toggle-border-color: transparent !default; - - -//== Navs -// -//## - -//=== Shared nav styles -$nav-link-padding: 10px 15px !default; -$nav-link-hover-bg: $gray-lighter !default; - -$nav-disabled-link-color: $gray-light !default; -$nav-disabled-link-hover-color: $gray-light !default; - -//== Tabs -$nav-tabs-border-color: $gray-light !default; - -$nav-tabs-link-hover-border-color: $gray-light !default; - -$nav-tabs-active-link-hover-bg: $body-bg !default; -$nav-tabs-active-link-hover-color: $gray !default; -$nav-tabs-active-link-hover-border-color: $gray-light !default; - -$nav-tabs-justified-link-border-color: $gray-light !default; -$nav-tabs-justified-active-link-border-color: $body-bg !default; - -//== Pills -$nav-pills-border-radius: $border-radius-base !default; -$nav-pills-active-link-hover-bg: $component-active-bg !default; -$nav-pills-active-link-hover-color: $component-active-color !default; - - -//== Pagination -// -//## - -$pagination-color: $gray !default; -$pagination-bg: $gray-lighter !default; -$pagination-border: $gray-light !default; - -$pagination-hover-color: $gray-dark !default; -$pagination-hover-bg: $gray-light !default; -$pagination-hover-border: $pagination-border !default; - -$pagination-active-color: $gray-dark !default; -$pagination-active-bg: $gray-light !default; -$pagination-active-border: $gray-light !default; - -$pagination-disabled-color: $gray-light !default; -$pagination-disabled-bg: $gray-lighter !default; -$pagination-disabled-border: $gray-light !default; - - -//== Pager -// -//## - -$pager-bg: $pagination-bg !default; -$pager-border: $pagination-border !default; -$pager-border-radius: 15px !default; - -$pager-hover-bg: $pagination-hover-bg !default; - -$pager-active-bg: $pagination-active-bg !default; -$pager-active-color: $pagination-active-color !default; - -$pager-disabled-color: $pagination-disabled-color !default; - - -//== Jumbotron -// -//## - -$jumbotron-padding: 30px !default; -$jumbotron-color: inherit !default; -$jumbotron-bg: $gray-lighter !default; -$jumbotron-heading-color: inherit !default; -$jumbotron-font-size: ceil(($font-size-base * 1.5)) !default; -$jumbotron-heading-font-size: ceil(($font-size-base * 4.5)) !default; - - -//== Form states and alerts -// -//## Define colors for form feedback states and, by default, alerts. - -$state-success-text: $brand-success !default; -$state-success-bg: #dff0d8 !default; -$state-success-border: darken(adjust-hue($state-success-bg, -10), 5%) !default; - -$state-info-text: $brand-info !default; -$state-info-bg: #d9edf7 !default; -$state-info-border: darken(adjust-hue($state-info-bg, -10), 7%) !default; - -$state-warning-text: $brand-warning !default; -$state-warning-bg: #fcf8e3 !default; -$state-warning-border: darken(adjust-hue($state-warning-bg, -10), 5%) !default; - -$state-danger-text: $brand-danger !default; -$state-danger-bg: #f2dede !default; -$state-danger-border: darken(adjust-hue($state-danger-bg, -10), 5%) !default; - - -//== Tooltips -// -//## - -//** Tooltip max width -$tooltip-max-width: 200px !default; -//** Tooltip text color -$tooltip-color: #fff !default; -//** Tooltip background color -$tooltip-bg: $navbar-default-bg !default; -$tooltip-opacity: 1 !default; - -//** Tooltip arrow width -$tooltip-arrow-width: 5px !default; -//** Tooltip arrow color -$tooltip-arrow-color: $tooltip-bg !default; - - -//== Popovers -// -//## - -//** Popover body background color -$popover-bg: #fff !default; -//** Popover maximum width -$popover-max-width: 276px !default; -//** Popover border color -$popover-border-color: $gray-light !default; -//** Popover fallback border color -$popover-fallback-border-color: $gray-light !default; - -//** Popover title background color -$popover-title-bg: $gray-lighter !default; - -//** Popover arrow width -$popover-arrow-width: 10px !default; -//** Popover arrow color -$popover-arrow-color: $popover-bg !default; - -//** Popover outer arrow width -$popover-arrow-outer-width: ($popover-arrow-width + 1) !default; -//** Popover outer arrow color -$popover-arrow-outer-color: fadein($popover-border-color, 5%) !default; -//** Popover outer arrow fallback color -$popover-arrow-outer-fallback-color: darken($popover-fallback-border-color, 20%) !default; - - -//== Labels -// -//## - -//** Default label background color -$label-default-bg: $navbar-default-bg !default; -//** Primary label background color -$label-primary-bg: $brand-primary !default; -//** Success label background color -$label-success-bg: $brand-success !default; -//** Info label background color -$label-info-bg: $brand-info !default; -//** Warning label background color -$label-warning-bg: $brand-warning !default; -//** Danger label background color -$label-danger-bg: $brand-danger !default; - -//** Default label text color -$label-color: #fff !default; -//** Default text color of a linked label -$label-link-hover-color: #fff !default; - - -//== Modals -// -//## - -//** Padding applied to the modal body -$modal-inner-padding: 15px !default; - -//** Padding applied to the modal title -$modal-title-padding: 15px !default; -//** Modal title line-height -$modal-title-line-height: $line-height-base !default; - -//** Background color of modal content area -$modal-content-bg: #fff !default; -//** Modal content border color -$modal-content-border-color: $gray-lighter !default; -//** Modal content border color **for IE8** -$modal-content-fallback-border-color: $gray-lighter !default; - -//** Modal backdrop background color -$modal-backdrop-bg: #000 !default; -//** Modal backdrop opacity -$modal-backdrop-opacity: .5 !default; -//** Modal header border color -$modal-header-border-color: $gray-lighter !default; -//** Modal footer border color -$modal-footer-border-color: $modal-header-border-color !default; - -$modal-lg: 900px !default; -$modal-md: 600px !default; -$modal-sm: 300px !default; - - -//== Alerts -// -//## Define alert colors, border radius, and padding. - -$alert-padding: 15px !default; -$alert-border-radius: $border-radius-base !default; -$alert-link-font-weight: bold !default; - -$alert-success-bg: $brand-success !default; -$alert-success-text: #fff !default; -$alert-success-border: transparent !default; - -$alert-info-bg: $brand-info !default; -$alert-info-text: #fff !default; -$alert-info-border: transparent !default; - -$alert-warning-bg: $brand-warning !default; -$alert-warning-text: #fff !default; -$alert-warning-border: transparent !default; - -$alert-danger-bg: $brand-danger !default; -$alert-danger-text: #fff !default; -$alert-danger-border: transparent !default; - - -//== Progress bars -// -//## - -//** Background color of the whole progress component -$progress-bg: #f5f5f5 !default; -//** Progress bar text color -$progress-bar-color: #fff !default; -//** Variable for setting rounded corners on progress bar. -$progress-border-radius: $border-radius-base !default; - -//** Default progress bar color -$progress-bar-bg: $brand-primary !default; -//** Success progress bar color -$progress-bar-success-bg: $brand-success !default; -//** Warning progress bar color -$progress-bar-warning-bg: $brand-warning !default; -//** Danger progress bar color -$progress-bar-danger-bg: $brand-danger !default; -//** Info progress bar color -$progress-bar-info-bg: $brand-info !default; - - -//== List group -// -//## - -//** Background color on `.list-group-item` -$list-group-bg: #fff !default; -//** `.list-group-item` border color -$list-group-border: $gray-light !default; -//** List group border radius -$list-group-border-radius: $border-radius-base !default; - -//** Background color of single list items on hover -$list-group-hover-bg: $gray-lighter !default; -//** Text color of active list items -$list-group-active-color: $text-color !default; -//** Background color of active list items -$list-group-active-bg: $component-active-bg !default; -//** Border color of active list elements -$list-group-active-border: $list-group-border !default; -//** Text color for content within active list items -$list-group-active-text-color: $text-color !default; - -//** Text color of disabled list items -$list-group-disabled-color: $gray-light !default; -//** Background color of disabled list items -$list-group-disabled-bg: $gray-lighter !default; -//** Text color for content within disabled list items -$list-group-disabled-text-color: $list-group-disabled-color !default; - -$list-group-link-color: $text-color !default; -$list-group-link-hover-color: $list-group-link-color !default; -$list-group-link-heading-color: $headings-color !default; - - -//== Panels -// -//## - -$panel-bg: #fff !default; -$panel-body-padding: 15px !default; -$panel-heading-padding: 10px 15px !default; -$panel-footer-padding: $panel-heading-padding !default; -$panel-border-radius: $border-radius-base !default; - -//** Border color for elements within panels -$panel-inner-border: $gray-light !default; -$panel-footer-bg: $gray-lighter !default; - -$panel-default-text: $text-color !default; -$panel-default-border: $gray-light !default; -$panel-default-heading-bg: $gray-lighter !default; - -$panel-primary-text: #fff !default; -$panel-primary-border: $brand-primary !default; -$panel-primary-heading-bg: $brand-primary !default; - -$panel-success-text: $state-success-text !default; -$panel-success-border: $state-success-border !default; -$panel-success-heading-bg: $brand-success !default; - -$panel-info-text: $state-info-text !default; -$panel-info-border: $state-info-border !default; -$panel-info-heading-bg: $brand-info !default; - -$panel-warning-text: $state-warning-text !default; -$panel-warning-border: $state-warning-border !default; -$panel-warning-heading-bg: $brand-warning !default; - -$panel-danger-text: $state-danger-text !default; -$panel-danger-border: $state-danger-border !default; -$panel-danger-heading-bg: $brand-danger !default; - - -//== Thumbnails -// -//## - -//** Padding around the thumbnail image -$thumbnail-padding: 4px !default; -//** Thumbnail background color -$thumbnail-bg: $gray-lighter !default; -//** Thumbnail border color -$thumbnail-border: $gray-light !default; -//** Thumbnail border radius -$thumbnail-border-radius: $border-radius-base !default; - -//** Custom text color for thumbnail captions -$thumbnail-caption-color: $text-color !default; -//** Padding around the thumbnail caption -$thumbnail-caption-padding: 9px !default; - - -//== Wells -// -//## - -$well-bg: $gray-lighter !default; -$well-border: transparent !default; - - -//== Badges -// -//## - -$badge-color: #fff !default; -//** Linked badge text color on hover -$badge-link-hover-color: #fff !default; -$badge-bg: $brand-success !default; - -//** Badge text color in active nav link -$badge-active-color: #fff !default; -//** Badge background color in active nav link -$badge-active-bg: $brand-success !default; - -$badge-font-weight: normal !default; -$badge-line-height: 1 !default; -$badge-border-radius: 10px !default; - - -//== Breadcrumbs -// -//## - -$breadcrumb-padding-vertical: 8px !default; -$breadcrumb-padding-horizontal: 15px !default; -//** Breadcrumb background color -$breadcrumb-bg: $gray-lighter !default; -//** Breadcrumb text color -$breadcrumb-color: $gray-light !default; -//** Text color of current page in the breadcrumb -$breadcrumb-active-color: $gray !default; -//** Textual separator for between breadcrumb elements -$breadcrumb-separator: "/" !default; - - -//== Carousel -// -//## - -$carousel-text-shadow: 0 1px 2px rgba(0,0,0,.6) !default; - -$carousel-control-color: #fff !default; -$carousel-control-width: 15% !default; -$carousel-control-opacity: .5 !default; -$carousel-control-font-size: 20px !default; - -$carousel-indicator-active-bg: #fff !default; -$carousel-indicator-border-color: #fff !default; - -$carousel-caption-color: #fff !default; - - -//== Close -// -//## - -$close-font-weight: bold !default; -$close-color: #000 !default; -$close-text-shadow: 0 0 0 transparent !default; - - -//== Code -// -//## - -$code-color: #c7254e !default; -$code-bg: #f9f2f4 !default; - -$kbd-color: #fff !default; -$kbd-bg: #333 !default; - -$pre-bg: #f5f5f5 !default; -$pre-color: $gray-dark !default; -$pre-border-color: #ccc !default; -$pre-scrollable-max-height: 340px !default; - - -//== Type -// -//## - -//** Horizontal offset for forms and lists. -$component-offset-horizontal: 180px !default; -//** Text muted color -$text-muted: $gray !default; -//** Abbreviations and acronyms border color -$abbr-border-color: $gray !default; -//** Headings small color -$headings-small-color: $gray !default; -//** Blockquote small color -$blockquote-small-color: $text-color !default; -//** Blockquote font size -$blockquote-font-size: ($font-size-base * 1.25) !default; -//** Blockquote border color -$blockquote-border-color: $gray-light !default; -//** Page header border color -$page-header-border-color: $gray-lighter !default; -//** Width of horizontal description list titles -$dl-horizontal-offset: $component-offset-horizontal !default; -//** Point at which .dl-horizontal becomes horizontal -$dl-horizontal-breakpoint: $grid-float-breakpoint !default; -//** Horizontal line color. -$hr-border: $gray-lighter !default; diff --git a/client/src/sass/_bootswatch.sandstone.scss b/client/src/sass/_bootswatch.sandstone.scss deleted file mode 100644 index 46ea2c6..0000000 --- a/client/src/sass/_bootswatch.sandstone.scss +++ /dev/null @@ -1,195 +0,0 @@ -// Sandstone 3.3.7 -// Bootswatch -// ----------------------------------------------------- - -$web-font-path: "https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700" !default; -@import url($web-font-path); - -// Navbar ===================================================================== - -.sandstone { - font-size: 11px; - line-height: 22px; - font-weight: 500; - text-transform: uppercase; -} - -.navbar { - .nav > li > a { - @extend .sandstone; - } - - &-form input, - &-form .form-control { - border: none; - } -} - -// Buttons ==================================================================== - -.btn { - border: none; - @extend .sandstone; - - &:hover { - border-color: transparent; - } - - &-lg { - line-height: 26px; - } - - &-default { - &:hover { - background-color: $navbar-default-link-active-bg; - } - } -} - -// Typography ================================================================= - -// Tables ===================================================================== - -// Forms ====================================================================== - -input, -.form-control { - @include box-shadow(none); - - &:focus { - border-color: $input-border; - @include box-shadow(none); - } -} - -// Navs ======================================================================= - -.nav { - @extend .sandstone; - - .open > a, - .open > a:hover, - .open > a:focus { - border-color: $gray-light; - } -} - -.nav-tabs { - - & > li > a { - background-color: $gray-lighter; - border-color: $nav-tabs-border-color; - color: $gray; - } - - > li.disabled > a:hover { - background-color: $gray-lighter; - } -} - -.nav-pills { - a { - color: $gray; - } - - li > a { - border: 1px solid transparent; - } - - li.active > a, - li > a:hover { - border-color: $gray-light; - } - - li.disabled > a { - border-color: transparent; - } -} - -.breadcrumb { - @extend .sandstone; - - border: 1px solid $gray-light; - - a { - color: $gray; - } -} - -.pagination { - @extend .sandstone; -} - -.pager { - @extend .sandstone; - - li > a { - color: $gray; - } -} - -.dropdown-menu { - & > li > a { - @extend .sandstone; - } -} - -// Indicators ================================================================= - -.alert { - - a, - .alert-link { - color: #fff; - } -} - -.tooltip { - @extend .sandstone; -} - -// Progress bars ============================================================== - -.progress { - border-radius: 10px; - background-color: $gray-light; - @include box-shadow(none); - - &-bar { - @include box-shadow(none); - } -} - -// Containers ================================================================= - -.list-group { - &-item { - padding: 16px 24px; - } -} - -.well { - @include box-shadow(none); -} - -.panel { - @include box-shadow(none); - - .panel-heading, - .panel-title { - @extend .sandstone; - color: #fff; - } - - .panel-footer { - @extend .sandstone; - } - - &-default { - .panel-heading, - .panel-title, - .panel-footer { - color: $gray; - } - } -} diff --git a/client/src/sass/_grid.scss b/client/src/sass/_grid.scss deleted file mode 100644 index d51fe89..0000000 --- a/client/src/sass/_grid.scss +++ /dev/null @@ -1,10 +0,0 @@ -.data-grid { - font-size: 1.2rem; - thead > tr > th { - // background: #fff; - color: #999; - font-weight: normal; - line-height: 32px; - text-transform: uppercase; - } -} diff --git a/client/src/sass/_utils.scss b/client/src/sass/_utils.scss deleted file mode 100644 index 6e53317..0000000 --- a/client/src/sass/_utils.scss +++ /dev/null @@ -1,109 +0,0 @@ -.card { - background: #fff; - border-radius: 4px; - padding: 32px 24px; -} - -@-moz-keyframes spin { - from { -moz-transform: rotate(0deg); } - to { -moz-transform: rotate(360deg); } -} -@-webkit-keyframes spin { - from { -webkit-transform: rotate(0deg); } - to { -webkit-transform: rotate(360deg); } -} -@keyframes spin { - from {transform:rotate(0deg);} - to {transform:rotate(360deg);} -} -.spin { - -webkit-animation-name: spin; - -webkit-animation-duration: 4000ms; - -webkit-animation-iteration-count: infinite; - -webkit-animation-timing-function: linear; - -moz-animation-name: spin; - -moz-animation-duration: 4000ms; - -moz-animation-iteration-count: infinite; - -moz-animation-timing-function: linear; - -ms-animation-name: spin; - -ms-animation-duration: 4000ms; - -ms-animation-iteration-count: infinite; - -ms-animation-timing-function: linear; - -o-transition: rotate(3600deg); -} - -.header-gradient-1 { - background: #ffffff; - background: -moz-linear-gradient(top, #ffffff 0%, #e5e5e5 100%); - background: -webkit-linear-gradient(top, #ffffff 0%,#e5e5e5 100%); - background: linear-gradient(to bottom, #ffffff 0%,#e5e5e5 100%); - filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#ffffff', endColorstr='#e5e5e5',GradientType=0 ); -} - -.toolbar, -.action-bar { - h3 { - font-size: 1.2rem; - font-weight: bold; - margin: 0; - padding: 16px; - text-transform: uppercase; - } - - .btn { - &, &:focus { - outline: none; - box-shadow: none; - text-decoration: none; - } - } -} - -.toolbar .btn { - float: right; - outline: none; - box-shadow: none; -} - -.btn.btn-link { - outline: none !important; - box-shadow: none; -} - -.panel { - position: relative; - - .header-buttons { - position: absolute; - right: 15px; - top: 7px; - - .btn-link, a { - text-decoration: none; - - &:last-child { - padding-right: 0; - } - } - } -} - -.dropzone { - position: relative; - &.active::after { - align-items: center; - background: #e2efeddb; - border-radius: 4px; - border: dashed 2px #839194; - bottom: 0; - color: #4f5a55; - content: "Drop File to Attach"; - display: grid; - font-size: 2.5rem; - justify-content: center; - left: 0; - position: absolute; - right: 0; - top: 0; - } -} diff --git a/client/src/sass/index.scss b/client/src/sass/index.scss deleted file mode 100644 index 1843488..0000000 --- a/client/src/sass/index.scss +++ /dev/null @@ -1,62 +0,0 @@ -$icon-font-path: "~bootstrap-sass/assets/fonts/bootstrap/"; - -@import "./bootstrap_variables"; -@import "~bootstrap-sass/assets/stylesheets/bootstrap"; -@import "./bootswatch.sandstone"; -@import "~react-s-alert/dist/s-alert-default.css"; -@import "~react-s-alert/dist/s-alert-css-effects/stackslide.css"; - -@import "utils"; -@import "grid"; - -body { - background: #f2f2f2; -} -nav.navbar { - border-radius: 0; - margin-bottom: 0; - .container { - width: 100%; - } -} - -html, -body, -#app-root { - height: 100% -} -.app { - display: grid; - // width: 100vw; - height: 100vh; - grid-template-rows: 64px 1fr; -} -main { - display: grid; -} -header .navbar-brand { - // padding: 10px; - .logo { - display: inline-block; - margin-right: 6px; - max-height: 100%; - max-width: 50px; - } - a { - color: #fff; - text-decoration: none; - } -} - -.s-alert-box { - z-index: 1100; -} -.s-alert-top { - text-align: center; -} -.s-alert-box { - padding: 10px !important; -} -.s-alert-close { - top: 10px !important; -} diff --git a/client/src/sass/login.scss b/client/src/sass/login.scss deleted file mode 100644 index 2c4f525..0000000 --- a/client/src/sass/login.scss +++ /dev/null @@ -1,73 +0,0 @@ -$icon-font-path: "~bootstrap-sass/assets/fonts/bootstrap/"; -@import "./bootstrap_variables"; -@import "~bootstrap-sass/assets/stylesheets/bootstrap"; -@import "./bootswatch.sandstone"; - -@import "utils"; - -body { - background: #f2f2f2; -} - -html, -body { - margin: 0; - padding: 0; -} -#app-root { - background: $btn-primary-bg; - color: $btn-primary-color; -} -.login-panel { - font-weight: 100; - margin: 0 auto; - padding: 60px 20px; - text-align: center; - .logo { - margin-bottom: 30px; - } - h1 { - font-size: 3rem; - } - .brief { - font-size: 2rem; - } - .btn-login { - margin: 40px 0; - } -} -.github-buttons { - background: $gray-darker; - padding: 30px; - text-align: center; -} -.changelog { - background: $gray-lighter; - color: $gray-dark; - font-weight: 300; - padding: 60px; - - .btn-roadmap { - margin: 20px 0 30px; - } - - h2 { - margin-bottom: 30px; - } - - h3 { - font-size: 1.5rem; - } -} -footer { - background: $gray-darker; - color: $gray-light; - font-size: 1.1rem; - padding: 20px; - position: relative; - text-align: center; - iframe { - position: absolute; - margin-left: 16px; - } -} diff --git a/client/src/selectors/Dashboard.js b/client/src/selectors/Dashboard.js deleted file mode 100644 index e95c315..0000000 --- a/client/src/selectors/Dashboard.js +++ /dev/null @@ -1,3 +0,0 @@ -export const getComments = state => state.dashboard.recentComments; -export const getDefects = state => state.dashboard.defects; -export const getExecCycles = state => state.dashboard.execCycles; diff --git a/client/src/selectors/DefectSelector.js b/client/src/selectors/DefectSelector.js deleted file mode 100644 index 25d142b..0000000 --- a/client/src/selectors/DefectSelector.js +++ /dev/null @@ -1,5 +0,0 @@ -// import { createSelector } from "reselect"; - -export const getAllItems = state => state.defectSelector.all; -export const getPath = state => state.defectSelector.path; -export const getSelected = state => state.defectSelector.selected; diff --git a/client/src/selectors/Defects.js b/client/src/selectors/Defects.js deleted file mode 100644 index e8ed150..0000000 --- a/client/src/selectors/Defects.js +++ /dev/null @@ -1,36 +0,0 @@ -import { createSelector } from "reselect"; - - -const getAllDefects = state => state.defects.list.all; -export const getSelectedDefects = state => state.defects.list.selected; -export const getDefectAddEditState = state => state.defects.addEdit; -export const getStatusFilter = state => state.defects.list.statusFilter; -export const getAssigneeFilter = state => state.defects.list.assigneeFilter; - -export const getDefects = createSelector( - [ getAllDefects, getSelectedDefects], - (all, selected) => all.map(defect => ({ - ...defect, - selected: Boolean(selected.find(d => d.id==defect.id)) - })) -); - -export const isEditMode = createSelector( - getDefectAddEditState, - addEdit => Boolean(addEdit.id) -); - -export const areAllDefectsSelected = createSelector( - [ getAllDefects, getSelectedDefects ], - (all, selected) => { - if(!selected.length) - return false; - const allInSelected = all.filter(a => selected.find(s => s.id == a.id)); - return allInSelected.length==all.length; - } -); - -export const allowDelete = createSelector( - getSelectedDefects, - selected => Boolean(selected.length) -); diff --git a/client/src/selectors/ExecCycle.js b/client/src/selectors/ExecCycle.js deleted file mode 100644 index b766d9b..0000000 --- a/client/src/selectors/ExecCycle.js +++ /dev/null @@ -1,90 +0,0 @@ -import { createSelector } from "reselect"; - -const getAllExecCycles = state => state.execCycle.list.all; -export const getSelectedExecCycle = state => state.execCycle.list.selected; -export const getStatusFilter = state => state.execCycle.list.statusFilter; -export const getAddEditState = state => state.execCycle.addEdit; -const getTestRunMap = state => state.execCycle.testRuns; -export const getTestRunAddEditState = state=> state.execCycle.addEdit; -export const showImportDialog = state => state.execCycle.importTestDialog; -export const getTestRun = state => state.execCycle.testRun; - -export const getExecCycles = createSelector( - [ getAllExecCycles, getSelectedExecCycle ], - (execCycles, selected) => execCycles.map(ec => ({ - ...ec, - selected: selected ? ec.id==selected.id : false - })) -); - -const getCurrentTestRuns = createSelector( - [ getTestRunMap, getSelectedExecCycle ], - (trMap, execCycle) => { - if(!execCycle || !trMap[execCycle.id] || !trMap[execCycle.id].all) - return []; - return trMap[execCycle.id].all; - } -); - -export const getSelectedTestRuns = createSelector( - [ getTestRunMap, getSelectedExecCycle ], - (trMap, execCycle) => { - if(!execCycle || !trMap[execCycle.id] || !trMap[execCycle.id].selected) - return []; - return trMap[execCycle.id].selected; - } -); - -export const areAllTestRunsSelected = createSelector( - [ getCurrentTestRuns, getSelectedTestRuns ], - (all, selected) => { - if(!selected.length) - return false; - const allInSelected = all.filter(a => selected.find(s => s.id == a.id)); - return allInSelected.length==all.length; - } -); - -export const getTestRuns = createSelector( - [ getCurrentTestRuns, getSelectedTestRuns ], - (all, selected) => all.map(tr => ({ - ...tr, - selected: Boolean(selected.find(t => t.id==tr.id)) - })) -); - -export const allowAddTestRun = createSelector( - getSelectedExecCycle, - execCycle => execCycle && execCycle.status != "Completed" -); - -export const allowDeleteTestRuns = createSelector( - [ getSelectedExecCycle, getSelectedTestRuns ], - (execCycle, testRuns) => Boolean(execCycle && testRuns.length && execCycle.status != "Completed") -); - -export const allowDeleteTestRun = createSelector( - getSelectedExecCycle, - execCycle => Boolean(execCycle && execCycle.status != "Completed") -); - -export const isInProgress = createSelector( - [ getSelectedExecCycle ], - execCycle => Boolean(execCycle && execCycle.status == "In Progress") -); - -export const allowStartExec = createSelector( - getSelectedExecCycle, - execCycle => execCycle && execCycle.status == "New" -); - -export const allowEndExec = createSelector( - [ getSelectedExecCycle, getSelectedTestRuns ], - (execCycle, testRuns) => { - if(!execCycle || execCycle.status != "In Progress") - return false; - if(testRuns.filter(tr => tr.status=="New").length) - return false; - return true; - } -); diff --git a/client/src/selectors/GroupMultiSelect.js b/client/src/selectors/GroupMultiSelect.js deleted file mode 100644 index e50d5c8..0000000 --- a/client/src/selectors/GroupMultiSelect.js +++ /dev/null @@ -1,67 +0,0 @@ -import deepEqual from "deep-equal"; -import shortId from "shortid"; -import { createSelector } from "reselect"; - -export const getFilterText = state => state.filterText; -export const getPath = state => state.path; - -export const getSelectedItems = state => - state.selectedItems ? state.selectedItems.filter(s => s.path) : []; - -const groupSelectedItems = createSelector( - getSelectedItems, - selectedItems => { - const result = []; - selectedItems.forEach(s => { - const ri = result.findIndex(r => deepEqual(r.path, s.path)); - if(ri < 0) { - result.push({ - id: shortId.generate(), - name: s.path.length - ? s.path[s.path.length-1].name - : "Test Plans", - path: s.path, - items: [ s ] - }); - } else { - result[ri].items.push(s); - } - }); - return result; - } -); - -export const flattenSelected = createSelector( - groupSelectedItems, - selectedItems => { - const arr = []; - selectedItems.forEach(s => { - arr.push({ - id: s.id, - isGroup: true, - name: s.name, - path: s.path - }); - s.items.forEach(sub => { - arr.push(sub); - }); - }); - return arr; - } -); - -export const getUnselectedItems = (state, allItems) => { - const selectedItems = getSelectedItems(state); - const filterText = getFilterText(state); - if(filterText) - allItems = allItems.filter(a => a.name.toLowerCase().indexOf(filterText.toLowerCase())>=0); - if(!selectedItems || !selectedItems.length) - return allItems; - return allItems.filter(a => !selectedItems.find(s => { - if(!s.path.length && !a.path.length) - return s.id===a.id; - return s.id===a.id && deepEqual(s.path, a.path); - })); -}; - -export const allowFilter = state => !state.path || state.path.length > 0; diff --git a/client/src/selectors/Shared.js b/client/src/selectors/Shared.js deleted file mode 100644 index 01ae047..0000000 --- a/client/src/selectors/Shared.js +++ /dev/null @@ -1,6 +0,0 @@ -export const isLoading = state => Boolean(state.isLoading.length); - -export const getUsers = state => state.users.all; - -export const isLoggedIn = state => Boolean(state.session.user); -export const getUser = state => state.session.user; diff --git a/client/src/selectors/TestDesign.js b/client/src/selectors/TestDesign.js deleted file mode 100644 index 7409d33..0000000 --- a/client/src/selectors/TestDesign.js +++ /dev/null @@ -1,25 +0,0 @@ -import { createSelector } from "reselect"; - -const getAllTestPlans = state => state.testDesign.testPlans.all; -export const getSelectedTestPlan = state => state.testDesign.testPlans.selected; -export const getTestPlans = createSelector( - [ getAllTestPlans, getSelectedTestPlan ], - (testPlans, selected) => testPlans.map(tp => ({ - ...tp, - selected: selected ? tp.id==selected.id : false - })) -); - -const getTestCaseMap = state => state.testDesign.testPlans.testCases; - -export const getTestCases = createSelector( - [ getTestCaseMap, getSelectedTestPlan ], - (testCases, testPlan) => testPlan && testCases[testPlan.id] || [] -); - -export const getTestCaseAddEditState = state=> state.testDesign.addEdit; - -export const isEditMode = createSelector( - getTestCaseAddEditState, - addEdit => Boolean(addEdit.id) -); diff --git a/client/src/selectors/TestSelector.js b/client/src/selectors/TestSelector.js deleted file mode 100644 index 80f950b..0000000 --- a/client/src/selectors/TestSelector.js +++ /dev/null @@ -1,5 +0,0 @@ -// import { createSelector } from "reselect"; - -export const getAllItems = state => state.testSelector.all; -export const getPath = state => state.testSelector.path; -export const getSelected = state => state.testSelector.selected; diff --git a/client/src/utils/DefectSelector/DefectSelectorCache.js b/client/src/utils/DefectSelector/DefectSelectorCache.js deleted file mode 100644 index 67f7c29..0000000 --- a/client/src/utils/DefectSelector/DefectSelectorCache.js +++ /dev/null @@ -1,89 +0,0 @@ -const getItemsOnLevel = (arr, path) => { - if(!path.length) - return arr; - const cur = path.shift(); - const nextLevel = arr.find(node => node.id===cur.id); - if(!nextLevel) - throw new Error("Unable to parse"); - return getItemsByPath(nextLevel.items, path); -}; - -const addMeta = node => { - let hasChildren = false; - if((node.items && node.items.length) || node.hasChildren) { - hasChildren = true; - } - return { - ...node, - items: undefined, - hasChildren - }; -}; - -const getItemsByPath = (arr=[], path=[]) => { - return getItemsOnLevel(arr, path).map(addMeta); -}; - -const findPath = (arr, item, path=[]) => { - const found = arr.find(n => n.id===item.id); - if(found) { - return { - found: addMeta(found), - path - }; - } - for(let i=0, len=arr.length; i<len; i++) { - const node = arr[i]; - if(node.items) { - const result = findPath(node.items, item, [ ...path, { id: node.id, name: node.name }]); - if(result.found) { - return result; - } - } - } - return { - found: false - }; -}; - -const addChildren = (arr=[], children=[], path=[]) => { - let node; - for(let i=0; i<path.length; i++) { - const levelId = path[i].id; - node = arr.find(n => n.id===levelId); - } - node.items = children; -}; - -class DefectSelectorCache { - constructor() { - this.data = []; - } - addDetails(items) { - return items.map(item => { - const result = findPath(this.data, item); - const obj = result.found || item; - return { - ...obj, - path: result.found ? result.path : undefined - }; - }); - } - getByPath(path=[]) { - return getItemsByPath(this.data, [ ...path ]); - } - isStale() { - return !this.data || !this.data.length; - } - save(data, path) { - if(!path || !path.length) { - this.data = data; - } else { - addChildren(this.data, data, path); - } - } -} - -const defectSelectorCache = new DefectSelectorCache(); - -export default defectSelectorCache; diff --git a/client/src/utils/DefectSelector/parseSelectorData.js b/client/src/utils/DefectSelector/parseSelectorData.js deleted file mode 100644 index 41e641c..0000000 --- a/client/src/utils/DefectSelector/parseSelectorData.js +++ /dev/null @@ -1,9 +0,0 @@ -const parseSelectorData = arr => arr - .map(def => ({ - id: def.id, - name: def.name, - items: [], - path: [] - })); - -export default parseSelectorData; diff --git a/client/src/utils/Defects/buildDefect.js b/client/src/utils/Defects/buildDefect.js deleted file mode 100644 index 0443542..0000000 --- a/client/src/utils/Defects/buildDefect.js +++ /dev/null @@ -1,25 +0,0 @@ -const buildDefect = data => { - const defect = {}; - if(data.id) - defect.id = data.id; - if(data.status) - defect.status = data.status; - - defect.name = data.name; - defect.description = { - value: data.description.value - }; - if(data.description.attachments) - defect.description.attachments = data.description.attachments - .map(a => ({ id: a.id })); - defect.testCases = data.testCases.map(tc => tc.id); - defect.testRuns = data.testRuns - ? data.testRuns.map(tr => tr.id) - : []; - defect.assignee = data.assignee - ? { id: data.assignee.id } - : null; - return defect; -}; - -export default buildDefect; diff --git a/client/src/utils/Defects/validateDefect.js b/client/src/utils/Defects/validateDefect.js deleted file mode 100644 index 147bee6..0000000 --- a/client/src/utils/Defects/validateDefect.js +++ /dev/null @@ -1,13 +0,0 @@ -const validateDefect = defect => { - if(!defect.testCases) { - return { - valid: false, - error: "A Defect must be tagged to at least one test case" - } - } - return { - valid: true - }; -}; - -export default validateDefect; diff --git a/client/src/utils/ExecCycle/buildExecCycle.js b/client/src/utils/ExecCycle/buildExecCycle.js deleted file mode 100644 index 7112e48..0000000 --- a/client/src/utils/ExecCycle/buildExecCycle.js +++ /dev/null @@ -1,14 +0,0 @@ -const buildExecCycle = data => { - const execCycle = {}; - if(data.id) - execCycle.id = data.id; - execCycle.name = data.name; - execCycle.status = data.status; - if(data.testCases) - execCycle.testCases = data.testCases.map(tc => tc.id); - else - execCycle.testCases = (data.testRuns || []).map(tr => tr.testCase); - return execCycle; -}; - -export default buildExecCycle; diff --git a/client/src/utils/ExecCycle/buildTestRun.js b/client/src/utils/ExecCycle/buildTestRun.js deleted file mode 100644 index 4b63e77..0000000 --- a/client/src/utils/ExecCycle/buildTestRun.js +++ /dev/null @@ -1,19 +0,0 @@ -const buildTestRun = data => { - const testRun = {}; - if(data.id) - testRun.id = data.id; - testRun.execCycle = data.execCycle && data.execCycle.id - ? data.execCycle.id - : data.execCycle; - testRun.name = data.name; - testRun.status = data.status; - testRun.testCase = data.testCase && data.testCase.id - ? data.testCase.id - : data.testCase; - if(data.defects) { - testRun.defects = data.defects.map(def => def.id); - } - return testRun; -}; - -export default buildTestRun; diff --git a/client/src/utils/ExecCycle/validateExecCycle.js b/client/src/utils/ExecCycle/validateExecCycle.js deleted file mode 100644 index ddf96e4..0000000 --- a/client/src/utils/ExecCycle/validateExecCycle.js +++ /dev/null @@ -1,14 +0,0 @@ -const validateExecCycle = data => { - if(!data.name) { - return { - valid: false, - error: "Name is required" - } - } - - return { - valid: true - }; -}; - -export default validateExecCycle; diff --git a/client/src/utils/ExecCycle/validateTestRun.js b/client/src/utils/ExecCycle/validateTestRun.js deleted file mode 100644 index 8eceff4..0000000 --- a/client/src/utils/ExecCycle/validateTestRun.js +++ /dev/null @@ -1,26 +0,0 @@ -const validateTestRun = data => { - if(!data.name) { - return { - valid: false, - error: "Name is required" - } - } - if(!data.execCycle) { - return { - valid: false, - error: "execCycle is required" - } - } - if(!data.testCase) { - return { - valid: false, - error: "testCase is required" - } - } - - return { - valid: true - }; -}; - -export default validateTestRun; diff --git a/client/src/utils/GroupMultiSelect/flatten.js b/client/src/utils/GroupMultiSelect/flatten.js deleted file mode 100644 index 628bae2..0000000 --- a/client/src/utils/GroupMultiSelect/flatten.js +++ /dev/null @@ -1,21 +0,0 @@ -function flatten(node, list=[]) { - if(!node) - return list; - if(node.items) { - // has children - return node.items.reduce((prev, cur) => { - const desc = flatten(cur); - return [ - ...prev, - ...desc - ]; - }, list); - } - // is a leaf - return [ - ...list, - node - ]; -} - -export default flatten; diff --git a/client/src/utils/GroupMultiSelect/getUnselectedItems.js b/client/src/utils/GroupMultiSelect/getUnselectedItems.js deleted file mode 100644 index dc0121e..0000000 --- a/client/src/utils/GroupMultiSelect/getUnselectedItems.js +++ /dev/null @@ -1,46 +0,0 @@ -import flatten from "./flatten"; - -const getUnselectedItems = (items, filterText, selectedItems=[], path) => { - const arr = []; - let currentLevel; - if(path && path instanceof Array) { - path.forEach(level => { - currentLevel = items.find(item => item.id === level.id); - items = currentLevel.items; - }); - } - // remove individual items if 'all' selected - const allItem = items.find(item => item.all); - if(!(allItem && selectedItems.find(item => item.id === allItem.id))) { - items.forEach(item => { - if(item.items) { - const leaves = flatten(item); - const remaining = leaves.filter(leaf => - !selectedItems.find(selectedItem => selectedItem.id === leaf.id)); - const folderSelected = selectedItems.find(selectedItem => selectedItem.id === item.id); - if((item.include || remaining.length) && !folderSelected){ - arr.push(item); - } - } else { - if(!selectedItems.find(y => y.id === item.id)) - arr.push(item); - } - }); - } - if(filterText && arr && !arr.find(item => (item.items))) { // <- filter leaves only - filterText = filterText.toLowerCase(); - return arr.filter(item => { - if(item.items) { - const leaves = flatten(item); - return leaves.find(leaf => leaf.name.toLowerCase().indexOf(filterText) >= 0); - } else { - if(typeof(item.name)!=="string") - return false; - return item.name.toLowerCase().indexOf(filterText) >= 0; - } - }); - } - return arr; -}; - -export default getUnselectedItems; diff --git a/client/src/utils/GroupMultiSelect/isChildOf.js b/client/src/utils/GroupMultiSelect/isChildOf.js deleted file mode 100644 index 2c67009..0000000 --- a/client/src/utils/GroupMultiSelect/isChildOf.js +++ /dev/null @@ -1,18 +0,0 @@ -// returns true is node1 is descendant of node2 -const isChildOf = (node1, node2) => { - const parentPath = [ - ...node2.path, - { id: node2.id } - ]; - - if(node1.path.length < parentPath.length) - return false; - for(let i=0; i<parentPath.length; i++) { - if(node1.path[i].id !== parentPath[i].id) { - return false; - } - } - return true; -}; - -export default isChildOf; diff --git a/client/src/utils/GroupMultiSelect/shouldShowFilter.js b/client/src/utils/GroupMultiSelect/shouldShowFilter.js deleted file mode 100644 index 6a3c8db..0000000 --- a/client/src/utils/GroupMultiSelect/shouldShowFilter.js +++ /dev/null @@ -1,6 +0,0 @@ -const shouldShowFilter = items => { - // show filter when only leaf nodes are visible - return items.length > 0 && !items.find(item => (item.items && item.items.length) || item.noSelectAll === true); -}; - -export default shouldShowFilter; diff --git a/client/src/utils/Shared/buildComment.js b/client/src/utils/Shared/buildComment.js deleted file mode 100644 index 06399fb..0000000 --- a/client/src/utils/Shared/buildComment.js +++ /dev/null @@ -1,24 +0,0 @@ -const buildComment = data => { - const comment = { - content: { - value: data.content - } - }; - if(data.id) - comment.id = data.id; - if(data.attachments) { - const attachments = []; - const files = []; - data.attachments.forEach(a => { - if(a instanceof File) - files.push(a); - else - attachments.push({ id: a.id }); - }); - comment.content.attachments = attachments; - comment.files = files; - } - return comment; -}; - -export default buildComment; diff --git a/client/src/utils/Shared/createTempAttachment.js b/client/src/utils/Shared/createTempAttachment.js deleted file mode 100644 index 1026da9..0000000 --- a/client/src/utils/Shared/createTempAttachment.js +++ /dev/null @@ -1,13 +0,0 @@ -import shortId from "shortid"; - -import dateFormat from "common/utils/dateFormat"; - -const createTempAttachment = file => ({ - created: dateFormat(), - name: file.name, - path: file.preview, - file, - _id: shortId.generate() -}); - -export default createTempAttachment; diff --git a/client/src/utils/Shared/history.js b/client/src/utils/Shared/history.js deleted file mode 100644 index df8357c..0000000 --- a/client/src/utils/Shared/history.js +++ /dev/null @@ -1,5 +0,0 @@ -import createHistory from "history/createBrowserHistory" - -const history = createHistory(); - -export default history; diff --git a/client/src/utils/Shared/request.js b/client/src/utils/Shared/request.js deleted file mode 100644 index 356ff81..0000000 --- a/client/src/utils/Shared/request.js +++ /dev/null @@ -1,231 +0,0 @@ -const defaultHeaders = {}; -const jsonHeaders = { - "Accept": "application/json", - "Content-Type": "application/json" -}; -const formHeaders = { - "Content-type": "application/x-www-form-urlencoded" -}; -const isJSON = response => { - const contentType = response.headers.get("content-type"); - return contentType && contentType.toLowerCase().indexOf("application/json")>=0; -}; -const serialize = jsonData => { - let qString = ""; - if(!jsonData || typeof(jsonData)!=="object") - return qString; - Object.keys(jsonData).forEach(key => { - qString += key + "=" + jsonData[key] + "&"; - }); - if(!qString) - return qString; - qString = qString.slice(0, qString.length - 1); - return qString; -}; -const getFormData = jsonData => { - const fd = new FormData(); - if(!jsonData || typeof(jsonData)!=="object") - return fd; - Object.keys(jsonData).forEach(key => { - fd.append(key, jsonData[key]); - }); - return fd; -}; - -const cbQ = { - complete: [], - success: [], - error: [] -}; -const onComplete = cb => { - if(typeof(cb)==="function") - cbQ.complete.push(cb); -}; -const onSuccess = cb => { - if(typeof(cb)==="function") { - cbQ.success.push(cb); - cbQ.complete.push(cb); - } -}; -const onError = cb => { - if(typeof(cb)==="function") { - cbQ.error.push(cb); - cbQ.complete.push(cb); - } -}; - -/** -* @param {Object} options -* @param {String} options.url - relative or absolute (starting with http) URL -* @param {String} [type="GET"] - HTTP request type -* @param {String} [dataType] - "json" | "form" -* @param {Object|FormData} [options.data] - - JSON for GET requests - - JSON for dataType=json - - FormData for dataType=form or unknown dataType -* @param {Object) [headers] - HTTP headers -*/ -const request = options => { - if(!options || !options.url) { - return Promise.reject("URL required"); - } - const params = { - method: options.type || options.method || "GET" - }; - params.method = params.method.toUpperCase(); - let url; - if(/^http/.test(options.url)) { - url = options.url; - } else if(/^\//.test(options.url)) { - url = `${options.url}`; - } else { - url = `/${options.url}`; - } - if(!options.noCredentials) { - params.credentials = "include"; - params.headers = { - ...defaultHeaders, - ...options.headers - }; - } else { - params.headers = { - ...defaultHeaders, - ...options.headers - }; - } - if(params.method !== "GET") { - if(options.dataType=="json") { - params.headers = { - ...jsonHeaders, - ...params.headers - }; - if(options.data) - params.body = JSON.stringify(options.data); - } else { - if(options.dataType=="form") { - params.headers = { - ...formHeaders, - ...params.headers - }; - } - } - if(!params.body) { - if(options.data instanceof FormData) - params.body = options.data; - else - params.body = getFormData(options.data); - } - } else if(options.data && typeof(options.data) === "object") { - // add query params - let qString = serialize(options.data); - if(url.indexOf("?") > 0) { - qString = "&" + qString; - } else { - qString = "?" + qString; - } - url += qString; - } - return fetch(url, params).then(response => { - cbQ.complete.forEach(cb => { - setTimeout(() => { - cb(response); - }, 0); - }); - if(response.ok) { - cbQ.success.forEach(cb => { - setTimeout(() => { - cb(response); - }, 0); - }); - if(isJSON(response)) { - return response.text().then(text => { - try { - const json = JSON.parse(text); - return Promise.resolve({ - status: response.status, - statusText: response.statusText, - text, - json, - }); - } catch(ex) { - return Promise.resolve({ - status: response.status, - statusText: response.statusText, - text - }); - } - }); - } - cbQ.forEach(cb => cb(response)); - return response.text().then(text => Promise.resolve({ - status: response.status, - statusText: response.statusText, - text - })); - } - // process error - if(isJSON(response)) { - cbQ.error.forEach(cb => { - setTimeout(() => { - cb(response); - }, 0); - }); - return response.text().then(text => { - try { - const errorJSON = JSON.parse(text); - return Promise.reject({ - status: response.status, - statusText: response.statusText, - text, - json: errorJSON, - errors: errorJSON - }); - } catch(ex) { - return Promise.reject({ - status: response.status, - statusText: response.statusText, - text - }); - } - }); - } - return response.text().then(text => Promise.reject({ - status: response.status, - statusText: response.statusText, - text - })); - }); -}; -request.on = (event, cb) => { - if(event==="success") { - onSuccess(cb); - onComplete(cb); - } - if(event==="error") { - onError(cb); - onComplete(cb); - } - if(event==="complete") { - onComplete(cb); - } -}; -request.off = (event, cb) => { - if(event==="success") { - const index = cbQ.success.findIndex(f => f===cb); - if(index != -1) { - cbQ.success.splice(index, 1); - } - } - if(event==="error") { - const index = cbQ.error.findIndex(f => f===cb); - if(index != -1) { - cbQ.error.splice(index, 1); - } - } - const index = cbQ.error.findIndex(f => f===cb); - if(index != -1) { - cbQ.complete.splice(index, 1); - } -}; - -export default request; diff --git a/client/src/utils/Shared/store.js b/client/src/utils/Shared/store.js deleted file mode 100644 index 7f446e9..0000000 --- a/client/src/utils/Shared/store.js +++ /dev/null @@ -1,9 +0,0 @@ -import { createStore, applyMiddleware, compose } from "redux"; - -export default function configureStore(reducer, sagaMiddleware) { - const store = createStore(reducer, {}, compose( - applyMiddleware(sagaMiddleware), - window.devToolsExtension ? window.devToolsExtension() : f => f - )); - return store; -} diff --git a/client/src/utils/TestDesign/buildTestCase.js b/client/src/utils/TestDesign/buildTestCase.js deleted file mode 100644 index 89afc59..0000000 --- a/client/src/utils/TestDesign/buildTestCase.js +++ /dev/null @@ -1,16 +0,0 @@ -const buildTestCase = data => { - const testCase = {}; - if(data.id) - testCase.id = data.id; - testCase.name = data.name; - testCase.description = { - value: data.description.value || "" - }; - if(data.description.attachments) - testCase.description.attachments = data.description.attachments - .map(a => ({ id: a.id })); - - return testCase; -}; - -export default buildTestCase; diff --git a/client/src/utils/TestDesign/buildTestPlan.js b/client/src/utils/TestDesign/buildTestPlan.js deleted file mode 100644 index 8db93ed..0000000 --- a/client/src/utils/TestDesign/buildTestPlan.js +++ /dev/null @@ -1,10 +0,0 @@ -const buildTestPlan = data => { - const testPlan = {}; - if(data.id) - testPlan.id = data.id; - testPlan.name = data.name; - - return testPlan; -}; - -export default buildTestPlan; diff --git a/client/src/utils/TestDesign/parseComment.js b/client/src/utils/TestDesign/parseComment.js deleted file mode 100644 index 8094d46..0000000 --- a/client/src/utils/TestDesign/parseComment.js +++ /dev/null @@ -1,11 +0,0 @@ -const parseComment = (comment={}) => { - return { - id: comment.id, - content: comment.content.value, - created: comment.created, - modified: comment.modified, - user: comment.user - }; -}; - -export default parseComment; diff --git a/client/src/utils/TestDesign/parseTestCase.js b/client/src/utils/TestDesign/parseTestCase.js deleted file mode 100644 index ac563fa..0000000 --- a/client/src/utils/TestDesign/parseTestCase.js +++ /dev/null @@ -1,14 +0,0 @@ -const parseTestCase = testCase => { - const comments = testCase.comments.map(c => ({ - ...c, - created: c.created || new Date("Jan 1 1970"), - modified: c.modified || new Date("Jan 1 1970") - })); - comments.sort((c1, c2) => c1.created < c2.created) - return { - ...testCase, - comments - }; -}; - -export default parseTestCase; diff --git a/client/src/utils/TestDesign/validateTestCase.js b/client/src/utils/TestDesign/validateTestCase.js deleted file mode 100644 index 7dae08f..0000000 --- a/client/src/utils/TestDesign/validateTestCase.js +++ /dev/null @@ -1,14 +0,0 @@ -const validateTestCase = data => { - if(!data.name) { - return { - valid: false, - error: "Name is required" - } - } - - return { - valid: true - }; -}; - -export default validateTestCase; diff --git a/client/src/utils/TestDesign/validateTestPlan.js b/client/src/utils/TestDesign/validateTestPlan.js deleted file mode 100644 index 826265f..0000000 --- a/client/src/utils/TestDesign/validateTestPlan.js +++ /dev/null @@ -1,14 +0,0 @@ -const validateTestPlan = data => { - if(!data.name) { - return { - valid: false, - error: "Name is required" - } - } - - return { - valid: true - }; -}; - -export default validateTestPlan; diff --git a/client/src/utils/TestSelector/TestSelectorCache.js b/client/src/utils/TestSelector/TestSelectorCache.js deleted file mode 100644 index 824d7c2..0000000 --- a/client/src/utils/TestSelector/TestSelectorCache.js +++ /dev/null @@ -1,89 +0,0 @@ -const getItemsOnLevel = (arr, path) => { - if(!path.length) - return arr; - const cur = path.shift(); - const nextLevel = arr.find(node => node.id===cur.id); - if(!nextLevel) - throw new Error("Unable to parse"); - return getItemsByPath(nextLevel.items, path); -}; - -const addMeta = node => { - let hasChildren = false; - if((node.items && node.items.length) || node.hasChildren) { - hasChildren = true; - } - return { - ...node, - items: undefined, - hasChildren - }; -}; - -const getItemsByPath = (arr=[], path=[]) => { - return getItemsOnLevel(arr, path).map(addMeta); -}; - -const findPath = (arr, item, path=[]) => { - const found = arr.find(n => n.id===item.id); - if(found) { - return { - found: addMeta(found), - path - }; - } - for(let i=0, len=arr.length; i<len; i++) { - const node = arr[i]; - if(node.items) { - const result = findPath(node.items, item, [ ...path, { id: node.id, name: node.name }]); - if(result.found) { - return result; - } - } - } - return { - found: false - }; -}; - -const addChildren = (arr=[], children=[], path=[]) => { - let node; - for(let i=0; i<path.length; i++) { - const levelId = path[i].id; - node = arr.find(n => n.id===levelId); - } - node.items = children; -}; - -class TCSelectorCache { - constructor() { - this.data = []; - } - addDetails(items) { - return items.map(item => { - const result = findPath(this.data, item); - const obj = result.found || item; - return { - ...obj, - path: result.found ? result.path : undefined - }; - }); - } - getByPath(path=[]) { - return getItemsByPath(this.data, [ ...path ]); - } - isStale() { - return !this.data || !this.data.length; - } - save(data, path) { - if(!path || !path.length) { - this.data = data; - } else { - addChildren(this.data, data, path); - } - } -} - -const tcSelectorCache = new TCSelectorCache(); - -export default tcSelectorCache; diff --git a/client/src/utils/TestSelector/parseSelectorData.js b/client/src/utils/TestSelector/parseSelectorData.js deleted file mode 100644 index f653792..0000000 --- a/client/src/utils/TestSelector/parseSelectorData.js +++ /dev/null @@ -1,24 +0,0 @@ -const parseSelectorData = (arr, path) => { - if(!path.length) { - return arr - .filter(tp => tp.testCases && tp.testCases.length) - .map(tp => ({ - id: tp.id, - name: tp.name, - items: tp.testCases.map(tc => ({ - id: tc, - name:`Test-${tc}` - })), - path, - selectable: true - })); - } - return arr.map(tc => ({ - id: tc.id, - name: tc.name, - testPlan: tc.testPlan, - path - })); -}; - -export default parseSelectorData; diff --git a/common/constants/DefectStates.js b/common/constants/DefectStates.js deleted file mode 100644 index 7969b57..0000000 --- a/common/constants/DefectStates.js +++ /dev/null @@ -1,6 +0,0 @@ -module.exports = [ - "Open", - "WIP", - "Resolved", - "Non Issue" -]; diff --git a/common/constants/ExecCycleCloneTypes.js b/common/constants/ExecCycleCloneTypes.js deleted file mode 100644 index 44141f3..0000000 --- a/common/constants/ExecCycleCloneTypes.js +++ /dev/null @@ -1,17 +0,0 @@ -const ALL = "ALL"; -const FAILED = "FAILED"; -const UNFINISHED = "UNFINISHED"; - -module.exports = [{ - id: ALL, - name: "All Tests" -}, { - id: FAILED, - name: "Failed Tests" -}, { - id: UNFINISHED, - name: "Unfinished Tests" -}]; -module.exports.ALL = ALL; -module.exports.FAILED = FAILED; -module.exports.UNFINISHED = UNFINISHED; diff --git a/common/constants/TestRunStates.js b/common/constants/TestRunStates.js deleted file mode 100644 index 025cd8f..0000000 --- a/common/constants/TestRunStates.js +++ /dev/null @@ -1,5 +0,0 @@ -module.exports = [ - "New", - "Pass", - "Fail" -]; diff --git a/common/constants/permissions.js b/common/constants/permissions.js deleted file mode 100644 index fcbbd67..0000000 --- a/common/constants/permissions.js +++ /dev/null @@ -1,38 +0,0 @@ -module.exports.DEFECT_READ = { id: "DEFECT_READ", name: "Read Defect" }; -module.exports.DEFECT_CREATE = { id: "DEFECT_CREATE", name: "Create Defect" }; -module.exports.DEFECT_UPDATE = { id: "DEFECT_UPDATE", name: "Update defect" }; -module.exports.DEFECT_DELETE = { id: "DEFECT_DELETE", name: "Delete Defect" }; - -module.exports.COMMENT_READ = { id: "COMMENT_READ", name: "Read comment" }; -module.exports.COMMENT_CREATE = { id: "COMMENT_CREATE", name: "Create comment" }; -module.exports.COMMENT_UPDATE = { id: "COMMENT_UPDATE", name: "Update comment" }; -module.exports.COMMENT_UPDATE_OWN = { id: "COMMENT_UPDATE_OWN", name: "Update own comment" }; -module.exports.COMMENT_DELETE = { id: "COMMENT_DELETE", name: "Delete comment" }; -module.exports.COMMENT_DELETE_OWN = { id: "COMMENT_DELETE_OWN", name: "Delete own comment" }; - -module.exports.TESTPLAN_READ = { id: "TESTPLAN_READ", name: "Read testplan" }; -module.exports.TESTPLAN_CREATE = { id: "TESTPLAN_CREATE", name: "Create testplan" }; -module.exports.TESTPLAN_UPDATE = { id: "TESTPLAN_UPDATE", name: "Update testplan" }; -module.exports.TESTPLAN_DELETE = { id: "TESTPLAN_DELETE", name: "Delete testplan" }; - -module.exports.TESTCASE_READ = { id: "TESTCASE_READ", name: "Read test case" }; -module.exports.TESTCASE_CREATE = { id: "TESTCASE_CREATE", name: "Create test case" }; -module.exports.TESTCASE_UPDATE = { id: "TESTCASE_UPDATE", name: "Update test case" }; -module.exports.TESTCASE_DELETE = { id: "TESTCASE_DELETE", name: "Delete test case" }; - -module.exports.EXECCYCLE_READ = { id: "EXECCYCLE_READ", name: "Read exec cycle" }; -module.exports.EXECCYCLE_CREATE = { id: "EXECCYCLE_CREATE", name: "Create exec cycle" }; -module.exports.EXECCYCLE_UPDATE = { id: "EXECCYCLE_UPDATE", name: "Update exec cycle" }; -module.exports.EXECCYCLE_DELETE = { id: "EXECCYCLE_DELETE", name: "Delete exec cycle" }; - -module.exports.TESTRUN_READ = { id: "TESTRUN_READ", name: "Read test run" }; -module.exports.TESTRUN_CREATE = { id: "TESTRUN_CREATE", name: "Create test run" }; -module.exports.TESTRUN_UPDATE = { id: "TESTRUN_UPDATE", name: "Update test run" }; -module.exports.TESTRUN_DELETE = { id: "TESTRUN_DELETE", name: "Delete test run" }; - -module.exports.USER_READ = { id: "USER_READ", name: "Read user" }; -module.exports.USER_CREATE = { id: "USER_CREATE", name: "Create user" }; -module.exports.USER_UPDATE = { id: "USER_UPDATE", name: "Update user" }; -module.exports.USER_UPDATE_OWN = { id: "USER_UPDATE_OWN", name: "Update own user" }; -module.exports.USER_DELETE = { id: "USER_DELETE", name: "Delete user" }; -module.exports.USER_DELETE_OWN = { id: "USER_DELETE_OWN", name: "Delete own user" }; diff --git a/common/images/TestMan.png b/common/images/TestMan.png deleted file mode 100644 index cacf8be3783b889e4ff76094330d889db5168b5f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2721 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 bp91_3Bso;=m%8>j00000NkvXXu0mjf^u#tl diff --git a/common/images/google_signin.png b/common/images/google_signin.png deleted file mode 100644 index b1327b4f7b47c04368da6c4b066645453398eca5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3983 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 diff --git a/common/utils/dateFormat.js b/common/utils/dateFormat.js deleted file mode 100644 index 1ad99ca..0000000 --- a/common/utils/dateFormat.js +++ /dev/null @@ -1,5 +0,0 @@ -const moment = require("moment"); - -const format = timestamp => moment(timestamp ? new Date(timestamp) : new Date()).format("DD MMM, YYYY HH:mm"); - -module.exports = format; diff --git a/common/utils/fromNow.js b/common/utils/fromNow.js deleted file mode 100644 index c0c67c7..0000000 --- a/common/utils/fromNow.js +++ /dev/null @@ -1,5 +0,0 @@ -const moment = require("moment"); - -const fromNow = formattedDate => moment(formattedDate, "DD MMM, YYYY HH:mm").fromNow(); - -module.exports = fromNow; diff --git a/config-overrides.js b/config-overrides.js new file mode 100644 index 0000000..fd54ed2 --- /dev/null +++ b/config-overrides.js @@ -0,0 +1,17 @@ +const path = require("path"); +const ModuleScopePlugin = require("react-dev-utils/ModuleScopePlugin"); + +module.exports = { + paths: function(paths) { + paths.appIndexJs = path.resolve(__dirname, "src/client/index.tsx"); + paths.appSrc = path.resolve(__dirname, "src"); + paths.appBuild = path.resolve(__dirname, "dist/client"); + return paths; + }, + webpack: function(config, env) { + config.resolve.plugins = config.resolve.plugins.filter( + plugin => !plugin instanceof ModuleScopePlugin + ); + return config; + } +}; diff --git a/dist/client/asset-manifest.json b/dist/client/asset-manifest.json new file mode 100644 index 0000000..e6c7ce8 --- /dev/null +++ b/dist/client/asset-manifest.json @@ -0,0 +1,22 @@ +{ + "files": { + "main.css": "/static/css/main.1e813b89.chunk.css", + "main.js": "/static/js/main.5239a377.chunk.js", + "main.js.map": "/static/js/main.5239a377.chunk.js.map", + "runtime-main.js": "/static/js/runtime-main.3940b3dc.js", + "runtime-main.js.map": "/static/js/runtime-main.3940b3dc.js.map", + "static/js/2.2441c286.chunk.js": "/static/js/2.2441c286.chunk.js", + "static/js/2.2441c286.chunk.js.map": "/static/js/2.2441c286.chunk.js.map", + "index.html": "/index.html", + "precache-manifest.7ad3c67ad1ebe92ef48b7d29f649614c.js": "/precache-manifest.7ad3c67ad1ebe92ef48b7d29f649614c.js", + "service-worker.js": "/service-worker.js", + "static/css/main.1e813b89.chunk.css.map": "/static/css/main.1e813b89.chunk.css.map", + "static/js/2.2441c286.chunk.js.LICENSE.txt": "/static/js/2.2441c286.chunk.js.LICENSE.txt" + }, + "entrypoints": [ + "static/js/runtime-main.3940b3dc.js", + "static/js/2.2441c286.chunk.js", + "static/css/main.1e813b89.chunk.css", + "static/js/main.5239a377.chunk.js" + ] +} \ No newline at end of file diff --git a/dist/client/favicon.ico b/dist/client/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..bcd5dfd67cd0361b78123e95c2dd96031f27f743 GIT binary patch literal 3150 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 MDeX2GIKR{Qf5okjU;qFB literal 0 HcmV?d00001 diff --git a/dist/client/index.html b/dist/client/index.html new file mode 100644 index 0000000..fc5cfc3 --- /dev/null +++ b/dist/client/index.html @@ -0,0 +1 @@ +<!doctype html><html lang="en"><head><meta charset="utf-8"/><link rel="icon" href="/favicon.ico"/><meta name="viewport" content="width=device-width,initial-scale=1"/><meta name="theme-color" content="#000000"/><meta name="description" content="CRA Express Starter"/><link rel="apple-touch-icon" href="/logo192.png"/><link rel="manifest" href="/manifest.json"/><title>CRA Express Starter
\ No newline at end of file diff --git a/dist/client/logo192.png b/dist/client/logo192.png new file mode 100644 index 0000000000000000000000000000000000000000..fc44b0a3796c0e0a64c3d858ca038bd4570465d9 GIT binary patch literal 5347 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 b{|{}U^kRn2wo}j7|0ATu<;8xA7zX}7|B6mN literal 0 HcmV?d00001 diff --git a/dist/client/manifest.json b/dist/client/manifest.json new file mode 100644 index 0000000..080d6c7 --- /dev/null +++ b/dist/client/manifest.json @@ -0,0 +1,25 @@ +{ + "short_name": "React App", + "name": "Create React App Sample", + "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/dist/client/precache-manifest.7ad3c67ad1ebe92ef48b7d29f649614c.js b/dist/client/precache-manifest.7ad3c67ad1ebe92ef48b7d29f649614c.js new file mode 100644 index 0000000..3d26a80 --- /dev/null +++ b/dist/client/precache-manifest.7ad3c67ad1ebe92ef48b7d29f649614c.js @@ -0,0 +1,26 @@ +self.__precacheManifest = (self.__precacheManifest || []).concat([ + { + "revision": "f79a4d6e502b01390bfc58b79c343908", + "url": "/index.html" + }, + { + "revision": "c55265decaeedf041489", + "url": "/static/css/main.1e813b89.chunk.css" + }, + { + "revision": "36bd04376c2c050ff87a", + "url": "/static/js/2.2441c286.chunk.js" + }, + { + "revision": "928d7b5eb39d16fe9a880722c974b51e", + "url": "/static/js/2.2441c286.chunk.js.LICENSE.txt" + }, + { + "revision": "c55265decaeedf041489", + "url": "/static/js/main.5239a377.chunk.js" + }, + { + "revision": "86ceb8118e8ed65832c4", + "url": "/static/js/runtime-main.3940b3dc.js" + } +]); \ No newline at end of file diff --git a/dist/client/robots.txt b/dist/client/robots.txt new file mode 100644 index 0000000..01b0f9a --- /dev/null +++ b/dist/client/robots.txt @@ -0,0 +1,2 @@ +# https://www.robotstxt.org/robotstxt.html +User-agent: * diff --git a/dist/client/service-worker.js b/dist/client/service-worker.js new file mode 100644 index 0000000..b4058b2 --- /dev/null +++ b/dist/client/service-worker.js @@ -0,0 +1,39 @@ +/** + * Welcome to your Workbox-powered service worker! + * + * You'll need to register this file in your web app and you should + * disable HTTP caching for this file too. + * See https://goo.gl/nhQhGp + * + * The rest of the code is auto-generated. Please don't update this file + * directly; instead, make changes to your Workbox build configuration + * and re-run your build process. + * See https://goo.gl/2aRDsh + */ + +importScripts("https://storage.googleapis.com/workbox-cdn/releases/4.3.1/workbox-sw.js"); + +importScripts( + "/precache-manifest.7ad3c67ad1ebe92ef48b7d29f649614c.js" +); + +self.addEventListener('message', (event) => { + if (event.data && event.data.type === 'SKIP_WAITING') { + self.skipWaiting(); + } +}); + +workbox.core.clientsClaim(); + +/** + * The workboxSW.precacheAndRoute() method efficiently caches and responds to + * requests for URLs in the manifest. + * See https://goo.gl/S9QRab + */ +self.__precacheManifest = [].concat(self.__precacheManifest || []); +workbox.precaching.precacheAndRoute(self.__precacheManifest, {}); + +workbox.routing.registerNavigationRoute(workbox.precaching.getCacheKeyForURL("/index.html"), { + + blacklist: [/^\/_/,/\/[^\/?]+\.[^\/]+$/], +}); diff --git a/dist/client/static/css/main.1e813b89.chunk.css b/dist/client/static/css/main.1e813b89.chunk.css new file mode 100644 index 0000000..5b52c28 --- /dev/null +++ b/dist/client/static/css/main.1e813b89.chunk.css @@ -0,0 +1,2 @@ +@import url(https://fonts.googleapis.com/css?family=Lato&display=swap);@import url(https://stackpath.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css);body{line-height:1;background:#e2e2e2;margin:0;padding:0}#root{color:#404041;font-family:"Lato",sans-serif}a,abbr,acronym,address,applet,article,aside,audio,b,big,blockquote,body,canvas,caption,center,cite,code,dd,del,details,dfn,div,dl,dt,em,embed,fieldset,figcaption,figure,footer,form,h1,h2,h3,h4,h5,h6,header,hgroup,html,i,iframe,img,ins,kbd,label,legend,li,mark,menu,nav,object,ol,output,p,pre,q,ruby,s,samp,section,small,span,strike,strong,sub,summary,sup,table,tbody,td,tfoot,th,thead,time,tr,tt,u,ul,var,video{margin:0;padding:0;border:0;font-size:100%;font:inherit;vertical-align:baseline}article,aside,details,figcaption,figure,footer,header,hgroup,menu,nav,section{display:block}body{line-height:1.25}ol,ul{list-style:none}blockquote,q{quotes:none}blockquote:after,blockquote:before,q:after,q:before{content:"";content:none}table{border-collapse:collapse;border-spacing:0}*{box-sizing:border-box} +/*# sourceMappingURL=main.1e813b89.chunk.css.map */ \ No newline at end of file diff --git a/dist/client/static/css/main.1e813b89.chunk.css.map b/dist/client/static/css/main.1e813b89.chunk.css.map new file mode 100644 index 0000000..a50061c --- /dev/null +++ b/dist/client/static/css/main.1e813b89.chunk.css.map @@ -0,0 +1 @@ +{"version":3,"sources":["main.1e813b89.chunk.css","App.css","index.css"],"names":[],"mappings":"AAAA,sEAAsE,CACtE,2FAA2F,CCoC3F,KACE,aAAc,CAqBd,kBAAmB,CACnB,QAAS,CACT,SAtBF,CAyBA,MACE,aAAc,CACd,6BACF,CC3DA,2ZAaE,QAAS,CACT,SAAU,CACV,QAAS,CACT,cAAe,CACf,YAAa,CACb,uBACF,CAEA,8EAEE,aACF,CACA,KACE,gBACF,CACA,MACE,eACF,CACA,aACE,WACF,CACA,oDAEE,UAAW,CACX,YACF,CACA,MACE,wBAAyB,CACzB,gBACF,CAEA,EACE,qBACF","file":"main.1e813b89.chunk.css","sourcesContent":["@import url(https://fonts.googleapis.com/css?family=Lato&display=swap);\n@import url(https://stackpath.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css);\n/* http://meyerweb.com/eric/tools/css/reset/\n v2.0 | 20110126\n License: none (public domain)\n*/\n/* http://meyerweb.com/eric/tools/css/reset/\n v2.0 | 20110126\n License: none (public domain)\n*/\n\nhtml, body, div, span, applet, object, iframe,\nh1, h2, h3, h4, h5, h6, p, blockquote, pre,\na, abbr, acronym, address, big, cite, code,\ndel, dfn, em, img, ins, kbd, q, s, samp,\nsmall, strike, strong, sub, sup, tt, var,\nb, u, i, center,\ndl, dt, dd, ol, ul, li,\nfieldset, form, label, legend,\ntable, caption, tbody, tfoot, thead, tr, th, td,\narticle, aside, canvas, details, embed,\nfigure, figcaption, footer, header, hgroup,\nmenu, nav, output, ruby, section, summary,\ntime, mark, audio, video {\n margin: 0;\n padding: 0;\n border: 0;\n font-size: 100%;\n font: inherit;\n vertical-align: baseline;\n}\n/* HTML5 display-role reset for older browsers */\narticle, aside, details, figcaption, figure,\nfooter, header, hgroup, menu, nav, section {\n display: block;\n}\nbody {\n line-height: 1;\n}\nol, ul {\n list-style: none;\n}\nblockquote, q {\n quotes: none;\n}\nblockquote:before, blockquote:after,\nq:before, q:after {\n content: '';\n content: none;\n}\ntable {\n border-collapse: collapse;\n border-spacing: 0;\n}\n\n/* App CSS starts here */\n\nbody {\n background: #e2e2e2;\n margin: 0;\n padding: 0;\n}\n\n#root {\n color: #404041;\n font-family: \"Lato\", sans-serif;\n}\n\n/*@import url(\"https://fonts.googleapis.com/css?family=Poppins:400,600,700&display=swap\");*/\n/*@import url(\"https://fonts.googleapis.com/css?family=Open+Sans:300,400,600,700&display=swap\");*/\n\n/* http://meyerweb.com/eric/tools/css/reset/\n v2.0 | 20110126\n License: none (public domain)\n*/\n\nhtml, body, div, span, applet, object, iframe,\nh1, h2, h3, h4, h5, h6, p, blockquote, pre,\na, abbr, acronym, address, big, cite, code,\ndel, dfn, em, img, ins, kbd, q, s, samp,\nsmall, strike, strong, sub, sup, tt, var,\nb, u, i, center,\ndl, dt, dd, ol, ul, li,\nfieldset, form, label, legend,\ntable, caption, tbody, tfoot, thead, tr, th, td,\narticle, aside, canvas, details, embed,\nfigure, figcaption, footer, header, hgroup,\nmenu, nav, output, ruby, section, summary,\ntime, mark, audio, video {\n margin: 0;\n padding: 0;\n border: 0;\n font-size: 100%;\n font: inherit;\n vertical-align: baseline;\n}\n/* HTML5 display-role reset for older browsers */\narticle, aside, details, figcaption, figure,\nfooter, header, hgroup, menu, nav, section {\n display: block;\n}\nbody {\n line-height: 1.25;\n}\nol, ul {\n list-style: none;\n}\nblockquote, q {\n quotes: none;\n}\nblockquote:before, blockquote:after,\nq:before, q:after {\n content: '';\n content: none;\n}\ntable {\n border-collapse: collapse;\n border-spacing: 0;\n}\n/* App CSS starts here */\n* {\n box-sizing: border-box;\n}\n\n","@import url(\"https://fonts.googleapis.com/css?family=Lato&display=swap\");\n@import url(\"https://stackpath.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css\");\n\n/* http://meyerweb.com/eric/tools/css/reset/\n v2.0 | 20110126\n License: none (public domain)\n*/\n/* http://meyerweb.com/eric/tools/css/reset/\n v2.0 | 20110126\n License: none (public domain)\n*/\n\nhtml, body, div, span, applet, object, iframe,\nh1, h2, h3, h4, h5, h6, p, blockquote, pre,\na, abbr, acronym, address, big, cite, code,\ndel, dfn, em, img, ins, kbd, q, s, samp,\nsmall, strike, strong, sub, sup, tt, var,\nb, u, i, center,\ndl, dt, dd, ol, ul, li,\nfieldset, form, label, legend,\ntable, caption, tbody, tfoot, thead, tr, th, td,\narticle, aside, canvas, details, embed,\nfigure, figcaption, footer, header, hgroup,\nmenu, nav, output, ruby, section, summary,\ntime, mark, audio, video {\n margin: 0;\n padding: 0;\n border: 0;\n font-size: 100%;\n font: inherit;\n vertical-align: baseline;\n}\n/* HTML5 display-role reset for older browsers */\narticle, aside, details, figcaption, figure,\nfooter, header, hgroup, menu, nav, section {\n display: block;\n}\nbody {\n line-height: 1;\n}\nol, ul {\n list-style: none;\n}\nblockquote, q {\n quotes: none;\n}\nblockquote:before, blockquote:after,\nq:before, q:after {\n content: '';\n content: none;\n}\ntable {\n border-collapse: collapse;\n border-spacing: 0;\n}\n\n/* App CSS starts here */\n\nbody {\n background: #e2e2e2;\n margin: 0;\n padding: 0;\n}\n\n#root {\n color: #404041;\n font-family: \"Lato\", sans-serif;\n}\n","/*@import url(\"https://fonts.googleapis.com/css?family=Poppins:400,600,700&display=swap\");*/\n/*@import url(\"https://fonts.googleapis.com/css?family=Open+Sans:300,400,600,700&display=swap\");*/\n\n/* http://meyerweb.com/eric/tools/css/reset/\n v2.0 | 20110126\n License: none (public domain)\n*/\n\nhtml, body, div, span, applet, object, iframe,\nh1, h2, h3, h4, h5, h6, p, blockquote, pre,\na, abbr, acronym, address, big, cite, code,\ndel, dfn, em, img, ins, kbd, q, s, samp,\nsmall, strike, strong, sub, sup, tt, var,\nb, u, i, center,\ndl, dt, dd, ol, ul, li,\nfieldset, form, label, legend,\ntable, caption, tbody, tfoot, thead, tr, th, td,\narticle, aside, canvas, details, embed,\nfigure, figcaption, footer, header, hgroup,\nmenu, nav, output, ruby, section, summary,\ntime, mark, audio, video {\n margin: 0;\n padding: 0;\n border: 0;\n font-size: 100%;\n font: inherit;\n vertical-align: baseline;\n}\n/* HTML5 display-role reset for older browsers */\narticle, aside, details, figcaption, figure,\nfooter, header, hgroup, menu, nav, section {\n display: block;\n}\nbody {\n line-height: 1.25;\n}\nol, ul {\n list-style: none;\n}\nblockquote, q {\n quotes: none;\n}\nblockquote:before, blockquote:after,\nq:before, q:after {\n content: '';\n content: none;\n}\ntable {\n border-collapse: collapse;\n border-spacing: 0;\n}\n/* App CSS starts here */\n* {\n box-sizing: border-box;\n}\n"]} \ No newline at end of file diff --git a/dist/client/static/js/2.2441c286.chunk.js b/dist/client/static/js/2.2441c286.chunk.js new file mode 100644 index 0000000..2c37e13 --- /dev/null +++ b/dist/client/static/js/2.2441c286.chunk.js @@ -0,0 +1,3 @@ +/*! For license information please see 2.2441c286.chunk.js.LICENSE.txt */ +(this["webpackJsonpcra-express-starter"]=this["webpackJsonpcra-express-starter"]||[]).push([[2],[function(e,t,n){"use strict";e.exports=n(50)},function(e,t,n){"use strict";n.d(t,"a",(function(){return M})),n.d(t,"b",(function(){return g})),n.d(t,"c",(function(){return z})),n.d(t,"d",(function(){return k})),n.d(t,"e",(function(){return c})),n.d(t,"f",(function(){return U})),n.d(t,"g",(function(){return q})),n.d(t,"h",(function(){return F})),n.d(t,"i",(function(){return G})),n.d(t,"j",(function(){return te})),n.d(t,"k",(function(){return ne})),n.d(t,"l",(function(){return ee})),n.d(t,"m",(function(){return re})),n.d(t,"n",(function(){return I})),n.d(t,"o",(function(){return N})),n.d(t,"p",(function(){return L})),n.d(t,"q",(function(){return B})),n.d(t,"r",(function(){return R})),n.d(t,"s",(function(){return oe})),n.d(t,"t",(function(){return J})),n.d(t,"u",(function(){return Y})),n.d(t,"v",(function(){return X})),n.d(t,"w",(function(){return D})),n.d(t,"x",(function(){return $})),n.d(t,"y",(function(){return s})),n.d(t,"z",(function(){return H})),n.d(t,"A",(function(){return A})),n.d(t,"B",(function(){return W})),n.d(t,"C",(function(){return V})),n.d(t,"D",(function(){return K})),n.d(t,"E",(function(){return b})),n.d(t,"F",(function(){return C})),n.d(t,"G",(function(){return u})),n.d(t,"H",(function(){return T})),n.d(t,"I",(function(){return w})),n.d(t,"J",(function(){return _})),n.d(t,"K",(function(){return h})),n.d(t,"L",(function(){return f})),n.d(t,"M",(function(){return y})),n.d(t,"N",(function(){return p})),n.d(t,"O",(function(){return S})),n.d(t,"P",(function(){return l})),n.d(t,"Q",(function(){return d})),n.d(t,"R",(function(){return O})),n.d(t,"S",(function(){return E})),n.d(t,"T",(function(){return x}));var r=n(3),o=n(5),i=n(2),a=n(24),u=function(e){return function(){return e}}(!0),l=function(){};var c=function(e){return e};"function"===typeof Symbol&&Symbol.asyncIterator&&Symbol.asyncIterator;function s(e,t,n){if(!t(e))throw new Error(n)}var f=function(e,t){Object(o.a)(e,t),Object.getOwnPropertySymbols&&Object.getOwnPropertySymbols(t).forEach((function(n){e[n]=t[n]}))},d=function(e,t){var n;return(n=[]).concat.apply(n,t.map(e))};function p(e,t){var n=e.indexOf(t);n>=0&&e.splice(n,1)}function h(e){var t=!1;return function(){t||(t=!0,e())}}var m=function(e){throw e},v=function(e){return{value:e,done:!0}};function y(e,t,n){void 0===t&&(t=m),void 0===n&&(n="iterator");var r={meta:{name:n},next:e,throw:t,return:v,isSagaIterator:!0};return"undefined"!==typeof Symbol&&(r[Symbol.iterator]=function(){return r}),r}function g(e,t){var n=t.sagaStack;console.error(e),console.error(n)}var b=function(e){return new Error("\n redux-saga: Error checking hooks detected an inconsistent state. This is likely a bug\n in redux-saga code and not yours. Thanks for reporting this in the project's github repo.\n Error: "+e+"\n")},w=function(e){return Array.apply(null,new Array(e))},k=function(e){return function(t){return e(Object.defineProperty(t,r.f,{value:!0}))}},x=function(e){return e===r.k},E=function(e){return e===r.j},S=function(e){return x(e)||E(e)};function T(e,t){var n=Object.keys(e),r=n.length;var o,a=0,u=Object(i.a)(e)?w(r):{},c={};return n.forEach((function(e){var n=function(n,i){o||(i||S(n)?(t.cancel(),t(n,i)):(u[e]=n,++a===r&&(o=!0,t(u))))};n.cancel=l,c[e]=n})),t.cancel=function(){o||(o=!0,n.forEach((function(e){return c[e].cancel()})))},c}function C(e){return{name:e.name||"anonymous",location:O(e)}}function O(e){return e[r.g]}var P={isEmpty:u,put:l,take:l};function j(e,t){void 0===e&&(e=10);var n=new Array(e),r=0,o=0,i=0,a=function(t){n[o]=t,o=(o+1)%e,r++},u=function(){if(0!=r){var t=n[i];return n[i]=null,r--,i=(i+1)%e,t}},l=function(){for(var e=[];r;)e.push(u());return e};return{isEmpty:function(){return 0==r},put:function(u){var c;if(r1?t-1:0),r=1;r1?t-1:0),r=1;r2?n-2:0),i=2;i2?n-2:0),i=2;i2&&void 0!==arguments[2]&&arguments[2];return{type:r,payload:{location:e,action:t,isFirstRendering:n}}},i="@@router/CALL_HISTORY_METHOD",a=function(e){return function(){for(var t=arguments.length,n=new Array(t),r=0;r1?t-1:0),r=1;r0?" Additional arguments: "+n.join(", "):""))}var S=function(e){var t=document.head,n=e||t,r=document.createElement("style"),o=function(e){for(var t=e.childNodes,n=t.length;n>=0;n--){var r=t[n];if(r&&1===r.nodeType&&r.hasAttribute(b))return r}}(n),i=void 0!==o?o.nextSibling:null;r.setAttribute(b,"active"),r.setAttribute("data-styled-version","5.0.1");var a=x();return a&&r.setAttribute("nonce",a),n.insertBefore(r,i),r},T=function(){function e(e){var t=this.element=S(e);t.appendChild(document.createTextNode("")),this.sheet=function(e){if(e.sheet)return e.sheet;for(var t=document.styleSheets,n=0,r=t.length;n=0){var n=document.createTextNode(t),r=this.nodes[e];return this.element.insertBefore(n,r||null),this.length++,!0}return!1},t.deleteRule=function(e){this.element.removeChild(this.nodes[e]),this.length--},t.getRule=function(e){return e=this.groupSizes.length){for(var n=this.groupSizes,r=n.length,o=r;e>=o;)(o<<=1)<0&&E(16,""+e);this.groupSizes=new Uint32Array(o),this.groupSizes.set(n),this.length=o;for(var i=r;i=this.length||0===this.groupSizes[e])return t;for(var n=this.groupSizes[e],r=this.indexOfGroup(e),o=r+n,i=r;i=N&&(N=t+1),j.set(e,t),_.set(t,e)},M="style["+b+'][data-styled-version="5.0.1"]',L=/(?:\s*)?(.*?){((?:{[^}]*}|(?!{).*?)*)}/g,z=new RegExp("^"+b+'\\.g(\\d+)\\[id="([\\w\\d-]+)"\\]'),D=function(e,t,n){for(var r,o=n.split(","),i=0,a=o.length;i0&&(c+=e+",")})),r+=""+u+l+'{content:"'+c+'"}\n'}}}return r}(this)},e}(),H=function(e,t){for(var n=t.length;n;)e=33*e^t.charCodeAt(--n);return e},W=function(e){return H(5381,e)};var V=/^\s*\/\/.*$/gm;function q(e){var t,n,r,o=void 0===e?m:e,i=o.options,u=void 0===i?m:i,l=o.plugins,c=void 0===l?h:l,s=new a.a(u),f=[],d=function(e){function t(t){if(t)try{e(t+"}")}catch(n){}}return function(n,r,o,i,a,u,l,c,s,f){switch(n){case 1:if(0===s&&64===r.charCodeAt(0))return e(r+";"),"";break;case 2:if(0===c)return r+"/*|*/";break;case 3:switch(c){case 102:case 112:return e(o[0]+r),"";default:return r+(0===f?"/*|*/":"")}case-2:r.split("/*|*/}").forEach(t)}}}((function(e){f.push(e)})),p=function(e,r,o){return r>0&&-1!==o.slice(0,r).indexOf(n)&&o.slice(r-n.length,r)!==n?"."+t:e};function v(e,o,i,a){void 0===a&&(a="&");var u=e.replace(V,""),l=o&&i?i+" "+o+" { "+u+" }":u;return t=a,n=o,r=new RegExp("\\"+n+"\\b","g"),s(i||!o?"":o,l)}return s.use([].concat(c,[function(e,t,o){2===e&&o.length&&o[0].lastIndexOf(n)>0&&(o[0]=o[0].replace(r,p))},d,function(e){if(-2===e){var t=f;return f=[],t}}])),v.hash=c.length?c.reduce((function(e,t){return t.name||E(15),H(e,t.name)}),5381).toString():"",v}var K=i.a.createContext(),Q=(K.Consumer,i.a.createContext()),G=(Q.Consumer,new B),Y=q();function X(){return Object(o.useContext)(K)||G}function J(){return Object(o.useContext)(Q)||Y}var Z=function(){function e(e,t){var n=this;this.inject=function(e){e.hasNameForId(n.id,n.name)||e.insertRules(n.id,n.name,Y.apply(void 0,n.stringifyArgs))},this.toString=function(){return E(12,String(n.name))},this.name=e,this.id="sc-keyframes-"+e,this.stringifyArgs=t}return e.prototype.getName=function(){return this.name},e}(),ee=/([A-Z])/g,te=/^ms-/;function ne(e){return e.replace(ee,"-$1").toLowerCase().replace(te,"-ms-")}var re=function(e){return void 0===e||null===e||!1===e||""===e},oe=function e(t,n){var r=[];return Object.keys(t).forEach((function(n){if(!re(t[n])){if(p(t[n]))return r.push.apply(r,e(t[n],n)),r;if(v(t[n]))return r.push(ne(n)+":",t[n],";"),r;r.push(ne(n)+": "+(o=n,null==(i=t[n])||"boolean"===typeof i||""===i?"":"number"!==typeof i||0===i||o in u.a?String(i).trim():i+"px")+";")}var o,i;return r})),n?[n+" {"].concat(r,["}"]):r};function ie(e,t,n){if(Array.isArray(e)){for(var r,o=[],i=0,a=e.length;i1?t-1:0),r=1;r1?t-1:0),r=1;r25?39:97))};function pe(e){var t,n="";for(t=Math.abs(e);t>52;t=t/52|0)n=de(t%52)+n;return(de(t%52)+n).replace(fe,"$1-$2")}function he(e){for(var t=0;t>>0);if(!t.hasNameForId(r,i)){var a=n(o,"."+i,void 0,r);t.insertRules(r,i,a)}return this.staticRulesId=i,i}for(var u=this.rules.length,l=H(this.baseHash,n.hash),c="",s=0;s>>0);if(!t.hasNameForId(r,h)){var m=n(c,"."+h,void 0,r);t.insertRules(r,h,m)}return h},e}(),ve=(new Set,function(e,t,n){return void 0===n&&(n=m),e.theme!==n.theme&&e.theme||t||n.theme}),ye=/[[\].#*$><+~=|^:(),"'`-]+/g,ge=/(^-|-$)/g;function be(e){return e.replace(ye,"-").replace(ge,"")}function we(e){return"string"===typeof e&&!0}var ke=function(e){return pe(W(e)>>>0)};var xe=i.a.createContext();xe.Consumer;var Ee={};function Se(e,t,n){var r=e.attrs,i=e.componentStyle,a=e.defaultProps,u=e.foldedComponentIds,c=e.styledComponentId,s=e.target;Object(o.useDebugValue)(c);var d=function(e,t,n){void 0===e&&(e=m);var r=f({},t,{theme:e}),o={};return n.forEach((function(e){var t,n,i,a=e;for(t in v(a)&&(a=a(r)),a)r[t]=o[t]="className"===t?(n=o[t],i=a[t],n&&i?n+" "+i:n||i):a[t]})),[r,o]}(ve(t,Object(o.useContext)(xe),a)||m,t,r),p=d[0],h=d[1],y=function(e,t,n,r){var i=X(),a=J(),u=e.isStatic&&!t?e.generateAndInjectStyles(m,i,a):e.generateAndInjectStyles(n,i,a);return Object(o.useDebugValue)(u),u}(i,r.length>0,p),g=n,b=h.as||t.as||s,w=we(b),k=h!==t?f({},t,{},h):t,x=w||"as"in k||"forwardedAs"in k,E=x?{}:f({},k);if(x)for(var S in k)"forwardedAs"===S?E.as=k[S]:"as"===S||"forwardedAs"===S||w&&!Object(l.a)(S)||(E[S]=k[S]);return t.style&&h.style!==t.style&&(E.style=f({},t.style,{},h.style)),E.className=Array.prototype.concat(u,c,y!==c?y:null,t.className,h.className).filter(Boolean).join(" "),E.ref=g,Object(o.createElement)(b,E)}function Te(e,t,n){var r,o=g(e),a=!we(e),u=t.displayName,l=void 0===u?function(e){return we(e)?"styled."+e:"Styled("+y(e)+")"}(e):u,c=t.componentId,d=void 0===c?function(e,t){var n="string"!==typeof e?"sc":be(e);Ee[n]=(Ee[n]||0)+1;var r=n+"-"+ke(n+Ee[n]);return t?t+"-"+r:r}(t.displayName,t.parentComponentId):c,p=t.attrs,m=void 0===p?h:p,v=t.displayName&&t.componentId?be(t.displayName)+"-"+t.componentId:t.componentId||d,b=o&&e.attrs?Array.prototype.concat(e.attrs,m).filter(Boolean):m,w=new me(o?e.componentStyle.rules.concat(n):n,v),k=function(e,t){return Se(r,e,t)};return k.displayName=l,(r=i.a.forwardRef(k)).attrs=b,r.componentStyle=w,r.displayName=l,r.foldedComponentIds=o?Array.prototype.concat(e.foldedComponentIds,e.styledComponentId):h,r.styledComponentId=v,r.target=o?e.target:e,r.withComponent=function(e){var r=t.componentId,o=function(e,t){if(null==e)return{};var n,r,o={},i=Object.keys(e);for(r=0;r=0||(o[n]=e[n]);return o}(t,["componentId"]),i=r&&r+"-"+(we(e)?e:be(y(e)));return Te(e,f({},o,{attrs:b,componentId:i}),n)},Object.defineProperty(r,"defaultProps",{get:function(){return this._foldedDefaultProps},set:function(t){this._foldedDefaultProps=o?se({},e.defaultProps,t):t}}),r.toString=function(){return"."+r.styledComponentId},a&&s()(r,e,{attrs:!0,componentStyle:!0,displayName:!0,foldedComponentIds:!0,self:!0,styledComponentId:!0,target:!0,withComponent:!0}),r}var Ce=function(e){return function e(t,n,o){if(void 0===o&&(o=m),!Object(r.isValidElementType)(n))return E(1,String(n));var i=function(){return t(n,o,ae.apply(void 0,arguments))};return i.withConfig=function(r){return e(t,n,f({},o,{},r))},i.attrs=function(r){return e(t,n,f({},o,{attrs:Array.prototype.concat(o.attrs,r).filter(Boolean)}))},i}(Te,e)};["a","abbr","address","area","article","aside","audio","b","base","bdi","bdo","big","blockquote","body","br","button","canvas","caption","cite","code","col","colgroup","data","datalist","dd","del","details","dfn","dialog","div","dl","dt","em","embed","fieldset","figcaption","figure","footer","form","h1","h2","h3","h4","h5","h6","head","header","hgroup","hr","html","i","iframe","img","input","ins","kbd","keygen","label","legend","li","link","main","map","mark","marquee","menu","menuitem","meta","meter","nav","noscript","object","ol","optgroup","option","output","p","param","picture","pre","progress","q","rp","rt","ruby","s","samp","script","section","select","small","source","span","strong","style","sub","summary","sup","table","tbody","td","textarea","tfoot","th","thead","time","title","tr","track","u","ul","var","video","wbr","circle","clipPath","defs","ellipse","foreignObject","g","image","line","linearGradient","marker","mask","path","pattern","polygon","polyline","radialGradient","rect","stop","svg","text","tspan"].forEach((function(e){Ce[e]=Ce(e)}));t.a=Ce}).call(this,n(61))},function(e,t,n){"use strict";function r(e,t){if(null==e)return{};var n,r,o={},i=Object.keys(e);for(r=0;r=0||(o[n]=e[n]);return o}n.d(t,"a",(function(){return r}))},function(e,t,n){"use strict";function r(e){return function(e){if(Array.isArray(e)){for(var t=0,n=new Array(e.length);t. You may also pass a {context : MyContext} option to connect");var I=A;return function(t){var n=t.displayName||t.name||"Component",i=a(n),u=Object(h.a)({},R,{getDisplayName:a,methodName:c,renderCountProp:d,shouldHandleStateChanges:v,storeKey:C,displayName:i,wrappedComponentName:n,WrappedComponent:t}),l=R.pure;var s=l?r.useMemo:function(e){return e()};function p(n){var a=Object(r.useMemo)((function(){var e=n.forwardedRef,t=Object(m.a)(n,["forwardedRef"]);return[n.context,e,t]}),[n]),l=a[0],c=a[1],d=a[2],p=Object(r.useMemo)((function(){return l&&l.Consumer&&Object(w.isContextConsumer)(o.a.createElement(l.Consumer,null))?l:I}),[l,I]),y=Object(r.useContext)(p),g=Boolean(n.store)&&Boolean(n.store.getState)&&Boolean(n.store.dispatch),C=Boolean(y)&&Boolean(y.store);b()(g||C,'Could not find "store" in the context of "'+i+'". Either wrap the root component in a , or pass a custom React context provider to and the corresponding React context consumer to '+i+" in connect options.");var O=g?n.store:y.store,P=Object(r.useMemo)((function(){return function(t){return e(t.dispatch,u)}(O)}),[O]),j=Object(r.useMemo)((function(){if(!v)return E;var e=new f(O,g?null:y.subscription),t=e.notifyNestedSubs.bind(e);return[e,t]}),[O,g,y]),_=j[0],N=j[1],A=Object(r.useMemo)((function(){return g?y:Object(h.a)({},y,{subscription:_})}),[g,y,_]),R=Object(r.useReducer)(S,x,T),M=R[0][0],L=R[1];if(M&&M.error)throw M.error;var z=Object(r.useRef)(),D=Object(r.useRef)(d),U=Object(r.useRef)(),F=Object(r.useRef)(!1),$=s((function(){return U.current&&d===D.current?U.current:P(O.getState(),d)}),[O,M,d]);k((function(){D.current=d,z.current=$,F.current=!1,U.current&&(U.current=null,N())})),k((function(){if(v){var e=!1,t=null,n=function(){if(!e){var n,r,o=O.getState();try{n=P(o,D.current)}catch(i){r=i,t=i}r||(t=null),n===z.current?F.current||N():(z.current=n,U.current=n,F.current=!0,L({type:"STORE_UPDATED",payload:{error:r}}))}};_.onStateChange=n,_.trySubscribe(),n();return function(){if(e=!0,_.tryUnsubscribe(),_.onStateChange=null,t)throw t}}}),[O,_,P]);var B=Object(r.useMemo)((function(){return o.a.createElement(t,Object(h.a)({},$,{ref:c}))}),[c,t,$]);return Object(r.useMemo)((function(){return v?o.a.createElement(p.Provider,{value:A},B):B}),[p,B,A])}var g=l?o.a.memo(p):p;if(g.WrappedComponent=t,g.displayName=i,_){var O=o.a.forwardRef((function(e,t){return o.a.createElement(g,Object(h.a)({},e,{forwardedRef:t}))}));return O.displayName=i,O.WrappedComponent=t,y()(O,t)}return y()(g,t)}}var O=Object.prototype.hasOwnProperty;function P(e,t){return e===t?0!==e||0!==t||1/e===1/t:e!==e&&t!==t}function j(e,t){if(P(e,t))return!0;if("object"!==typeof e||null===e||"object"!==typeof t||null===t)return!1;var n=Object.keys(e),r=Object.keys(t);if(n.length!==r.length)return!1;for(var o=0;o=0;r--){var o=t[r](e);if(o)return o}return function(t,r){throw new Error("Invalid value of type "+typeof e+" for "+n+" argument when connecting component "+r.wrappedComponentName+".")}}function B(e,t){return e===t}var H=function(e){var t=void 0===e?{}:e,n=t.connectHOC,r=void 0===n?C:n,o=t.mapStateToPropsFactories,i=void 0===o?M:o,a=t.mapDispatchToPropsFactories,u=void 0===a?I:a,l=t.mergePropsFactories,c=void 0===l?z:l,s=t.selectorFactory,f=void 0===s?F:s;return function(e,t,n,o){void 0===o&&(o={});var a=o,l=a.pure,s=void 0===l||l,d=a.areStatesEqual,p=void 0===d?B:d,v=a.areOwnPropsEqual,y=void 0===v?j:v,g=a.areStatePropsEqual,b=void 0===g?j:g,w=a.areMergedPropsEqual,k=void 0===w?j:w,x=Object(m.a)(a,["pure","areStatesEqual","areOwnPropsEqual","areStatePropsEqual","areMergedPropsEqual"]),E=$(e,i,"mapStateToProps"),S=$(t,u,"mapDispatchToProps"),T=$(n,c,"mergeProps");return r(f,Object(h.a)({methodName:"connect",getDisplayName:function(e){return"Connect("+e+")"},shouldHandleStateChanges:Boolean(e),initMapStateToProps:E,initMapDispatchToProps:S,initMergeProps:T,pure:s,areStatesEqual:p,areOwnPropsEqual:y,areStatePropsEqual:b,areMergedPropsEqual:k},x))}}();function W(){var e=Object(r.useContext)(u);return b()(e,"could not find react-redux context value; please ensure the component is wrapped in a "),e}function V(e){void 0===e&&(e=u);var t=e===u?W:function(){return Object(r.useContext)(e)};return function(){return t().store}}var q=V();var K=function(e){void 0===e&&(e=u);var t=e===u?q:V(e);return function(){return t().dispatch}}(),Q=function(e,t){return e===t};var G,Y=function(e){void 0===e&&(e=u);var t=e===u?W:function(){return Object(r.useContext)(e)};return function(e,n){void 0===n&&(n=Q),b()(e,"You must pass a selector to useSelectors");var o=t();return function(e,t,n,o){var i,a=Object(r.useReducer)((function(e){return e+1}),0)[1],u=Object(r.useMemo)((function(){return new f(n,o)}),[n,o]),l=Object(r.useRef)(),c=Object(r.useRef)(),s=Object(r.useRef)();try{i=e!==c.current||l.current?e(n.getState()):s.current}catch(p){var d="An error occurred while selecting the store state: "+p.message+".";throw l.current&&(d+="\nThe error may be correlated with this previous error:\n"+l.current.stack+"\n\nOriginal stack trace:"),new Error(d)}return k((function(){c.current=e,s.current=i,l.current=void 0})),k((function(){function e(){try{var e=c.current(n.getState());if(t(e,s.current))return;s.current=e}catch(p){l.current=p}a({})}return u.onStateChange=e,u.trySubscribe(),e(),function(){return u.tryUnsubscribe()}}),[n,u]),i}(e,n,o.store,o.subscription)}}(),X=n(23);n.d(t,"a",(function(){return p})),n.d(t,"b",(function(){return u})),n.d(t,"c",(function(){return H})),n.d(t,"d",(function(){return K})),n.d(t,"e",(function(){return Y})),G=X.unstable_batchedUpdates,l=G},function(e,t,n){"use strict";t.a=function(e,t){if(!e)throw new Error("Invariant failed")}},function(e,t,n){"use strict";var r=n(5);function o(e){return"/"===e.charAt(0)}function i(e,t){for(var n=t,r=n+1,o=e.length;r=0;d--){var p=a[d];"."===p?i(a,d):".."===p?(i(a,d),f++):f&&(i(a,d),f--)}if(!c)for(;f--;f)a.unshift("..");!c||""===a[0]||a[0]&&o(a[0])||a.unshift("");var h=a.join("/");return n&&"/"!==h.substr(-1)&&(h+="/"),h};function u(e){return e.valueOf?e.valueOf():Object.prototype.valueOf.call(e)}var l=function e(t,n){if(t===n)return!0;if(null==t||null==n)return!1;if(Array.isArray(t))return Array.isArray(n)&&t.length===n.length&&t.every((function(t,r){return e(t,n[r])}));if("object"===typeof t||"object"===typeof n){var r=u(t),o=u(n);return r!==t||o!==n?e(r,o):Object.keys(Object.assign({},t,n)).every((function(r){return e(t[r],n[r])}))}return!1},c=n(15);function s(e){return"/"===e.charAt(0)?e:"/"+e}function f(e,t){return function(e,t){return 0===e.toLowerCase().indexOf(t.toLowerCase())&&-1!=="/?#".indexOf(e.charAt(t.length))}(e,t)?e.substr(t.length):e}function d(e){return"/"===e.charAt(e.length-1)?e.slice(0,-1):e}function p(e){var t=e.pathname,n=e.search,r=e.hash,o=t||"/";return n&&"?"!==n&&(o+="?"===n.charAt(0)?n:"?"+n),r&&"#"!==r&&(o+="#"===r.charAt(0)?r:"#"+r),o}function h(e,t,n,o){var i;"string"===typeof e?(i=function(e){var t=e||"/",n="",r="",o=t.indexOf("#");-1!==o&&(r=t.substr(o),t=t.substr(0,o));var i=t.indexOf("?");return-1!==i&&(n=t.substr(i),t=t.substr(0,i)),{pathname:t,search:"?"===n?"":n,hash:"#"===r?"":r}}(e)).state=t:(void 0===(i=Object(r.a)({},e)).pathname&&(i.pathname=""),i.search?"?"!==i.search.charAt(0)&&(i.search="?"+i.search):i.search="",i.hash?"#"!==i.hash.charAt(0)&&(i.hash="#"+i.hash):i.hash="",void 0!==t&&void 0===i.state&&(i.state=t));try{i.pathname=decodeURI(i.pathname)}catch(u){throw u instanceof URIError?new URIError('Pathname "'+i.pathname+'" could not be decoded. This is likely caused by an invalid percent-encoding.'):u}return n&&(i.key=n),o?i.pathname?"/"!==i.pathname.charAt(0)&&(i.pathname=a(i.pathname,o.pathname)):i.pathname=o.pathname:i.pathname||(i.pathname="/"),i}function m(e,t){return e.pathname===t.pathname&&e.search===t.search&&e.hash===t.hash&&e.key===t.key&&l(e.state,t.state)}function v(){var e=null;var t=[];return{setPrompt:function(t){return e=t,function(){e===t&&(e=null)}},confirmTransitionTo:function(t,n,r,o){if(null!=e){var i="function"===typeof e?e(t,n):e;"string"===typeof i?"function"===typeof r?r(i,o):o(!0):o(!1!==i)}else o(!0)},appendListener:function(e){var n=!0;function r(){n&&e.apply(void 0,arguments)}return t.push(r),function(){n=!1,t=t.filter((function(e){return e!==r}))}},notifyListeners:function(){for(var e=arguments.length,n=new Array(e),r=0;rt?n.splice(t,n.length-t,r):n.push(r),f({action:"PUSH",location:r,index:t,entries:n})}}))},replace:function(e,t){var r=h(e,t,d(),w.location);s.confirmTransitionTo(r,"REPLACE",n,(function(e){e&&(w.entries[w.index]=r,f({action:"REPLACE",location:r}))}))},go:b,goBack:function(){b(-1)},goForward:function(){b(1)},canGo:function(e){var t=w.index+e;return t>=0&&t2&&void 0!==arguments[2]&&arguments[2];r.inTimeTravelling?r.inTimeTravelling=!1:a(e,t,n)};return r.unlisten=i.listen(u),u(i.location,i.action,!0),r}var r,i,a;return function(e,t){if("function"!==typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function");e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,writable:!0,configurable:!0}}),t&&y(e,t)}(n,e),r=n,(i=[{key:"componentWillUnmount",value:function(){this.unlisten(),this.unsubscribe()}},{key:"render",value:function(){var e=this.props,t=e.history,n=e.children;return o.a.createElement(l.b,{history:t},n)}}])&&h(r.prototype,i),a&&h(r,a),n}(r.PureComponent);n.propTypes={store:a.a.shape({getState:a.a.func.isRequired,subscribe:a.a.func.isRequired}).isRequired,history:a.a.shape({action:a.a.string.isRequired,listen:a.a.func.isRequired,location:a.a.object.isRequired,push:a.a.func.isRequired}).isRequired,basename:a.a.string,children:a.a.oneOfType([a.a.func,a.a.node]),onLocationChanged:a.a.func.isRequired};var i=function(e){var t=e.context||u.b;if(null==t)throw"Please upgrade to react-redux v6";return o.a.createElement(t.Consumer,null,(function(t){var r=t.store;return o.a.createElement(n,p({store:r},e))}))};return i.propTypes={context:a.a.object},Object(u.c)(null,(function(e){return{onLocationChanged:function(t,n,r){return e(Object(c.c)(t,n,r))}}}))(i)};function b(e,t){return function(e){if(Array.isArray(e))return e}(e)||function(e,t){var n=[],r=!0,o=!1,i=void 0;try{for(var a,u=e[Symbol.iterator]();!(r=(a=u.next()).done)&&(n.push(a.value),!t||n.length!==t);r=!0);}catch(l){o=!0,i=l}finally{try{r||null==u.return||u.return()}finally{if(o)throw i}}return n}(e,t)||function(){throw new TypeError("Invalid attempt to destructure non-iterable instance")}()}function w(e){for(var t=1;t0&&void 0!==arguments[0]?arguments[0]:r,o=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},i=o.type,a=o.payload;if(i===c.b){var u=a.location,l=a.action,s=a.isFirstRendering;return s?e:n(e,{location:t(x(u)),action:l})}return e}}};function S(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}var T={fromJS:function(e){return e},getIn:function(e,t){if(!e)return e;var n=t.length;if(n){for(var r=e,o=0;or&&(r=(t=t.trim()).charCodeAt(0)),r){case 38:return t.replace(m,"$1"+e.trim());case 58:return e.trim()+t.replace(m,"$1"+e.trim());default:if(0<1*n&&0l.charCodeAt(8))break;case 115:a=a.replace(l,"-webkit-"+l)+";"+a;break;case 207:case 102:a=a.replace(l,"-webkit-"+(102u.charCodeAt(0)&&(u=u.trim()),u=[u],0p)&&(U=(B=B.replace(" ",":")).length),02?h-2:0),y=2;yN.length&&N.push(e)}function I(e,t,n){return null==e?0:function e(t,n,r,o){var u=typeof t;"undefined"!==u&&"boolean"!==u||(t=null);var l=!1;if(null===t)l=!0;else switch(u){case"string":case"number":l=!0;break;case"object":switch(t.$$typeof){case i:case a:l=!0}}if(l)return r(o,t,""===n?"."+M(t,0):n),1;if(l=0,n=""===n?".":n+":",Array.isArray(t))for(var c=0;c