From 576b956c5faeef51b83938290dbefbb1be3fb344 Mon Sep 17 00:00:00 2001 From: "huoyong.msb" Date: Thu, 18 Jul 2019 11:11:51 +0800 Subject: [PATCH] RAP v2.1 commits --- package.json | 128 +++-- public/logo.svg | 19 + src/actions/account.js | 67 --- src/actions/account.ts | 59 +++ src/actions/analytics.js | 20 - src/actions/analytics.ts | 28 ++ src/actions/common.ts | 1 + src/actions/interface.js | 113 ----- src/actions/interface.ts | 113 +++++ src/actions/module.js | 56 --- src/actions/module.ts | 56 +++ src/actions/organization.js | 31 -- src/actions/organization.ts | 33 ++ src/actions/property.js | 71 --- src/actions/property.ts | 71 +++ src/actions/repository.js | 37 -- src/actions/repository.ts | 48 ++ src/actions/types.ts | 66 +++ src/assets/index.sass | 7 +- src/assets/variables.sass | 1 - .../account/{Index.jsx => Index.tsx} | 5 +- src/components/account/LoginForm.jsx | 91 ---- src/components/account/LoginForm.sass | 43 +- src/components/account/LoginForm.tsx | 109 +++++ src/components/account/Navigation.jsx | 19 - src/components/account/Navigation.tsx | 18 + src/components/account/RegisterForm.jsx | 79 ---- src/components/account/RegisterForm.sass | 36 +- src/components/account/RegisterForm.tsx | 102 ++++ src/components/account/ResetForm.jsx | 107 ----- src/components/account/UpdateForm.jsx | 76 --- src/components/account/{User.jsx => User.tsx} | 4 +- .../account/{UserList.jsx => UserList.tsx} | 29 +- src/components/api/API.sass | 1 + src/components/api/{API.jsx => API.tsx} | 103 +++-- src/components/checker/Checker.jsx | 107 ----- src/components/checker/Checker.tsx | 110 +++++ src/components/common/Footer.jsx | 29 -- src/components/common/Footer.tsx | 30 ++ src/components/common/GlobalStyles.tsx | 30 ++ src/components/common/Header.jsx | 61 --- src/components/common/Header.sass | 38 -- src/components/common/Header.tsx | 39 ++ src/components/common/LoadingButton.tsx | 42 ++ src/components/common/MuiTheme.tsx | 21 + src/components/common/Navigation.jsx | 19 - src/components/common/Navigation.tsx | 13 + src/components/common/UserList.tsx | 275 +++++++++++ .../editor/DuplicatedInterfacesWarning.jsx | 65 --- .../editor/DuplicatedInterfacesWarning.tsx | 84 ++++ src/components/editor/Importer.jsx | 143 ------ src/components/editor/Importer.tsx | 213 +++++++++ src/components/editor/InterfaceEditor.jsx | 196 -------- src/components/editor/InterfaceEditor.tsx | 232 ++++++++++ .../editor/InterfaceEditorToolbar.jsx | 57 --- .../editor/InterfaceEditorToolbar.tsx | 84 ++++ src/components/editor/InterfaceForm.jsx | 135 ------ src/components/editor/InterfaceForm.tsx | 173 +++++++ src/components/editor/InterfaceList.jsx | 147 ------ src/components/editor/InterfaceList.tsx | 140 ++++++ ...cePreviewer.jsx => InterfacePreviewer.tsx} | 88 ++-- src/components/editor/InterfaceSummary.jsx | 181 -------- src/components/editor/InterfaceSummary.sass | 9 +- src/components/editor/InterfaceSummary.tsx | 285 ++++++++++++ src/components/editor/ModuleForm.jsx | 95 ---- src/components/editor/ModuleForm.tsx | 114 +++++ src/components/editor/ModuleList.jsx | 126 ----- src/components/editor/ModuleList.tsx | 112 +++++ src/components/editor/MoveInterfaceForm.jsx | 106 ----- src/components/editor/MoveInterfaceForm.tsx | 115 +++++ src/components/editor/PropertyForm.jsx | 137 ------ src/components/editor/PropertyForm.tsx | 178 +++++++ src/components/editor/PropertyList.jsx | 267 ----------- src/components/editor/PropertyList.tsx | 311 +++++++++++++ src/components/editor/RepositoryEditor.sass | 5 +- ...ositoryEditor.jsx => RepositoryEditor.tsx} | 115 ++--- src/components/editor/RepositorySearcher.jsx | 132 ------ src/components/editor/RepositorySearcher.tsx | 145 ++++++ src/components/home/Home.jsx | 53 --- src/components/home/Home.tsx | 56 +++ ...iesCard.jsx => JoinedRepositoriesCard.tsx} | 17 +- .../home/{LogsCard.jsx => LogsCard.tsx} | 158 ++++--- ...riesCard.jsx => OwnedRepositoriesCard.tsx} | 17 +- src/components/layout/Logo.tsx | 9 + src/components/layout/MainMenu.tsx | 72 +++ ...zationList.jsx => AllOrganizationList.tsx} | 19 +- ...ionList.jsx => JoinedOrganizationList.tsx} | 21 +- src/components/organization/Organization.jsx | 98 ---- src/components/organization/Organization.tsx | 67 +++ .../organization/OrganizationForm.jsx | 141 ------ .../organization/OrganizationForm.tsx | 177 +++++++ .../organization/OrganizationList.jsx | 35 -- .../organization/OrganizationList.tsx | 29 ++ .../organization/OrganizationListParts.jsx | 179 ------- .../organization/OrganizationListParts.tsx | 152 ++++++ .../OrganizationRepositoryList.jsx | 49 -- .../OrganizationRepositoryList.tsx | 46 ++ .../repository/AllRepositoryList.jsx | 38 -- .../repository/AllRepositoryList.tsx | 35 ++ .../repository/ExportPostmanForm.jsx | 69 --- .../repository/ExportPostmanForm.tsx | 70 +++ .../repository/ImportRepositoryForm.jsx | 99 ---- .../repository/ImportRepositoryForm.tsx | 115 +++++ ...itoryList.jsx => JoinedRepositoryList.tsx} | 31 +- src/components/repository/Repository.jsx | 89 ---- src/components/repository/Repository.tsx | 106 +++++ src/components/repository/RepositoryForm.jsx | 156 ------- src/components/repository/RepositoryForm.tsx | 193 ++++++++ ...{RepositoryList.jsx => RepositoryList.tsx} | 18 +- .../repository/RepositoryListParts.jsx | 159 ------- .../repository/RepositoryListParts.tsx | 127 +++++ src/components/status/Status.sass | 8 +- .../status/{Status.jsx => Status.tsx} | 90 ++-- src/components/tester/Tester.jsx | 170 ------- src/components/tester/Tester.tsx | 161 +++++++ src/components/utils/Dialog.jsx | 75 --- src/components/utils/Dialog.tsx | 68 +++ ...logController.jsx => DialogController.tsx} | 32 +- src/components/utils/Form.jsx | 25 - src/components/utils/Form.tsx | 25 + src/components/utils/MembersInput.jsx | 82 ---- src/components/utils/MembersInput.tsx | 94 ++++ src/components/utils/{Modal.jsx => Modal.tsx} | 47 +- .../{ModalExample.jsx => ModalExample.tsx} | 27 +- .../utils/{NoMatch.jsx => NoMatch.tsx} | 4 +- .../utils/{Pagination.jsx => Pagination.tsx} | 115 ++--- .../utils/{Popover.jsx => Popover.tsx} | 60 +-- src/components/utils/PopoverExample.jsx | 10 - src/components/utils/PopoverExample.tsx | 18 + .../utils/{Portal.jsx => Portal.tsx} | 14 +- ...{PropertyEditor.jsx => PropertyEditor.tsx} | 148 +++--- .../utils/{RChart.jsx => RChart.tsx} | 20 +- src/components/utils/RCodeMirror.jsx | 26 -- src/components/utils/RCodeMirror.tsx | 35 ++ .../utils/{RModal.jsx => RModal.tsx} | 71 +-- .../utils/{RParsley.jsx => RParsley.tsx} | 13 +- src/components/utils/RSortable.jsx | 38 -- src/components/utils/RSortable.tsx | 42 ++ src/components/utils/RSortableExample.jsx | 62 --- src/components/utils/RSortableExample.tsx | 62 +++ src/components/utils/RadioList.jsx | 46 -- src/components/utils/RadioList.tsx | 55 +++ .../{SmartTextarea.jsx => SmartTextarea.tsx} | 22 +- ...eaExample.jsx => SmartTextareaExample.tsx} | 2 +- src/components/utils/Spin.jsx | 12 - src/components/utils/Spin.tsx | 12 + .../{SpinExample.jsx => SpinExample.tsx} | 0 src/components/utils/SpinInline.tsx | 12 + src/components/utils/TagsInput.jsx | 72 --- src/components/utils/TagsInput.tsx | 84 ++++ ...sInputExample.jsx => TagsInputExample.tsx} | 25 +- src/components/utils/{Tree.js => Tree.ts} | 52 +-- src/components/utils/Utils.jsx | 34 -- src/components/utils/Utils.tsx | 34 ++ src/components/utils/{index.js => index.ts} | 0 src/config/{config.dev.js => config.dev.ts} | 8 +- src/config/{config.prod.js => config.prod.ts} | 10 +- src/config/index.js | 4 - src/config/index.ts | 6 + src/family/{Bundle.jsx => Bundle.tsx} | 19 +- src/family/Family.jsx | 106 ----- src/family/Family.tsx | 110 +++++ src/family/UIConst.js | 10 - src/family/UIConst.ts | 13 + src/family/{classNames.js => classNames.ts} | 15 +- .../{handleLocation.js => handleLocation.ts} | 15 +- src/family/index.js | 22 - src/family/index.ts | 28 ++ ...oggerMiddleware.js => loggerMiddleware.ts} | 10 +- src/family/start.jsx | 47 -- src/family/start.tsx | 71 +++ src/{index.js => index.tsx} | 30 +- src/react-app-env.d.ts | 1 + src/relatives/AccountRelative.js | 146 ------ src/relatives/AccountRelative.ts | 159 +++++++ .../{EditorRelative.js => EditorRelative.ts} | 4 +- .../{HomeRelative.js => HomeRelative.ts} | 23 +- ...ionRelative.js => OrganizationRelative.ts} | 54 +-- src/relatives/RepositoryRelative.js | 355 -------------- src/relatives/RepositoryRelative.tsx | 437 ++++++++++++++++++ .../{StatusRelative.js => StatusRelative.ts} | 54 +-- .../effects/{interface.js => interface.ts} | 39 +- .../effects/{module.js => module.ts} | 32 +- .../{organization.js => organization.ts} | 38 +- .../effects/{property.js => property.ts} | 30 +- .../effects/{repository.js => repository.ts} | 43 +- .../services/{Account.js => Account.ts} | 109 +++-- .../services/{Editor.js => Editor.ts} | 90 ++-- .../services/{Foreign.js => Foreign.ts} | 53 +-- .../{Organization.js => Organization.ts} | 28 +- .../services/{Repository.js => Repository.ts} | 26 +- .../services/{Status.js => Status.ts} | 12 +- src/relatives/services/constant.js | 6 - src/relatives/services/constant.ts | 6 + src/routes.jsx | 174 ------- src/routes.tsx | 182 ++++++++ src/selectors/{interface.js => interface.ts} | 13 +- src/selectors/router.js | 8 - src/selectors/router.ts | 9 + src/types/index.d.ts | 7 + src/utils/ImageUtils.ts | 13 + src/utils/{URLUtils.js => URLUtils.ts} | 2 +- tsconfig.json | 65 +++ tslint.json | 127 +++++ 204 files changed, 7912 insertions(+), 6612 deletions(-) create mode 100644 public/logo.svg delete mode 100644 src/actions/account.js create mode 100644 src/actions/account.ts delete mode 100644 src/actions/analytics.js create mode 100644 src/actions/analytics.ts create mode 100644 src/actions/common.ts delete mode 100644 src/actions/interface.js create mode 100644 src/actions/interface.ts delete mode 100644 src/actions/module.js create mode 100644 src/actions/module.ts delete mode 100644 src/actions/organization.js create mode 100644 src/actions/organization.ts delete mode 100644 src/actions/property.js create mode 100644 src/actions/property.ts delete mode 100644 src/actions/repository.js create mode 100644 src/actions/repository.ts create mode 100644 src/actions/types.ts rename src/components/account/{Index.jsx => Index.tsx} (71%) delete mode 100644 src/components/account/LoginForm.jsx create mode 100644 src/components/account/LoginForm.tsx delete mode 100644 src/components/account/Navigation.jsx create mode 100644 src/components/account/Navigation.tsx delete mode 100644 src/components/account/RegisterForm.jsx create mode 100644 src/components/account/RegisterForm.tsx delete mode 100644 src/components/account/ResetForm.jsx delete mode 100644 src/components/account/UpdateForm.jsx rename src/components/account/{User.jsx => User.tsx} (57%) rename src/components/account/{UserList.jsx => UserList.tsx} (68%) rename src/components/api/{API.jsx => API.tsx} (63%) delete mode 100644 src/components/checker/Checker.jsx create mode 100644 src/components/checker/Checker.tsx delete mode 100644 src/components/common/Footer.jsx create mode 100644 src/components/common/Footer.tsx create mode 100644 src/components/common/GlobalStyles.tsx delete mode 100644 src/components/common/Header.jsx delete mode 100644 src/components/common/Header.sass create mode 100644 src/components/common/Header.tsx create mode 100644 src/components/common/LoadingButton.tsx create mode 100644 src/components/common/MuiTheme.tsx delete mode 100644 src/components/common/Navigation.jsx create mode 100644 src/components/common/Navigation.tsx create mode 100644 src/components/common/UserList.tsx delete mode 100644 src/components/editor/DuplicatedInterfacesWarning.jsx create mode 100644 src/components/editor/DuplicatedInterfacesWarning.tsx delete mode 100644 src/components/editor/Importer.jsx create mode 100644 src/components/editor/Importer.tsx delete mode 100644 src/components/editor/InterfaceEditor.jsx create mode 100644 src/components/editor/InterfaceEditor.tsx delete mode 100644 src/components/editor/InterfaceEditorToolbar.jsx create mode 100644 src/components/editor/InterfaceEditorToolbar.tsx delete mode 100644 src/components/editor/InterfaceForm.jsx create mode 100644 src/components/editor/InterfaceForm.tsx delete mode 100644 src/components/editor/InterfaceList.jsx create mode 100644 src/components/editor/InterfaceList.tsx rename src/components/editor/{InterfacePreviewer.jsx => InterfacePreviewer.tsx} (50%) delete mode 100644 src/components/editor/InterfaceSummary.jsx create mode 100644 src/components/editor/InterfaceSummary.tsx delete mode 100644 src/components/editor/ModuleForm.jsx create mode 100644 src/components/editor/ModuleForm.tsx delete mode 100644 src/components/editor/ModuleList.jsx create mode 100644 src/components/editor/ModuleList.tsx delete mode 100644 src/components/editor/MoveInterfaceForm.jsx create mode 100644 src/components/editor/MoveInterfaceForm.tsx delete mode 100644 src/components/editor/PropertyForm.jsx create mode 100644 src/components/editor/PropertyForm.tsx delete mode 100644 src/components/editor/PropertyList.jsx create mode 100644 src/components/editor/PropertyList.tsx rename src/components/editor/{RepositoryEditor.jsx => RepositoryEditor.tsx} (61%) delete mode 100644 src/components/editor/RepositorySearcher.jsx create mode 100644 src/components/editor/RepositorySearcher.tsx delete mode 100644 src/components/home/Home.jsx create mode 100644 src/components/home/Home.tsx rename src/components/home/{JoinedRepositoriesCard.jsx => JoinedRepositoriesCard.tsx} (63%) rename src/components/home/{LogsCard.jsx => LogsCard.tsx} (59%) rename src/components/home/{OwnedRepositoriesCard.jsx => OwnedRepositoriesCard.tsx} (62%) create mode 100644 src/components/layout/Logo.tsx create mode 100644 src/components/layout/MainMenu.tsx rename src/components/organization/{AllOrganizationList.jsx => AllOrganizationList.tsx} (69%) rename src/components/organization/{JoinedOrganizationList.jsx => JoinedOrganizationList.tsx} (76%) delete mode 100644 src/components/organization/Organization.jsx create mode 100644 src/components/organization/Organization.tsx delete mode 100644 src/components/organization/OrganizationForm.jsx create mode 100644 src/components/organization/OrganizationForm.tsx delete mode 100644 src/components/organization/OrganizationList.jsx create mode 100644 src/components/organization/OrganizationList.tsx delete mode 100644 src/components/organization/OrganizationListParts.jsx create mode 100644 src/components/organization/OrganizationListParts.tsx delete mode 100644 src/components/organization/OrganizationRepositoryList.jsx create mode 100644 src/components/organization/OrganizationRepositoryList.tsx delete mode 100644 src/components/repository/AllRepositoryList.jsx create mode 100644 src/components/repository/AllRepositoryList.tsx delete mode 100644 src/components/repository/ExportPostmanForm.jsx create mode 100644 src/components/repository/ExportPostmanForm.tsx delete mode 100644 src/components/repository/ImportRepositoryForm.jsx create mode 100644 src/components/repository/ImportRepositoryForm.tsx rename src/components/repository/{JoinedRepositoryList.jsx => JoinedRepositoryList.tsx} (55%) delete mode 100644 src/components/repository/Repository.jsx create mode 100644 src/components/repository/Repository.tsx delete mode 100644 src/components/repository/RepositoryForm.jsx create mode 100644 src/components/repository/RepositoryForm.tsx rename src/components/repository/{RepositoryList.jsx => RepositoryList.tsx} (52%) delete mode 100644 src/components/repository/RepositoryListParts.jsx create mode 100644 src/components/repository/RepositoryListParts.tsx rename src/components/status/{Status.jsx => Status.tsx} (54%) delete mode 100644 src/components/tester/Tester.jsx create mode 100644 src/components/tester/Tester.tsx delete mode 100644 src/components/utils/Dialog.jsx create mode 100644 src/components/utils/Dialog.tsx rename src/components/utils/{DialogController.jsx => DialogController.tsx} (60%) delete mode 100644 src/components/utils/Form.jsx create mode 100644 src/components/utils/Form.tsx delete mode 100644 src/components/utils/MembersInput.jsx create mode 100644 src/components/utils/MembersInput.tsx rename src/components/utils/{Modal.jsx => Modal.tsx} (72%) rename src/components/utils/{ModalExample.jsx => ModalExample.tsx} (56%) rename src/components/utils/{NoMatch.jsx => NoMatch.tsx} (53%) rename src/components/utils/{Pagination.jsx => Pagination.tsx} (69%) rename src/components/utils/{Popover.jsx => Popover.tsx} (69%) delete mode 100644 src/components/utils/PopoverExample.jsx create mode 100644 src/components/utils/PopoverExample.tsx rename src/components/utils/{Portal.jsx => Portal.tsx} (75%) rename src/components/utils/{PropertyEditor.jsx => PropertyEditor.tsx} (64%) rename src/components/utils/{RChart.jsx => RChart.tsx} (69%) delete mode 100644 src/components/utils/RCodeMirror.jsx create mode 100644 src/components/utils/RCodeMirror.tsx rename src/components/utils/{RModal.jsx => RModal.tsx} (53%) rename src/components/utils/{RParsley.jsx => RParsley.tsx} (69%) delete mode 100644 src/components/utils/RSortable.jsx create mode 100644 src/components/utils/RSortable.tsx delete mode 100644 src/components/utils/RSortableExample.jsx create mode 100644 src/components/utils/RSortableExample.tsx delete mode 100644 src/components/utils/RadioList.jsx create mode 100644 src/components/utils/RadioList.tsx rename src/components/utils/{SmartTextarea.jsx => SmartTextarea.tsx} (57%) rename src/components/utils/{SmartTextareaExample.jsx => SmartTextareaExample.tsx} (64%) delete mode 100644 src/components/utils/Spin.jsx create mode 100644 src/components/utils/Spin.tsx rename src/components/utils/{SpinExample.jsx => SpinExample.tsx} (100%) create mode 100644 src/components/utils/SpinInline.tsx delete mode 100644 src/components/utils/TagsInput.jsx create mode 100644 src/components/utils/TagsInput.tsx rename src/components/utils/{TagsInputExample.jsx => TagsInputExample.tsx} (61%) rename src/components/utils/{Tree.js => Tree.ts} (72%) delete mode 100644 src/components/utils/Utils.jsx create mode 100644 src/components/utils/Utils.tsx rename src/components/utils/{index.js => index.ts} (100%) rename src/config/{config.dev.js => config.dev.ts} (51%) rename src/config/{config.prod.js => config.prod.ts} (51%) delete mode 100644 src/config/index.js create mode 100644 src/config/index.ts rename src/family/{Bundle.jsx => Bundle.tsx} (63%) delete mode 100644 src/family/Family.jsx create mode 100644 src/family/Family.tsx delete mode 100644 src/family/UIConst.js create mode 100644 src/family/UIConst.ts rename src/family/{classNames.js => classNames.ts} (64%) rename src/family/{handleLocation.js => handleLocation.ts} (58%) delete mode 100644 src/family/index.js create mode 100644 src/family/index.ts rename src/family/{loggerMiddleware.js => loggerMiddleware.ts} (58%) delete mode 100644 src/family/start.jsx create mode 100644 src/family/start.tsx rename src/{index.js => index.tsx} (68%) create mode 100644 src/react-app-env.d.ts delete mode 100644 src/relatives/AccountRelative.js create mode 100644 src/relatives/AccountRelative.ts rename src/relatives/{EditorRelative.js => EditorRelative.ts} (93%) rename src/relatives/{HomeRelative.js => HomeRelative.ts} (69%) rename src/relatives/{OrganizationRelative.js => OrganizationRelative.ts} (70%) delete mode 100644 src/relatives/RepositoryRelative.js create mode 100644 src/relatives/RepositoryRelative.tsx rename src/relatives/{StatusRelative.js => StatusRelative.ts} (79%) rename src/relatives/effects/{interface.js => interface.ts} (74%) rename src/relatives/effects/{module.js => module.ts} (69%) rename src/relatives/effects/{organization.js => organization.ts} (70%) rename src/relatives/effects/{property.js => property.ts} (68%) rename src/relatives/effects/{repository.js => repository.ts} (68%) rename src/relatives/services/{Account.js => Account.ts} (54%) rename src/relatives/services/{Editor.js => Editor.ts} (74%) rename src/relatives/services/{Foreign.js => Foreign.ts} (69%) rename src/relatives/services/{Organization.js => Organization.ts} (69%) rename src/relatives/services/{Repository.js => Repository.ts} (72%) rename src/relatives/services/{Status.js => Status.ts} (84%) delete mode 100644 src/relatives/services/constant.js create mode 100644 src/relatives/services/constant.ts delete mode 100644 src/routes.jsx create mode 100644 src/routes.tsx rename src/selectors/{interface.js => interface.ts} (64%) delete mode 100644 src/selectors/router.js create mode 100644 src/selectors/router.ts create mode 100644 src/types/index.d.ts create mode 100644 src/utils/ImageUtils.ts rename src/utils/{URLUtils.js => URLUtils.ts} (88%) create mode 100644 tsconfig.json create mode 100644 tslint.json diff --git a/package.json b/package.json index 4c557ec..082fb3c 100644 --- a/package.json +++ b/package.json @@ -5,15 +5,15 @@ "main": "app.js", "scripts": { "dev": "npm run start", - "build-css": "node-sass src/ -o src/", - "watch-css": "npm run build-css && node-sass src/ -o src/ --watch --recursive", + "build-css": "node-sass src/ -o build/", + "watch-css": "npm run build-css && node-sass src/ -o build/ --watch --recursive", "start-js": "react-scripts start", - "start": "npm-run-all -p watch-css start-js", - "build": "npm run build-css && react-scripts build", + "start": "npm-run-all -p watch-css start-js --max-old-space-size=4096", + "build": "npm run lint && npm run build-css && react-scripts build", "test-backup": "npm run linter && react-scripts test --env=jsdom", "test": "npm run linter", "eject": "react-scripts eject", - "linter": "standard --fix" + "lint": "tslint --project ./ -c tslint.json" }, "repository": { "type": "git", @@ -22,39 +22,50 @@ "author": "mozhi.gyy@alibaba-inc.com", "license": "ISC", "dependencies": { - "animate.css": "3.6.1", - "bootstrap": "^4.1.1", - "chart.js": "^2.6.0", - "codemirror": "5.39.0", - "graceful": "1.0.1", - "jquery": "^3.3.1", - "koa": "2.5.1", + "@material-ui/core": "^4.2.0", + "@material-ui/icons": "^4.2.1", + "@material-ui/pickers": "^3.1.2", + "@material-ui/styles": "^4.2.0", + "animate.css": "3.7.2", + "bootstrap": "^4.3.1", + "chart.js": "^2.8.0", + "classnames": "^2.2.6", + "codemirror": "5.48.0", + "connected-react-router": "^6.5.0", + "debounce-promise": "^3.1.2", + "formik": "^1.5.7", + "formik-material-ui": "^0.0.19", + "graceful": "1.0.2", + "history": "^4.9.0", + "jquery": "^3.4.1", + "koa": "2.7.0", "koa-router": "7.4.0", - "koa-session": "5.8.1", + "koa-session": "5.12.2", "koa-static": "5.0.0", - "lodash": "4.17.10", + "lodash": "4.17.13", "mockjs": "1.0.1-beta3", - "moment": "2.22.2", - "node-fetch": "2.1.2", - "normalizr": "^3.2.4", + "moment": "2.24.0", + "node-fetch": "2.6.0", + "normalizr": "^3.4.0", "nprogress": "0.2.0", - "parsleyjs": "^2.7.2", - "popper.js": "^1.14.3", - "prop-types": "15.6.2", - "react": "16.4.1", - "react-dom": "16.4.1", - "react-icons": "2.2.7", - "react-modal": "3.4.5", - "react-redux": "5.0.7", - "react-router": "4.3.1", - "react-router-config": "1.0.0-beta.4", - "react-router-dom": "4.3.1", - "react-router-redux": "5.0.0-alpha.6", - "redux": "4.0.0", - "redux-saga": "0.16.0", - "reselect": "^3.0.1", - "sortablejs": "1.7.0", - "urijs": "1.19.1" + "parsleyjs": "^2.9.1", + "popper.js": "^1.15.0", + "prop-types": "15.7.2", + "react": "^16.8.6", + "react-dom": "^16.8.6", + "react-icons": "3.7.0", + "react-modal": "3.8.2", + "react-redux": "^7.1.0", + "react-router": "5.0.1", + "react-router-config": "5.0.1", + "react-router-dom": "5.0.1", + "react-select": "^3.0.4", + "redux": "4.0.3", + "redux-saga": "1.0.5", + "reselect": "^4.0.0", + "sortablejs": "1.9.0", + "urijs": "1.19.1", + "yup": "^0.27.0" }, "standard": { "parser": "babel-eslint", @@ -66,10 +77,49 @@ ] }, "devDependencies": { - "babel-eslint": "7.2.3", - "node-sass": "4.9.0", - "npm-run-all": "4.1.3", - "react-scripts": "1.1.4", - "standard": "11.0.1" + "@types/chart.js": "^2.7.55", + "@types/classnames": "^2.2.9", + "@types/codemirror": "^0.0.76", + "@types/history": "^4.7.2", + "@types/jest": "^24.0.15", + "@types/jquery": "^3.3.30", + "@types/lodash": "^4.14.136", + "@types/mockjs": "^1.0.2", + "@types/node": "^12.6.2", + "@types/nprogress": "^0.2.0", + "@types/react": "^16.8.23", + "@types/react-dom": "^16.8.4", + "@types/react-modal": "^3.8.2", + "@types/react-redux": "^7.1.1", + "@types/react-router-config": "^5.0.0", + "@types/react-router-dom": "^4.3.4", + "@types/react-select": "^3.0.0", + "@types/sortablejs": "^1.7.2", + "@types/urijs": "^1.19.3", + "@types/yup": "^0.26.21", + "node-sass": "4.12.0", + "npm-run-all": "4.1.5", + "pre-commit": "^1.2.2", + "react-scripts": "^3.0.1", + "standard": "12.0.1", + "tslint": "^5.18.0", + "tslint-react": "^4.0.0", + "tslint-react-hooks": "^2.1.1", + "typescript": "^3.5.3" + }, + "pre-commit": [ + "lint" + ], + "browserslist": { + "production": [ + ">0.2%", + "not dead", + "not op_mini all" + ], + "development": [ + "last 1 chrome version", + "last 1 firefox version", + "last 1 safari version" + ] } } diff --git a/public/logo.svg b/public/logo.svg new file mode 100644 index 0000000..814e66d --- /dev/null +++ b/public/logo.svg @@ -0,0 +1,19 @@ + + + + logo + Created with Sketch. + + + + \ No newline at end of file diff --git a/src/actions/account.js b/src/actions/account.js deleted file mode 100644 index 8316e20..0000000 --- a/src/actions/account.js +++ /dev/null @@ -1,67 +0,0 @@ -// 登陆 -export const login = (user, onResolved) => ({ type: 'USER_LOGIN', user, onResolved }) -export const loginSucceeded = (user) => ({ type: 'USER_LOGIN_SUCCEEDED', user }) -export const loginFailed = (message) => ({ type: 'USER_LOGIN_FAILED', message }) - -// 重置 -export const reset = (email, password, onResolved) => ({ type: 'USER_RESET', email, password, onResolved }) -export const resetSucceeded = () => ({ type: 'USER_RESET_SUCCEEDED' }) -export const resetFailed = (message) => ({ type: 'USER_RESET_FAILED', message }) - -// 登出 -export const logout = () => ({ type: 'USER_LOGOUT' }) -export const logoutSucceeded = () => ({ type: 'USER_LOGOUT_SUCCEEDED' }) -export const logoutFailed = () => ({ type: 'USER_LOGOUT_FAILED' }) - -// 获取登陆信息 -export const fetchLoginInfo = () => ({ type: 'USER_FETCH_LOGIN_INFO' }) -export const fetchLoginInfoSucceeded = (user) => ({ type: 'USER_FETCH_LOGIN_INFO_SUCCEEDED', user }) -export const fetchLoginInfoFailed = (message) => ({ type: 'USER_FETCH_LOGIN_INFO_FAILED', message }) - -// 注册 -export const addUser = (user, onResolved) => ({ type: 'USER_ADD', user, onResolved }) -export const addUserSucceeded = (user) => ({ type: 'USER_ADD_SUCCEEDED', user }) -export const addUserFailed = (message) => ({ type: 'USER_ADD_FAILED', message }) - -// 更新 -export const updateUser = (user, onResolved) => ({ type: 'USER_UPDATE', user, onResolved }) -export const updateUserSucceeded = (user) => ({ type: 'USER_UPDATE_SUCCEEDED', user }) -export const updateUserFailed = (message) => ({ type: 'USER_UPDATE_FAILED', message }) - -// 删除用户 -export const deleteUser = (id) => ({ type: 'USER_DELETE', id }) -export const deleteUserSucceeded = (id) => ({ type: 'USER_DELETE_SUCCEEDED', id }) -export const deleteUserFailed = (id) => ({ type: 'USER_DELETE_FAILED', id }) - -// 获取用户列表 -export const fetchUserCount = () => ({ type: 'USER_COUNT_FETCH' }) -export const fetchUserCountSucceeded = (count) => ({ type: 'USER_COUNT_FETCH_SUCCEEDED', count }) -export const fetchUserCountFailed = (message) => ({ type: 'USER_COUNT_FETCH_FAILED', message }) -export const fetchUserList = ({ cursor, limit } = {}) => ({ type: 'USER_LIST_FETCH', cursor, limit }) -export const fetchUserListSucceeded = (users) => ({ type: 'USER_LIST_FETCH_SUCCEEDED', users }) -export const fetchUserListFailed = (message) => ({ type: 'USER_LIST_FETCH_FAILED', message }) - -// 获取用户设置 -export const fetchSetting = () => ({ type: 'SETTING_FETCH' }) -export const fetchSettingSucceeded = (setting) => ({ type: 'SETTING_FETCH_SUCCEEDED', setting }) -export const fetchSettingFailed = (message) => ({ type: 'SETTING_FETCH_FAILED', message }) - -// 修改用户设置 -export const updateSetting = (setting) => ({ type: 'SETTING_UPDATE', setting }) -export const updateSettingSucceeded = (setting) => ({ type: 'SETTING_UPDATE', setting }) -export const updateSettingFailed = (message) => ({ type: 'SETTING_UPDATE', message }) - -// 获取用户通知 -export const fetchNotificationList = () => ({ type: 'NOTIFICATION_LIST_FETCH' }) -export const fetchNotificationListSucceeded = () => ({ type: 'NOTIFICATION_LIST_FETCH_SUCCEEDED' }) -export const fetchNotificationListFailed = () => ({ type: 'NOTIFICATION_LIST_FETCH_Failed' }) - -// 阅读用户通知 -export const readNotification = (id) => ({ type: 'NOTIFICATION_READ', id }) -export const readNotificationSucceeded = (id) => ({ type: 'NOTIFICATION_READ_SUCCEEDED', id }) -export const readNotificationFailed = (message) => ({ type: 'NOTIFICATION_READ_FAILED', message }) - -// 获取用户日志 -export const fetchLogList = ({ cursor, limit } = {}) => ({ type: 'LOG_LIST_FETCH', cursor, limit }) -export const fetchLogListSucceeded = (logs) => ({ type: 'LOG_LIST_FETCH_SUCCEEDED', logs }) -export const fetchLogListFailed = (message) => ({ type: 'LOG_LIST_FETCH_FAILED', message }) diff --git a/src/actions/account.ts b/src/actions/account.ts new file mode 100644 index 0000000..1e13920 --- /dev/null +++ b/src/actions/account.ts @@ -0,0 +1,59 @@ +// 登陆 +export const login = (user: any, onResolved: any) => ({ type: 'USER_LOGIN', user, onResolved }) +export const loginSucceeded = (user: any) => ({ type: 'USER_LOGIN_SUCCEEDED', user }) +export const loginFailed = (message: any) => ({ type: 'USER_LOGIN_FAILED', message }) + +// 登出 +export const logout = () => ({ type: 'USER_LOGOUT' }) +export const logoutSucceeded = () => { return ({ type: 'USER_LOGOUT_SUCCEEDED' }) } +export const logoutFailed = () => ({ type: 'USER_LOGOUT_FAILED' }) + +// 获取登陆信息 +export const fetchLoginInfo = () => ({ type: 'USER_FETCH_LOGIN_INFO' }) +export const fetchLoginInfoSucceeded = (user: any) => ({ type: 'USER_FETCH_LOGIN_INFO_SUCCEEDED', user }) +export const fetchLoginInfoFailed = (message: any) => ({ type: 'USER_FETCH_LOGIN_INFO_FAILED', message }) + +// 注册 +export const addUser = (user: any, onResolved: any) => ({ type: 'USER_ADD', user, onResolved }) +export const addUserSucceeded = (user: any) => ({ type: 'USER_ADD_SUCCEEDED', user }) +export const addUserFailed = (message: any) => ({ type: 'USER_ADD_FAILED', message }) + +// 删除用户 +export const deleteUser = (id: any) => ({ type: 'USER_DELETE', id }) +export const deleteUserSucceeded = (id: any) => ({ type: 'USER_DELETE_SUCCEEDED', id }) +export const deleteUserFailed = (id: any) => ({ type: 'USER_DELETE_FAILED', id }) + +// 获取用户列表 +export const fetchUserCount = () => ({ type: 'USER_COUNT_FETCH' }) +export const fetchUserCountSucceeded = (count: any) => ({ type: 'USER_COUNT_FETCH_SUCCEEDED', count }) +export const fetchUserCountFailed = (message: any) => ({ type: 'USER_COUNT_FETCH_FAILED', message }) +export const fetchUserList = ({ cursor, limit } = { cursor: '', limit: ''}) => ({ type: 'USER_LIST_FETCH', cursor, limit }) +export const fetchUserListSucceeded = (users: any) => ({ type: 'USER_LIST_FETCH_SUCCEEDED', users }) +export const fetchUserListFailed = (message: any) => ({ type: 'USER_LIST_FETCH_FAILED', message }) + +// 获取用户设置 +export const fetchSetting = () => ({ type: 'SETTING_FETCH' }) +export const fetchSettingSucceeded = (setting: any) => ({ type: 'SETTING_FETCH_SUCCEEDED', setting }) +export const fetchSettingFailed = (message: any) => ({ type: 'SETTING_FETCH_FAILED', message }) + +// 修改用户设置 +export const updateSetting = (setting: any) => ({ type: 'SETTING_UPDATE', setting }) +export const updateSettingSucceeded = (setting: any) => ({ type: 'SETTING_UPDATE', setting }) +export const updateSettingFailed = (message: any) => ({ type: 'SETTING_UPDATE', message }) + +// 获取用户通知 +export const fetchNotificationList = () => ({ type: 'NOTIFICATION_LIST_FETCH' }) +export const fetchNotificationListSucceeded = () => ({ type: 'NOTIFICATION_LIST_FETCH_SUCCEEDED' }) +export const fetchNotificationListFailed = () => ({ type: 'NOTIFICATION_LIST_FETCH_Failed' }) + +// 阅读用户通知 +export const readNotification = (id: any) => ({ type: 'NOTIFICATION_READ', id }) +export const readNotificationSucceeded = (id: any) => ({ type: 'NOTIFICATION_READ_SUCCEEDED', id }) +export const readNotificationFailed = (message: any) => ({ type: 'NOTIFICATION_READ_FAILED', message }) + +// 获取用户日志 +export const fetchLogList = ( + { cursor, limit } = { cursor: '', limit: '' } +) => ({ type: 'LOG_LIST_FETCH', cursor, limit }) +export const fetchLogListSucceeded = (logs: any) => ({ type: 'LOG_LIST_FETCH_SUCCEEDED', logs }) +export const fetchLogListFailed = (message: any) => ({ type: 'LOG_LIST_FETCH_FAILED', message }) diff --git a/src/actions/analytics.js b/src/actions/analytics.js deleted file mode 100644 index 80aea1f..0000000 --- a/src/actions/analytics.js +++ /dev/null @@ -1,20 +0,0 @@ -// 获取平台计数信息 -export const fetchCounter = () => ({ type: 'ANALYTICS_COUNTER_FETCH' }) -export const fetchCounterSucceeded = (counter) => ({ type: 'ANALYTICS_COUNTER_FETCH_SUCCEEDED', counter }) -export const fetchCounterFailed = (message) => ({ type: 'ANALYTICS_COUNTER_FETCH_FAILED', message }) - -export const fetchAnalyticsRepositoriesCreated = ({ start, end } = {}) => ({ type: 'ANALYTICS_REPOSITORIES_CREATED', start, end }) -export const fetchAnalyticsRepositoriesCreatedSucceeded = (analytics) => ({ type: 'ANALYTICS_REPOSITORIES_CREATED_SUCCEEDED', analytics }) -export const fetchAnalyticsRepositoriesCreatedFailed = (message) => ({ type: 'ANALYTICS_REPOSITORIES_CREATED_FAILED', message }) - -export const fetchAnalyticsRepositoriesUpdated = ({ start, end } = {}) => ({ type: 'ANALYTICS_REPOSITORIES_UPDATED', start, end }) -export const fetchAnalyticsRepositoriesUpdatedSucceeded = (analytics) => ({ type: 'ANALYTICS_REPOSITORIES_UPDATED_SUCCEEDED', analytics }) -export const fetchAnalyticsRepositoriesUpdatedFailed = (message) => ({ type: 'ANALYTICS_REPOSITORIES_UPDATED_FAILED', message }) - -export const fetchAnalyticsUsersActivation = ({ start, end } = {}) => ({ type: 'ANALYTICS_USERS_ACTIVATION', start, end }) -export const fetchAnalyticsUsersActivationSucceeded = (analytics) => ({ type: 'ANALYTICS_USERS_ACTIVATION_SUCCEEDED', analytics }) -export const fetchAnalyticsUsersActivationFailed = (message) => ({ type: 'ANALYTICS_USERS_ACTIVATION_FAILED', message }) - -export const fetchAnalyticsRepositoriesActivation = ({ start, end } = {}) => ({ type: 'ANALYTICS_REPOSITORIES_ACTIVATION', start, end }) -export const fetchAnalyticsRepositoriesActivationSucceeded = (analytics) => ({ type: 'ANALYTICS_REPOSITORIES_ACTIVATION_SUCCEEDED', analytics }) -export const fetchAnalyticsRepositoriesActivationFailed = (message) => ({ type: 'ANALYTICS_REPOSITORIES_ACTIVATION_FAILED', message }) diff --git a/src/actions/analytics.ts b/src/actions/analytics.ts new file mode 100644 index 0000000..abdacd1 --- /dev/null +++ b/src/actions/analytics.ts @@ -0,0 +1,28 @@ +// 获取平台计数信息 +export const fetchCounter = () => ({ type: 'ANALYTICS_COUNTER_FETCH' }) +export const fetchCounterSucceeded = (counter: any) => ({ type: 'ANALYTICS_COUNTER_FETCH_SUCCEEDED', counter }) +export const fetchCounterFailed = (message: any) => ({ type: 'ANALYTICS_COUNTER_FETCH_FAILED', message }) + +export const fetchAnalyticsRepositoriesCreated = ( + { start, end } = { start: '', end: '' } +) => ({ type: 'ANALYTICS_REPOSITORIES_CREATED', start, end }) +export const fetchAnalyticsRepositoriesCreatedSucceeded = (analytics: any) => ({ type: 'ANALYTICS_REPOSITORIES_CREATED_SUCCEEDED', analytics }) +export const fetchAnalyticsRepositoriesCreatedFailed = (message: any) => ({ type: 'ANALYTICS_REPOSITORIES_CREATED_FAILED', message }) + +export const fetchAnalyticsRepositoriesUpdated = ( + { start, end } = { start: '', end: '' } +) => ({ type: 'ANALYTICS_REPOSITORIES_UPDATED', start, end }) +export const fetchAnalyticsRepositoriesUpdatedSucceeded = (analytics: any) => ({ type: 'ANALYTICS_REPOSITORIES_UPDATED_SUCCEEDED', analytics }) +export const fetchAnalyticsRepositoriesUpdatedFailed = (message: any) => ({ type: 'ANALYTICS_REPOSITORIES_UPDATED_FAILED', message }) + +export const fetchAnalyticsUsersActivation = ( + { start, end } = { start: '', end: '' } +) => ({ type: 'ANALYTICS_USERS_ACTIVATION', start, end }) +export const fetchAnalyticsUsersActivationSucceeded = (analytics: any) => ({ type: 'ANALYTICS_USERS_ACTIVATION_SUCCEEDED', analytics }) +export const fetchAnalyticsUsersActivationFailed = (message: any) => ({ type: 'ANALYTICS_USERS_ACTIVATION_FAILED', message }) + +export const fetchAnalyticsRepositoriesActivation = ( + { start, end } = { start: '', end: '' } +) => ({ type: 'ANALYTICS_REPOSITORIES_ACTIVATION', start, end }) +export const fetchAnalyticsRepositoriesActivationSucceeded = (analytics: any) => ({ type: 'ANALYTICS_REPOSITORIES_ACTIVATION_SUCCEEDED', analytics }) +export const fetchAnalyticsRepositoriesActivationFailed = (message: any) => ({ type: 'ANALYTICS_REPOSITORIES_ACTIVATION_FAILED', message }) diff --git a/src/actions/common.ts b/src/actions/common.ts new file mode 100644 index 0000000..6e5b1ea --- /dev/null +++ b/src/actions/common.ts @@ -0,0 +1 @@ +export const refresh = () => ({ type: 'REFRESH' }) diff --git a/src/actions/interface.js b/src/actions/interface.js deleted file mode 100644 index a0114b9..0000000 --- a/src/actions/interface.js +++ /dev/null @@ -1,113 +0,0 @@ -export const addInterface = (itf, onResolved) => ({ - type: 'INTERFACE_ADD', - interface: itf, - onResolved -}) -export const addInterfaceSucceeded = (payload) => ({ - type: 'INTERFACE_ADD_SUCCEEDED', - payload -}) -export const addInterfaceFailed = (message) => ({ - type: 'INTERFACE_ADD_FAILED', - message -}) - -export const updateInterface = (itf, onResolved) => ({ - type: 'INTERFACE_UPDATE', - interface: itf, - onResolved -}) -export const updateInterfaceSucceeded = (payload) => ({ - type: 'INTERFACE_UPDATE_SUCCEEDED', - payload -}) -export const updateInterfaceFailed = (message) => ({ - type: 'INTERFACE_UPDATE_FAILED', - message -}) - -export const moveInterface = (params, onResolved) => ({ - type: 'INTERFACE_MOVE', - params, - onResolved -}) -export const moveInterfaceSucceeded = () => ({ - type: 'INTERFACE_MOVE_SUCCEEDED' -}) -export const moveInterfaceFailed = (message) => ({ - type: 'INTERFACE_MOVE_FAILED', - message -}) - -export const deleteInterface = (id, onResolved) => ({ - type: 'INTERFACE_DELETE', - id, - onResolved -}) -export const deleteInterfaceSucceeded = (payload) => ({ - type: 'INTERFACE_DELETE_SUCCEEDED', - payload -}) -export const deleteInterfaceFailed = (message) => ({ - type: 'INTERFACE_DELETE_FAILED', - message -}) - -export const fetchInterfaceCount = () => ({ - type: 'INTERFACE_COUNT_FETCH' -}) -export const fetchInterfaceCountSucceeded = (count) => ({ - type: 'INTERFACE_COUNT_FETCH_SUCCEEDED', - count -}) -export const fetchInterfaceCountFailed = (message) => ({ - type: 'INTERFACE_COUNT_FETCH_FAILED', - message -}) - -export const lockInterface = (id, onResolved) => ({ - type: 'INTERFACE_LOCK', - id, - onResolved -}) -export const lockInterfaceSucceeded = (itfId, locker) => ({ - type: 'INTERFACE_LOCK_SUCCEEDED', - payload: { - itfId, - locker - } -}) -export const lockInterfaceFailed = (message) => ({ - type: 'INTERFACE_LOCK_FAILED', - message -}) - -export const unlockInterface = (id, onResolved) => ({ - type: 'INTERFACE_UNLOCK', - id, - onResolved -}) -export const unlockInterfaceSucceeded = (itfId) => ({ - type: 'INTERFACE_UNLOCK_SUCCEEDED', - payload: { - itfId - }, -}) -export const unlockInterfaceFailed = (message) => ({ - type: 'INTERFACE_UNLOCK_FAILED', - message -}) - -export const sortInterfaceList = (ids, onResolved) => ({ - type: 'INTERFACE_LIST_SORT', - ids, - onResolved -}) -export const sortInterfaceListSucceeded = (count) => ({ - type: 'INTERFACE_LIST_SORT_SUCCEEDED', - count -}) -export const sortInterfaceListFailed = (message) => ({ - type: 'INTERFACE_LIST_SORT_FAILED', - message -}) \ No newline at end of file diff --git a/src/actions/interface.ts b/src/actions/interface.ts new file mode 100644 index 0000000..242a863 --- /dev/null +++ b/src/actions/interface.ts @@ -0,0 +1,113 @@ +export const addInterface = (itf: any, onResolved: any) => ({ + type: 'INTERFACE_ADD', + interface: itf, + onResolved, +}) +export const addInterfaceSucceeded = (payload: any) => ({ + type: 'INTERFACE_ADD_SUCCEEDED', + payload, +}) +export const addInterfaceFailed = (message: any) => ({ + type: 'INTERFACE_ADD_FAILED', + message, +}) + +export const updateInterface = (itf: any, onResolved: any) => ({ + type: 'INTERFACE_UPDATE', + interface: itf, + onResolved, +}) +export const updateInterfaceSucceeded = (payload: any) => ({ + type: 'INTERFACE_UPDATE_SUCCEEDED', + payload, +}) +export const updateInterfaceFailed = (message: any) => ({ + type: 'INTERFACE_UPDATE_FAILED', + message, +}) + +export const moveInterface = (params: any, onResolved: any) => ({ + type: 'INTERFACE_MOVE', + params, + onResolved, +}) +export const moveInterfaceSucceeded = () => ({ + type: 'INTERFACE_MOVE_SUCCEEDED', +}) +export const moveInterfaceFailed = (message: any) => ({ + type: 'INTERFACE_MOVE_FAILED', + message, +}) + +export const deleteInterface = (id: any, onResolved: any) => ({ + type: 'INTERFACE_DELETE', + id, + onResolved, +}) +export const deleteInterfaceSucceeded = (payload: any) => ({ + type: 'INTERFACE_DELETE_SUCCEEDED', + payload, +}) +export const deleteInterfaceFailed = (message: any) => ({ + type: 'INTERFACE_DELETE_FAILED', + message, +}) + +export const fetchInterfaceCount = () => ({ + type: 'INTERFACE_COUNT_FETCH', +}) +export const fetchInterfaceCountSucceeded = (count: any) => ({ + type: 'INTERFACE_COUNT_FETCH_SUCCEEDED', + count, +}) +export const fetchInterfaceCountFailed = (message: any) => ({ + type: 'INTERFACE_COUNT_FETCH_FAILED', + message, +}) + +export const lockInterface = (id: any, onResolved?: any) => ({ + type: 'INTERFACE_LOCK', + id, + onResolved, +}) +export const lockInterfaceSucceeded = (itfId: any, locker: any) => ({ + type: 'INTERFACE_LOCK_SUCCEEDED', + payload: { + itfId, + locker, + }, +}) +export const lockInterfaceFailed = (message: any) => ({ + type: 'INTERFACE_LOCK_FAILED', + message, +}) + +export const unlockInterface = (id: any, onResolved?: any) => ({ + type: 'INTERFACE_UNLOCK', + id, + onResolved, +}) +export const unlockInterfaceSucceeded = (itfId: any) => ({ + type: 'INTERFACE_UNLOCK_SUCCEEDED', + payload: { + itfId, + }, +}) +export const unlockInterfaceFailed = (message: any) => ({ + type: 'INTERFACE_UNLOCK_FAILED', + message, +}) + +export const sortInterfaceList = (ids: any, onResolved: any) => ({ + type: 'INTERFACE_LIST_SORT', + ids, + onResolved, +}) +export const sortInterfaceListSucceeded = (count: any) => ({ + type: 'INTERFACE_LIST_SORT_SUCCEEDED', + count, +}) +export const sortInterfaceListFailed = (message: any) => ({ + type: 'INTERFACE_LIST_SORT_FAILED', + message, +}) diff --git a/src/actions/module.js b/src/actions/module.js deleted file mode 100644 index 18b5048..0000000 --- a/src/actions/module.js +++ /dev/null @@ -1,56 +0,0 @@ -export const addModule = (module, onResolved) => ({ - type: 'MODULE_ADD', - module, - onResolved -}) -export const addModuleSucceeded = (module) => ({ - type: 'MODULE_ADD_SUCCEEDED', - module -}) -export const addModuleFailed = (message) => ({ - type: 'MODULE_ADD_FAILED', - message -}) - -export const updateModule = (module, onResolved) => ({ - type: 'MODULE_UPDATE', - module, - onResolved -}) -export const updateModuleSucceeded = (payload) => ({ - type: 'MODULE_UPDATE_SUCCEEDED', - payload -}) -export const updateModuleFailed = (message) => ({ - type: 'MODULE_UPDATE_FAILED', - message -}) - -export const deleteModule = (id, onResolved, repoId) => ({ - type: 'MODULE_DELETE', - id, - onResolved, - repoId -}) -export const deleteModuleSucceeded = (id) => ({ - type: 'MODULE_DELETE_SUCCEEDED', - id -}) -export const deleteModuleFailed = (message) => ({ - type: 'MODULE_DELETE_FAILED', - message -}) - -export const sortModuleList = (ids, onResolved) => ({ - type: 'MODULE_LIST_SORT', - ids, - onResolved -}) -export const sortModuleListSucceeded = (count) => ({ - type: 'MODULE_LIST_SORT_SUCCEEDED', - count -}) -export const sortModuleListFailed = (message) => ({ - type: 'MODULE_LIST_SORT_FAILED', - message -}) \ No newline at end of file diff --git a/src/actions/module.ts b/src/actions/module.ts new file mode 100644 index 0000000..6eb5785 --- /dev/null +++ b/src/actions/module.ts @@ -0,0 +1,56 @@ +export const addModule = (module: any, onResolved: any) => ({ + type: 'MODULE_ADD', + module, + onResolved, +}) +export const addModuleSucceeded = (module: any) => ({ + type: 'MODULE_ADD_SUCCEEDED', + module, +}) +export const addModuleFailed = (message: any) => ({ + type: 'MODULE_ADD_FAILED', + message, +}) + +export const updateModule = (module: any, onResolved: any) => ({ + type: 'MODULE_UPDATE', + module, + onResolved, +}) +export const updateModuleSucceeded = (payload: any) => ({ + type: 'MODULE_UPDATE_SUCCEEDED', + payload, +}) +export const updateModuleFailed = (message: any) => ({ + type: 'MODULE_UPDATE_FAILED', + message, +}) + +export const deleteModule = (id: any, onResolved: any, repoId: any) => ({ + type: 'MODULE_DELETE', + id, + onResolved, + repoId, +}) +export const deleteModuleSucceeded = (id: any) => ({ + type: 'MODULE_DELETE_SUCCEEDED', + id, +}) +export const deleteModuleFailed = (message: any) => ({ + type: 'MODULE_DELETE_FAILED', + message, +}) + +export const sortModuleList = (ids: any, onResolved: any) => ({ + type: 'MODULE_LIST_SORT', + ids, + onResolved, +}) +export const sortModuleListSucceeded = (count: any) => ({ + type: 'MODULE_LIST_SORT_SUCCEEDED', + count, +}) +export const sortModuleListFailed = (message: any) => ({ + type: 'MODULE_LIST_SORT_FAILED', + message, +}) diff --git a/src/actions/organization.js b/src/actions/organization.js deleted file mode 100644 index dddcd06..0000000 --- a/src/actions/organization.js +++ /dev/null @@ -1,31 +0,0 @@ -export const addOrganization = (organization, onResolved) => ({ type: 'ORGANIZATION_ADD', organization, onResolved }) -export const addOrganizationSucceeded = (organization) => ({ type: 'ORGANIZATION_ADD_SUCCEEDED', organization }) -export const addOrganizationFailed = (message) => ({ type: 'ORGANIZATION_ADD_FAILED', message }) - -export const updateOrganization = (organization, onResolved) => ({ type: 'ORGANIZATION_UPDATE', organization, onResolved }) -export const updateOrganizationSucceeded = (organization) => ({ type: 'ORGANIZATION_UPDATE_SUCCEEDED', organization }) -export const updateOrganizationFailed = (message) => ({ type: 'ORGANIZATION_UPDATE_FAILED', message }) - -export const deleteOrganization = (id, onResolved) => ({ type: 'ORGANIZATION_DELETE', id, onResolved }) -export const deleteOrganizationSucceeded = (id) => ({ type: 'ORGANIZATION_DELETE_SUCCEEDED', id }) -export const deleteOrganizationFailed = (message) => ({ type: 'ORGANIZATION_DELETE_FAILED', message }) - -export const fetchOrganization = ({ id, organization } = {}) => ({ type: 'ORGANIZATION_FETCH', id, organization }) -export const fetchOrganizationSucceeded = (organization) => ({ type: 'ORGANIZATION_FETCH_SUCCEEDED', organization }) -export const fetchOrganizationFailed = (message) => ({ type: 'ORGANIZATION_FETCH_FAILED', message }) - -export const fetchOrganizationCount = () => ({ type: 'ORGANIZATION_COUNT_FETCH' }) -export const fetchOrganizationCountSucceeded = (count) => ({ type: 'ORGANIZATION_COUNT_FETCH_SUCCEEDED', count }) -export const fetchOrganizationCountFailed = (message) => ({ type: 'ORGANIZATION_COUNT_FETCH_FAILED', message }) - -export const fetchOwnedOrganizationList = ({ name } = {}) => ({ type: 'OWNED_ORGANIZATION_LIST_FETCH', name }) -export const fetchOwnedOrganizationListSucceeded = (organizations) => ({ type: 'OWNED_ORGANIZATION_LIST_FETCH_SUCCEEDED', organizations }) -export const fetchOwnedOrganizationListFailed = (message) => ({ type: 'OWNED_ORGANIZATION_LIST_FETCH_FAILED', message }) - -export const fetchJoinedOrganizationList = ({ name } = {}) => ({ type: 'JOINED_ORGANIZATION_LIST_FETCH', name }) -export const fetchJoinedOrganizationListSucceeded = (organizations) => ({ type: 'JOINED_ORGANIZATION_LIST_FETCH_SUCCEEDED', organizations }) -export const fetchJoinedOrganizationListFailed = (message) => ({ type: 'JOINED_ORGANIZATION_LIST_FETCH_FAILED', message }) - -export const fetchOrganizationList = ({ name, cursor, limit } = {}) => ({ type: 'ORGANIZATION_LIST_FETCH', name, cursor, limit }) -export const fetchOrganizationListSucceeded = (organizations) => ({ type: 'ORGANIZATION_LIST_FETCH_SUCCEEDED', organizations }) -export const fetchOrganizationListFailed = (message) => ({ type: 'ORGANIZATION_LIST_FETCH_FAILED', message }) diff --git a/src/actions/organization.ts b/src/actions/organization.ts new file mode 100644 index 0000000..1a480bd --- /dev/null +++ b/src/actions/organization.ts @@ -0,0 +1,33 @@ +export const addOrganization = (organization: any, onResolved: any) => ({ type: 'ORGANIZATION_ADD', organization, onResolved }) +export const addOrganizationSucceeded = (organization: any) => ({ type: 'ORGANIZATION_ADD_SUCCEEDED', organization }) +export const addOrganizationFailed = (message: any) => ({ type: 'ORGANIZATION_ADD_FAILED', message }) + +export const updateOrganization = (organization: any, onResolved: any) => ({ type: 'ORGANIZATION_UPDATE', organization, onResolved }) +export const updateOrganizationSucceeded = (organization: any) => ({ type: 'ORGANIZATION_UPDATE_SUCCEEDED', organization }) +export const updateOrganizationFailed = (message: any) => ({ type: 'ORGANIZATION_UPDATE_FAILED', message }) + +export const deleteOrganization = (id: any, onResolved: any) => ({ type: 'ORGANIZATION_DELETE', id, onResolved }) +export const deleteOrganizationSucceeded = (id: any) => ({ type: 'ORGANIZATION_DELETE_SUCCEEDED', id }) +export const deleteOrganizationFailed = (message: any) => ({ type: 'ORGANIZATION_DELETE_FAILED', message }) + +export const fetchOrganization = ({ id, organization } = { id: '', organization: ''}) => ({ type: 'ORGANIZATION_FETCH', id, organization }) +export const fetchOrganizationSucceeded = (organization: any) => ({ type: 'ORGANIZATION_FETCH_SUCCEEDED', organization }) +export const fetchOrganizationFailed = (message: any) => ({ type: 'ORGANIZATION_FETCH_FAILED', message }) + +export const fetchOrganizationCount = () => ({ type: 'ORGANIZATION_COUNT_FETCH' }) +export const fetchOrganizationCountSucceeded = (count: any) => ({ type: 'ORGANIZATION_COUNT_FETCH_SUCCEEDED', count }) +export const fetchOrganizationCountFailed = (message: any) => ({ type: 'ORGANIZATION_COUNT_FETCH_FAILED', message }) + +export const fetchOwnedOrganizationList = ({ name } = { name: ''}) => ({ type: 'OWNED_ORGANIZATION_LIST_FETCH', name }) +export const fetchOwnedOrganizationListSucceeded = (organizations: any) => ({ type: 'OWNED_ORGANIZATION_LIST_FETCH_SUCCEEDED', organizations }) +export const fetchOwnedOrganizationListFailed = (message: any) => ({ type: 'OWNED_ORGANIZATION_LIST_FETCH_FAILED', message }) + +export const fetchJoinedOrganizationList = ({ name } = { name: ''}) => ({ type: 'JOINED_ORGANIZATION_LIST_FETCH', name }) +export const fetchJoinedOrganizationListSucceeded = (organizations: any) => ({ type: 'JOINED_ORGANIZATION_LIST_FETCH_SUCCEEDED', organizations }) +export const fetchJoinedOrganizationListFailed = (message: any) => ({ type: 'JOINED_ORGANIZATION_LIST_FETCH_FAILED', message }) + +export const fetchOrganizationList = ( + { name, cursor, limit } = { name: '', cursor: '', limit: '' } +) => ({ type: 'ORGANIZATION_LIST_FETCH', name, cursor, limit }) +export const fetchOrganizationListSucceeded = (organizations: any) => ({ type: 'ORGANIZATION_LIST_FETCH_SUCCEEDED', organizations }) +export const fetchOrganizationListFailed = (message: any) => ({ type: 'ORGANIZATION_LIST_FETCH_FAILED', message }) diff --git a/src/actions/property.js b/src/actions/property.js deleted file mode 100644 index 47c84fb..0000000 --- a/src/actions/property.js +++ /dev/null @@ -1,71 +0,0 @@ -export const addProperty = (property, onResolved) => ({ - type: 'PROPERTY_ADD', - property, - onResolved -}) -export const addPropertySucceeded = (property) => ({ - type: 'PROPERTY_ADD_SUCCEEDED', - property -}) -export const addPropertyFailed = (message) => ({ - type: 'PROPERTY_ADD_FAILED', - message -}) - -export const updateProperty = (property, onResolved) => ({ - type: 'PROPERTY_UPDATE', - property, - onResolved -}) -export const updatePropertySucceeded = (property) => ({ - type: 'PROPERTY_UPDATE_SUCCEEDED', - property -}) -export const updatePropertyFailed = (message) => ({ - type: 'PROPERTY_UPDATE_FAILED', - message -}) - -export const updateProperties = (itf, properties, summary, onResolved) => ({ - type: 'PROPERTIES_UPDATE', - itf, - properties, - summary, - onResolved -}) -export const updatePropertiesSucceeded = (payload) => ({ - type: 'PROPERTIES_UPDATE_SUCCEEDED', - payload -}) -export const updatePropertiesFailed = (message) => ({ - type: 'PROPERTIES_UPDATE_FAILED', - message -}) - -export const deleteProperty = (id, onResolved) => ({ - type: 'PROPERTY_DELETE', - id, - onResolved -}) -export const deletePropertySucceeded = (id) => ({ - type: 'PROPERTY_DELETE_SUCCEEDED', - id -}) -export const deletePropertyFailed = (message) => ({ - type: 'PROPERTY_DELETE_FAILED', - message -}) - -export const sortPropertyList = (ids, onResolved) => ({ - type: 'PROPERTY_LIST_SORT', - ids, - onResolved -}) -export const sortPropertyListSucceeded = (count) => ({ - type: 'PROPERTY_LIST_SORT_SUCCEEDED', - count -}) -export const sortPropertyListFailed = (message) => ({ - type: 'PROPERTY_LIST_SORT_FAILED', - message -}) \ No newline at end of file diff --git a/src/actions/property.ts b/src/actions/property.ts new file mode 100644 index 0000000..c906f96 --- /dev/null +++ b/src/actions/property.ts @@ -0,0 +1,71 @@ +export const addProperty = (property: any, onResolved: any) => ({ + type: 'PROPERTY_ADD', + property, + onResolved, +}) +export const addPropertySucceeded = (property: any) => ({ + type: 'PROPERTY_ADD_SUCCEEDED', + property, +}) +export const addPropertyFailed = (message: any) => ({ + type: 'PROPERTY_ADD_FAILED', + message, +}) + +export const updateProperty = (property: any, onResolved: any) => ({ + type: 'PROPERTY_UPDATE', + property, + onResolved, +}) +export const updatePropertySucceeded = (property: any) => ({ + type: 'PROPERTY_UPDATE_SUCCEEDED', + property, +}) +export const updatePropertyFailed = (message: any) => ({ + type: 'PROPERTY_UPDATE_FAILED', + message, +}) + +export const updateProperties = (itf: any, properties: any, summary: any, onResolved: any) => ({ + type: 'PROPERTIES_UPDATE', + itf, + properties, + summary, + onResolved, +}) +export const updatePropertiesSucceeded = (payload: any) => ({ + type: 'PROPERTIES_UPDATE_SUCCEEDED', + payload, +}) +export const updatePropertiesFailed = (message: any) => ({ + type: 'PROPERTIES_UPDATE_FAILED', + message, +}) + +export const deleteProperty = (id: any, onResolved: any) => ({ + type: 'PROPERTY_DELETE', + id, + onResolved, +}) +export const deletePropertySucceeded = (id: any) => ({ + type: 'PROPERTY_DELETE_SUCCEEDED', + id, +}) +export const deletePropertyFailed = (message: any) => ({ + type: 'PROPERTY_DELETE_FAILED', + message, +}) + +export const sortPropertyList = (ids: any, onResolved: any) => ({ + type: 'PROPERTY_LIST_SORT', + ids, + onResolved, +}) +export const sortPropertyListSucceeded = (count: any) => ({ + type: 'PROPERTY_LIST_SORT_SUCCEEDED', + count, +}) +export const sortPropertyListFailed = (message: any) => ({ + type: 'PROPERTY_LIST_SORT_FAILED', + message, +}) diff --git a/src/actions/repository.js b/src/actions/repository.js deleted file mode 100644 index 0cff93d..0000000 --- a/src/actions/repository.js +++ /dev/null @@ -1,37 +0,0 @@ -export const addRepository = (repository, onResolved) => ({ type: 'REPOSITORY_ADD', repository, onResolved }) -export const addRepositorySucceeded = (repository) => ({ type: 'REPOSITORY_ADD_SUCCEEDED', repository }) -export const addRepositoryFailed = (message) => ({ type: 'REPOSITORY_ADD_FAILED', message }) - -export const importRepository = (data, onResolved) => ({ type: 'REPOSITORY_IMPORT', onResolved, data }) -export const importRepositorySucceeded = () => ({ type: 'REPOSITORY_IMPORT_SUCCEEDED' }) -export const importRepositoryFailed = (message) => ({ type: 'REPOSITORY_IMPORT_FAILED', message }) - -export const updateRepository = (repository, onResolved) => ({ type: 'REPOSITORY_UPDATE', repository, onResolved }) -export const updateRepositorySucceeded = (repository) => ({ type: 'REPOSITORY_UPDATE_SUCCEEDED', repository }) -export const updateRepositoryFailed = (message) => ({ type: 'REPOSITORY_UPDATE_FAILED', message }) - -export const deleteRepository = (id) => ({ type: 'REPOSITORY_DELETE', id }) -export const deleteRepositorySucceeded = (id) => ({ type: 'REPOSITORY_DELETE_SUCCEEDED', id }) -export const deleteRepositoryFailed = (message) => ({ type: 'REPOSITORY_DELETE_FAILED', message }) - -export const fetchRepository = ({ id, repository } = {}) => ({ type: 'REPOSITORY_FETCH', id, repository }) -export const fetchRepositorySucceeded = (repository) => ({ type: 'REPOSITORY_FETCH_SUCCEEDED', repository }) -export const fetchRepositoryFailed = (message) => ({ type: 'REPOSITORY_FETCH_FAILED', message }) - -export const clearRepository = () => ({ type: 'REPOSITORY_CLEAR' }) - -export const fetchRepositoryCount = () => ({ type: 'REPOSITORY_COUNT_FETCH' }) -export const fetchRepositoryCountSucceeded = (count) => ({ type: 'REPOSITORY_COUNT_FETCH_SUCCEEDED', count }) -export const fetchRepositoryCountFailed = (message) => ({ type: 'REPOSITORY_COUNT_FETCH_FAILED', message }) - -export const fetchRepositoryList = ({ user, organization, name, cursor, limit } = {}) => ({ type: 'REPOSITORY_LIST_FETCH', user, organization, name, cursor, limit }) -export const fetchRepositoryListSucceeded = (repositories) => ({ type: 'REPOSITORY_LIST_FETCH_SUCCEEDED', repositories }) -export const fetchRepositoryListFailed = (message) => ({ type: 'REPOSITORY_LIST_FETCH_FAILED', message }) - -export const fetchOwnedRepositoryList = ({ user, name } = {}) => ({ type: 'OWNED_REPOSITORY_LIST_FETCH', user, name }) -export const fetchOwnedRepositoryListSucceeded = (repositories) => ({ type: 'OWNED_REPOSITORY_LIST_FETCH_SUCCEEDED', repositories }) -export const fetchOwnedRepositoryListFailed = (message) => ({ type: 'OWNED_REPOSITORY_LIST_FETCH_FAILED', message }) - -export const fetchJoinedRepositoryList = ({ user, name } = {}) => ({ type: 'JOINED_REPOSITORY_LIST_FETCH', user, name }) -export const fetchJoinedRepositoryListSucceeded = (repositories) => ({ type: 'JOINED_REPOSITORY_LIST_FETCH_SUCCEEDED', repositories }) -export const fetchJoinedRepositoryListFailed = (message) => ({ type: 'JOINED_REPOSITORY_LIST_FETCH_FAILED', message }) diff --git a/src/actions/repository.ts b/src/actions/repository.ts new file mode 100644 index 0000000..8f6c8b6 --- /dev/null +++ b/src/actions/repository.ts @@ -0,0 +1,48 @@ +export const addRepository = (repository: any, onResolved: any) => ({ type: 'REPOSITORY_ADD', repository, onResolved }) +export const addRepositorySucceeded = (repository: any) => ({ type: 'REPOSITORY_ADD_SUCCEEDED', repository }) +export const addRepositoryFailed = (message: any) => ({ type: 'REPOSITORY_ADD_FAILED', message }) + +export const importRepository = (data: any, onResolved: any) => ({ type: 'REPOSITORY_IMPORT', onResolved, data }) +export const importRepositorySucceeded = () => ({ type: 'REPOSITORY_IMPORT_SUCCEEDED' }) +export const importRepositoryFailed = (message: any) => ({ type: 'REPOSITORY_IMPORT_FAILED', message }) + +export const updateRepository = (repository: any, onResolved: any) => ({ type: 'REPOSITORY_UPDATE', repository, onResolved }) +export const updateRepositorySucceeded = (repository: any) => ({ type: 'REPOSITORY_UPDATE_SUCCEEDED', repository }) +export const updateRepositoryFailed = (message: any) => ({ type: 'REPOSITORY_UPDATE_FAILED', message }) + +export const deleteRepository = (id: any) => ({ type: 'REPOSITORY_DELETE', id }) +export const deleteRepositorySucceeded = (id: any) => ({ type: 'REPOSITORY_DELETE_SUCCEEDED', id }) +export const deleteRepositoryFailed = (message: any) => ({ type: 'REPOSITORY_DELETE_FAILED', message }) + +export const fetchRepository = ({ id, repository }: any) => ({ type: 'REPOSITORY_FETCH', id, repository }) +export const fetchRepositorySucceeded = (repository: any) => ({ type: 'REPOSITORY_FETCH_SUCCEEDED', repository }) +export const fetchRepositoryFailed = (message: any) => ({ type: 'REPOSITORY_FETCH_FAILED', message }) + +export const clearRepository = () => ({ type: 'REPOSITORY_CLEAR' }) + +export const fetchRepositoryCount = () => ({ type: 'REPOSITORY_COUNT_FETCH' }) +export const fetchRepositoryCountSucceeded = (count: any) => ({ type: 'REPOSITORY_COUNT_FETCH_SUCCEEDED', count }) +export const fetchRepositoryCountFailed = (message: any) => ({ type: 'REPOSITORY_COUNT_FETCH_FAILED', message }) + +export const fetchRepositoryList = ({ user, organization, name, cursor, limit } = {user: '', organization: '', name: '', cursor: '', limit: ''}) => ({ type: 'REPOSITORY_LIST_FETCH', user, organization, name, cursor, limit }) +export const fetchRepositoryListSucceeded = (repositories: any) => ({ type: 'REPOSITORY_LIST_FETCH_SUCCEEDED', repositories }) +export const fetchRepositoryListFailed = (message: any) => ({ type: 'REPOSITORY_LIST_FETCH_FAILED', message }) +export const fetchOwnedRepositoryList = ( + { user, name } = { user: '', name: '' } +) => ({ type: 'OWNED_REPOSITORY_LIST_FETCH', user, name }) +export const fetchOwnedRepositoryListSucceeded = (repositories: any) => ({ type: 'OWNED_REPOSITORY_LIST_FETCH_SUCCEEDED', repositories }) +export const fetchOwnedRepositoryListFailed = (message: any) => ({ type: 'OWNED_REPOSITORY_LIST_FETCH_FAILED', message }) + +export const fetchJoinedRepositoryList = ( + { user, name } = { user: '', name: '' } +) => ({ type: 'JOINED_REPOSITORY_LIST_FETCH', user, name }) +export const fetchJoinedRepositoryListSucceeded = (repositories: any) => ({ type: 'JOINED_REPOSITORY_LIST_FETCH_SUCCEEDED', repositories }) +export const fetchJoinedRepositoryListFailed = (message: any) => ({ type: 'JOINED_REPOSITORY_LIST_FETCH_FAILED', message }) + +export const fetchForeignInterfaces = ({ id, itf } = {id: '', itf: ''}) => ({ type: 'FOREIGN_INTERFACE_FETCH', id, itf }) +export const fetchForeignInterfacesSuccessed = (data: any) => ({ type: 'FOREIGN_INTERFACE_FETCH_SUCCEEDED', data }) +export const fetchForeignInterfacesFailed = (message: any) => ({ type: 'FOREIGN_INTERFACE_FETCH_FAILED', message }) + +export const addForeignRoomCase = ({ id, itf, name } = { id: '', itf: '', name: ''}) => ({ type: 'ADD_FOREIGN_ROOM_CASE', id, itf, name }) +export const addForeignRoomCaseSuccessed = ({ id, itf } = { id: '', itf: ''}) => ({ type: 'ADD_FOREIGN_ROOM_CASE_SUCCEEDED', id, itf }) +export const addForeignRoomCaseFailed = ({ id, itf, message }: any) => ({ type: 'ADD_FOREIGN_ROOM_CASE_FAILED', id, itf, message }) diff --git a/src/actions/types.ts b/src/actions/types.ts new file mode 100644 index 0000000..eef94ea --- /dev/null +++ b/src/actions/types.ts @@ -0,0 +1,66 @@ +import { RouterState } from 'connected-react-router' + +export interface RootState { + auth: any + router: RouterState + repository: any + repositories: any + organization: any + organizations: any + ownedRepositories: any + ownedOrganizations: any + joinedOrganizations: any + joinedRepositories: any + users: any + + counter: any + logs: any + foreign: any + loading: boolean +} + +export interface Organization { + + id: number + + name: string + + description?: string + + logo?: string + + /** true: 公开, false: 私有 */ + visibility?: boolean + + creatorId?: number + + ownerId?: number + + memberIds?: number[] + + members?: User[] + + owner?: User + + newOwner?: User + +} + +export interface User { + id: number + fullname: string + email: string +} + +export interface INumItem { + value: number + label: string +} + +export interface IConfig { + serve: string + keys: string[] + session: { + key: string + } +} diff --git a/src/assets/index.sass b/src/assets/index.sass index 4c1c8db..3ab3cc0 100644 --- a/src/assets/index.sass +++ b/src/assets/index.sass @@ -17,7 +17,6 @@ body line-height: 1.5; font-family: $font-family; -webkit-font-smoothing: antialiased; - color: #24292e; html, body height: 100%; @@ -45,7 +44,6 @@ html, body button, input, optgroup, select, textarea font-size: 1.2rem; font-family: $font-family; - color: #24292e; -webkit-font-smoothing: antialiased; // * @@ -86,4 +84,7 @@ pre border: 1px solid #DDDDDD; border-radius: 16px; border-bottom-left-radius: 0; - border-bottom-right-radius: 0; \ No newline at end of file + border-bottom-right-radius: 0; + +.mr8 + margin-right: 8px; \ No newline at end of file diff --git a/src/assets/variables.sass b/src/assets/variables.sass index 0fc58bf..ad1b7da 100644 --- a/src/assets/variables.sass +++ b/src/assets/variables.sass @@ -4,7 +4,6 @@ $brand: #4A7BF7; $brand-hover: #4471E3; -$nav-bg: #24292e; $nav-color: rgba(255, 255, 255, 0.75); $nav-hover: rgba(255, 255, 255, 1); $table-hover: #F0F3FA; diff --git a/src/components/account/Index.jsx b/src/components/account/Index.tsx similarity index 71% rename from src/components/account/Index.jsx rename to src/components/account/Index.tsx index 8140a1a..ee0ef8d 100644 --- a/src/components/account/Index.jsx +++ b/src/components/account/Index.tsx @@ -2,16 +2,15 @@ import React from 'react' import { connect } from 'react-redux' import { renderRoutes } from 'react-router-config' -const Account = ({ route, match, location }) => ( +const Account = ({ route }: any) => (
{route && renderRoutes(route.routes)}
) -const mapStateToProps = (state) => ({}) const mapDispatchToProps = ({}) export default connect( - mapStateToProps, + null, mapDispatchToProps )(Account) diff --git a/src/components/account/LoginForm.jsx b/src/components/account/LoginForm.jsx deleted file mode 100644 index 8377eff..0000000 --- a/src/components/account/LoginForm.jsx +++ /dev/null @@ -1,91 +0,0 @@ -import React, { Component } from 'react' -import { PropTypes, connect } from '../../family' -import { login } from '../../actions/account' -import { Link } from 'react-router-dom' -import { serve } from '../../config' -import Mock from 'mockjs' -import './LoginForm.css' - -// 模拟数据 -const mockUser = process.env.NODE_ENV === 'development' - ? () => Mock.mock({ - email: 'admin@rap2.com', - password: 'admin' - }) - : () => ({ - email: '', - password: '' - }) - -mockUser.captchaId = Date.now() - -// 展示组件 -class LoginForm extends Component { - static contextTypes = { - store: PropTypes.object.isRequired - } - static propTypes = { - auth: PropTypes.object.isRequired - } - constructor (props) { - super(props) - this.state = mockUser() - } - render () { - return ( -
-
- 登录 -
-
-
-
- - this.setState({ email: e.target.value })} className='form-control' placeholder='Email' autoFocus='true' required /> -
-
- - this.setState({ password: e.target.value })} className='form-control' placeholder='Password' required /> -
-
- - this.setState({ captcha: e.target.value })} className='form-control' placeholder='验证码' required /> - this.setState({ captchaId: Date.now() })} alt='captcha' /> -
-
-
- - 注册 - 密码找回 -
- {this.props.auth.message && -
- {this.props.auth.message} -
- } -
-
- ) - } - handleSubmit = (e) => { - let { history, onLogin } = this.props - e.preventDefault() - onLogin(this.state, () => { - let { pathname } = history.location - if (pathname !== '/account/login') history.push(pathname) // 如果用户在其他业务页面,则不跳转 - else history.push('/') // 跳转到用户面板 - }) - } -} - -// 容器组件 -const mapStateToProps = (state) => ({ - auth: state.auth -}) -const mapDispatchToProps = ({ - onLogin: login -}) -export default connect( - mapStateToProps, - mapDispatchToProps -)(LoginForm) diff --git a/src/components/account/LoginForm.sass b/src/components/account/LoginForm.sass index 349dd88..b319c4a 100644 --- a/src/components/account/LoginForm.sass +++ b/src/components/account/LoginForm.sass @@ -1,22 +1,27 @@ @import "../../assets/variables.sass"; // 水平居中 + 垂直居中 -.LoginForm - position: fixed; - left: 50%; - top: 50%; - width: 25rem; - margin-left: -12.5rem; - margin-top: -135px; - border: 1px solid #E6E6E6; - border-radius: .5rem; - .header - padding: 1.5rem 3rem; - border-bottom: 1px solid $border; - .title - font-size: 2rem; - .body - padding: 1.5rem 3rem; - .footer - padding: 1.5rem 3rem; - border-top: 1px solid $border; +.wrapper + background-size: cover; + width: 100% + height: 100% + min-height: 800px + .LoginForm + position: fixed; + left: 50%; + top: 30%; + width: 25rem; + margin-left: -12.5rem; + margin-top: -135px; + border: 1px solid #E6E6E6; + border-radius: .5rem; + .header + padding: 1.5rem 3rem; + border-bottom: 1px solid $border; + .title + font-size: 2rem; + .body + padding: 1.5rem 3rem; + .footer + padding: 1.5rem 3rem; + border-top: 1px solid $border; diff --git a/src/components/account/LoginForm.tsx b/src/components/account/LoginForm.tsx new file mode 100644 index 0000000..e055058 --- /dev/null +++ b/src/components/account/LoginForm.tsx @@ -0,0 +1,109 @@ +import React, { Component } from 'react' +import { connect } from 'react-redux' +import { login } from '../../actions/account' +import { Link } from 'react-router-dom' +import Mock from 'mockjs' +import './LoginForm.css' +import { RootState } from 'actions/types' +import config from '../../config' +import { Button, Card } from '@material-ui/core' +import { getBGImageUrl } from 'utils/ImageUtils' +import Logo from 'components/layout/Logo' + +const { serve } = config + +// 模拟数据 +const mockUser: any = process.env.NODE_ENV === 'development' + ? () => Mock.mock({ + email: 'admin@rap2.com', + password: 'admin', + }) + : () => ({ + email: '', + password: '', + }) + +mockUser.captchaId = Date.now() + +// 展示组件 +class LoginForm extends Component { + constructor(props: any) { + super(props) + this.state = { + ...mockUser(), + bg: getBGImageUrl(), + } + } + render() { + return ( +
+ +
+ +
+
+
+
+ + this.setState({ email: e.target.value })} + className="form-control" + placeholder="Email" + autoFocus={true} + required={true} + /> +
+
+ + this.setState({ password: e.target.value })} + className="form-control" + placeholder="Password" + required={true} + /> +
+
+ + this.setState({ captcha: e.target.value })} className="form-control" placeholder="验证码" required={true} /> + this.setState({ captchaId: Date.now() })} alt="captcha" /> +
+
+
+ + 注册 + {/* 密码找回 */} +
+ {this.props.auth.message && +
+ {this.props.auth.message} +
+ } +
+
+
+ ) + } + handleSubmit = (e: any) => { + const { onLogin } = this.props + e.preventDefault() + const { email, password, captcha } = this.state + onLogin({ email, password, captcha }, () => { + window.location.href = '/' + }) + } +} + +// 容器组件 +const mapStateToProps = (state: RootState) => ({ + auth: state.auth, +}) +const mapDispatchToProps = ({ + onLogin: login, +}) +export default connect( + mapStateToProps, + mapDispatchToProps +)(LoginForm) diff --git a/src/components/account/Navigation.jsx b/src/components/account/Navigation.jsx deleted file mode 100644 index 66e2c15..0000000 --- a/src/components/account/Navigation.jsx +++ /dev/null @@ -1,19 +0,0 @@ -import React from 'react' -import { NavLink } from 'react-router-dom' -import { connect } from 'react-redux' - -const Nav = ({ route, match, location }) => ( - -) - -const mapStateToProps = (state) => ({}) -const mapDispatchToProps = ({}) - -export default connect( - mapStateToProps, - mapDispatchToProps -)(Nav) diff --git a/src/components/account/Navigation.tsx b/src/components/account/Navigation.tsx new file mode 100644 index 0000000..5553b26 --- /dev/null +++ b/src/components/account/Navigation.tsx @@ -0,0 +1,18 @@ +import React from 'react' +import { NavLink } from 'react-router-dom' +import { connect } from 'react-redux' + +const Nav = () => ( + +) + +const mapDispatchToProps = ({}) + +export default connect( + null, + mapDispatchToProps +)(Nav) diff --git a/src/components/account/RegisterForm.jsx b/src/components/account/RegisterForm.jsx deleted file mode 100644 index e2d8266..0000000 --- a/src/components/account/RegisterForm.jsx +++ /dev/null @@ -1,79 +0,0 @@ -import React, { Component } from 'react' -import { connect } from 'react-redux' -import { Link } from 'react-router-dom' -import Mock from 'mockjs' -import { addUser } from '../../actions/account' -import './RegisterForm.css' - -// 模拟数据 -const mockUser = process.env.NODE_ENV === 'development' - ? () => Mock.mock({ - fullname: '@CNAME', - email: '@email', - password: '@string(6)', - errMsg: '', - }) - : () => ({ - fullname: '', - email: '', - password: '', - errMsg: '', - }) - -// 展示组件 -class RegisterForm extends Component { - constructor (props) { - super(props) - this.state = mockUser() - } - render () { - const { errMsg } = this.state - return ( -
-
- 注册 -
-
-
- - this.setState({ fullname: e.target.value })} className='form-control' placeholder='Name' autoFocus='true' required /> -
-
- - this.setState({ email: e.target.value })} className='form-control' placeholder='Email' required /> -
-
- - this.setState({ password: e.target.value })} type='password' className='form-control' placeholder='Password' required /> -
- {errMsg &&
{errMsg}
} - - 取消 -
-
- ) - } - handleSubmit = (e) => { - let { history, onAddUser } = this.props - e.preventDefault() - onAddUser(this.state, (errorOccurred, errMsg) => { - if (errorOccurred) { - this.setState({ - errMsg, - }) - } else { - history.push('/') // 另一种方式是 - } - }) - } -} - -// 容器组件 -const mapStateToProps = (state) => ({}) -const mapDispatchToProps = ({ - onAddUser: addUser -}) -export default connect( - mapStateToProps, - mapDispatchToProps -)(RegisterForm) diff --git a/src/components/account/RegisterForm.sass b/src/components/account/RegisterForm.sass index d3a896d..b546800 100644 --- a/src/components/account/RegisterForm.sass +++ b/src/components/account/RegisterForm.sass @@ -1,9 +1,27 @@ -.RegisterForm - width: 30rem; - margin: 0 auto; - .header - margin-bottom: 3rem; - .title - font-size: 2rem; - .body - .footer \ No newline at end of file +@import "../../assets/variables.sass"; +// 水平居中 + 垂直居中 +.wrapper + background-size: cover; + width: 100% + height: 100% + min-height: 800p + .RegisterForm + position: fixed; + left: 50%; + top: 30%; + width: 25rem; + margin-left: -12.5rem; + margin-top: -135px; + border: 1px solid #E6E6E6; + border-radius: .5rem; + .header + padding: 1.5rem 3rem; + border-bottom: 1px solid $border; + .title + font-size: 2rem; + .body + padding: 1.5rem 3rem; + .footer + padding: 1.5rem 3rem; + border-top: 1px solid $border; + diff --git a/src/components/account/RegisterForm.tsx b/src/components/account/RegisterForm.tsx new file mode 100644 index 0000000..6387990 --- /dev/null +++ b/src/components/account/RegisterForm.tsx @@ -0,0 +1,102 @@ +import React, { Component } from 'react' +import { connect } from 'react-redux' +import { Link } from 'react-router-dom' +import Mock from 'mockjs' +import { addUser } from '../../actions/account' +import './RegisterForm.css' +import { Button, Card } from '@material-ui/core' +import { getBGImageUrl } from 'utils/ImageUtils' + +type State = { + fullname: string + email: string + password: string + bg: string +} + +type Props = { + history: any + onAddUser: any +} + +// 模拟数据 +const mockUser = () => + Mock.mock({ + fullname: '@CNAME', + email: '@email', + password: '@string(6)', + }) + +// 展示组件 +class RegisterForm extends Component { + constructor(props: any) { + super(props) + this.state = { + ...mockUser(), + bg: getBGImageUrl(), + } + } + render() { + return ( +
+ +
+ 注册 +
+
+
+ + this.setState({ fullname: e.target.value })} + className="form-control" + placeholder="Name" + autoFocus={true} + required={true} + /> +
+
+ + this.setState({ email: e.target.value })} + className="form-control" + placeholder="Email" + required={true} + /> +
+
+ + this.setState({ password: e.target.value })} + type="password" + className="form-control" + placeholder="Password" + required={true} + /> +
+ + 取消 +
+
+
+ ) + } + handleSubmit = (e: any) => { + const { history, onAddUser } = this.props + e.preventDefault() + onAddUser(this.state, () => { + history.push('/') + }) + }; +} + +// 容器组件 +const mapDispatchToProps = { + onAddUser: addUser, +} +export default connect( + null, + mapDispatchToProps +)(RegisterForm) diff --git a/src/components/account/ResetForm.jsx b/src/components/account/ResetForm.jsx deleted file mode 100644 index f4e79c4..0000000 --- a/src/components/account/ResetForm.jsx +++ /dev/null @@ -1,107 +0,0 @@ -import React, { Component } from 'react' -import { PropTypes, connect } from '../../family' -import { reset } from '../../actions/account' -import { Link } from 'react-router-dom' -import './ResetForm.css' - -// 展示组件 -class LoginForm extends Component { - static contextTypes = { - store: PropTypes.object.isRequired - } - static propTypes = { - auth: PropTypes.object.isRequired - } - constructor(props) { - super(props) - this.state = { - email: '', - password: '', - showPassword: false, - newPassword: '', - } - } - render() { - const { showPassword, email, password, newPassword } = this.state - if (newPassword) { - return ( -
-
- 找回密码 -
-
-
- 您的密码已重设为: {newPassword}, 请重新登陆。 -
-
-
- ) - } - return ( -
-
- 找回密码 -
-
-
-
- - this.setState({ email: e.target.value })} className='form-control' placeholder='Email' autoFocus='true' required disabled={showPassword} /> -
- {showPassword &&
-
- - this.setState({ password: e.target.value })} className='form-control' placeholder='验证码' /> -
-
- 已发送验证码至您输入的邮箱,请输入验证码以重置您的密码。 -
-
} -
-
- - 返回 -
- {this.props.auth.message && -
- {this.props.auth.message} -
- } -
-
- ) - } - handleSubmit = (e) => { - const { onReset } = this.props - const { email, password } = this.state - e.preventDefault() - if (email) { - this.setState({ - showPassword: true, - }) - onReset(email, password, (result) => { - if (!result.isOk) { - alert(result.errMsg) - return - } - if (result.data) { - this.setState({ - newPassword: result.data, - }) - } - }) - } - } -} - -// 容器组件 -const mapStateToProps = (state) => ({ - auth: state.auth -}) -const mapDispatchToProps = ({ - onReset: reset, -}) -export default connect( - mapStateToProps, - mapDispatchToProps -)(LoginForm) diff --git a/src/components/account/UpdateForm.jsx b/src/components/account/UpdateForm.jsx deleted file mode 100644 index b238029..0000000 --- a/src/components/account/UpdateForm.jsx +++ /dev/null @@ -1,76 +0,0 @@ -import React, { Component } from 'react' -import { PropTypes, connect } from '../../family' -import { updateUser } from '../../actions/account' -import './UpdateForm.css' - -// 展示组件 -class UpdateForm extends Component { - static propTypes = { - auth: PropTypes.object.isRequired - } - constructor (props) { - super(props) - this.state = { - fullname: '', - email: '', - password: '' - } - } - render () { - const auth = this.props.auth - return ( -
-
- 个人资料修改 -
-
-
- - -
-
- - -
-
- - this.setState({ password: e.target.value })} type='password' className='form-control' placeholder='Password' required autoFocus='true' /> -
-
- - this.setState({ passwordConfirm: e.target.value })} type='password' className='form-control' placeholder='Password' required autoFocus='true' /> -
- - { e.preventDefault(); window.history.go(-1) }} >取消 -
-
- ) - } - handleSubmit = (e) => { - let { history, onUpdateUser } = this.props - e.preventDefault() - if (this.state.password && this.state.password === this.state.passwordConfirm) { - onUpdateUser({ password: this.state.password }, (errMsg) => { - if (errMsg) { - window.alert(errMsg) - } else { - history.push('/') - } - }) - } else { - window.alert('两次密码不一致,请检查后重新输入') - } - } -} - -// 容器组件 -const mapStateToProps = (state) => ({ - auth: state.auth -}) -const mapDispatchToProps = ({ - onUpdateUser: updateUser -}) -export default connect( - mapStateToProps, - mapDispatchToProps -)(UpdateForm) diff --git a/src/components/account/User.jsx b/src/components/account/User.tsx similarity index 57% rename from src/components/account/User.jsx rename to src/components/account/User.tsx index 46300b6..cc6ceb5 100644 --- a/src/components/account/User.jsx +++ b/src/components/account/User.tsx @@ -1,13 +1,13 @@ import React from 'react' import { Link } from 'react-router-dom' -const User = ({ match, id, fullname, email, onDeleteUser }) => ( +const User = ({ match, id, fullname, email, onDeleteUser }: any) => ( {id} {fullname} {email} - onDeleteUser(id)}>X + onDeleteUser(id)}>X ) diff --git a/src/components/account/UserList.jsx b/src/components/account/UserList.tsx similarity index 68% rename from src/components/account/UserList.jsx rename to src/components/account/UserList.tsx index 00f990b..98025f1 100644 --- a/src/components/account/UserList.jsx +++ b/src/components/account/UserList.tsx @@ -4,14 +4,15 @@ import { connect } from 'react-redux' import Pagination from '../utils/Pagination' import { addUser, deleteUser, fetchUserList } from '../../actions/account' import './UserList.css' +import { RootState } from 'actions/types' // 展示组件 -const UserList = ({ history, match, location, users, onAddUser, onDeleteUser, onFetchUserList, tmpl = {} }) => ( -
-
- 用户管理 +const UserList = ({ match, location, users, onDeleteUser }: any) => ( +
+
+ 用户管理
-
*/} -
- +
+
- + - {users.data.map(user => + {users.data.map((user: any) => @@ -45,20 +46,20 @@ const UserList = ({ history, match, location, users, onAddUser, onDeleteUser, on
姓名 邮箱操作操作
#{user.id} {user.fullname} @@ -37,7 +38,7 @@ const UserList = ({ history, match, location, users, onAddUser, onDeleteUser, on {user.email} - onDeleteUser(user.id)} className='operation disabled'>删除 + onDeleteUser(user.id)} className="operation disabled">删除
-
+
) // 容器组件 -const mapStateToProps = (state) => ({ - users: state.users +const mapStateToProps = (state: RootState) => ({ + users: state.users, }) const mapDispatchToProps = ({ onAddUser: addUser, onDeleteUser: deleteUser, - onFetchUserList: fetchUserList + onFetchUserList: fetchUserList, }) export default connect( mapStateToProps, diff --git a/src/components/api/API.sass b/src/components/api/API.sass index 6f3930e..8e8b25f 100644 --- a/src/components/api/API.sass +++ b/src/components/api/API.sass @@ -1,5 +1,6 @@ .APIList padding: 2rem; + margin: 16px; > .header margin-bottom: 2rem; .title diff --git a/src/components/api/API.jsx b/src/components/api/API.tsx similarity index 63% rename from src/components/api/API.jsx rename to src/components/api/API.tsx index 9e11c00..0e27d0e 100644 --- a/src/components/api/API.jsx +++ b/src/components/api/API.tsx @@ -1,6 +1,7 @@ import React from 'react' import { serve } from '../../relatives/services/constant' import './API.css' +import { Paper } from '@material-ui/core' const ExampleJQuery = () => (
@@ -10,7 +11,7 @@ const ExampleJQuery = () => (
  • 最后引入RAP jQuery插件
  • 示例代码

    -
    +    
           {
             '\n' +
             '\n' +
    @@ -31,70 +32,84 @@ const ExampleJQuery = () => (
     
     // DONE 2.3 区分请求和响应作用域
     
    -class API extends React.Component {
    -  constructor (props) {
    +type State = {
    +  showExampleJQuery: boolean;
    +}
    +
    +type Props = any
    +
    +class API extends React.Component {
    +
    +  constructor(props: any) {
         super(props)
         this.state = {
    -      showExampleJQuery: false
    +      showExampleJQuery: false,
         }
       }
    -  render () {
    +  render() {
         return (
    -      
    -
    - 用户手册 + +
    + 用户手册
    -
    -
    + -
    - 平台API接口 +
    + 平台API接口
    -
    -
    -
    获取仓库的完整数据(JSON)
    +
    +
    +
    获取仓库的完整数据(JSON)
    • {serve}/repository/get?id=:repositoryId
    -
    -
    获取接口的完整数据(JSON)
    +
    +
    获取接口的完整数据(JSON)
    • {serve}/interface/get?id=:interfaceId
    -
    -
    获取仓库的前端插件(JS)
    +
    +
    获取仓库的前端插件(JS)
      -
    • 基础插件{serve}/app/plugin/:repositories
    • -
    • jQuery 插件{serve}/libs/jquery.rap.js
    • +
    • 基础插件{serve}/app/plugin/:repositories
    • +
    • jQuery 插件{serve}/libs/jquery.rap.js + +
    • {this.state.showExampleJQuery && } -
    • Mock.js 插件{serve}/libs/mock.rap.js
    • -
    • fetch 插件{serve}/libs/fetch.rap.js
    • +
    • Mock.js 插件{serve}/libs/mock.rap.js
    • +
    • fetch 插件{serve}/libs/fetch.rap.js
    -
    -
    获取单个接口的数据(JSON)
    +
    +
    获取单个接口的数据(JSON)
    • {serve}/app/mock/data/:interfaceId?scope=response|request - +
      - + @@ -113,15 +128,15 @@ class API extends React.Component {
    • {serve}/app/mock/:repositoryId/:method/:url
    • -
      -
      获取单个接口的模板(JSON)
      +
      +
      获取单个接口的模板(JSON)
      • {serve}/app/mock/template/:interfaceId?scope=response|request -
      scopescope 描述
      +
      - + @@ -139,15 +154,15 @@ class API extends React.Component { -
      -
      获取单个接口的模板(JSON Schema)
      +
      +
      获取单个接口的模板(JSON Schema)
      • {serve}/app/mock/schema/:interfaceId?scope=response|request -
      scopescope 描述
      +
      - + @@ -166,7 +181,7 @@ class API extends React.Component { - + ) } } diff --git a/src/components/checker/Checker.jsx b/src/components/checker/Checker.jsx deleted file mode 100644 index 8360328..0000000 --- a/src/components/checker/Checker.jsx +++ /dev/null @@ -1,107 +0,0 @@ -import React, { Component } from 'react' -import { PropTypes, connect, Link } from '../../family' -import { Spin } from '../utils' -import { serve } from '../../relatives/services/constant' -import Mock from 'mockjs' -import './Checker.css' - -class Checker extends Component { - static contextTypes = { - store: PropTypes.object.isRequired - } - static propTypes = { - repository: PropTypes.object.isRequired - } - constructor (props) { - super(props) - this.state = { - mod: null, - itf: null, - target: serve - } - } - render () { - let { repository } = this.props - if (repository.fetching) return - - repository = repository.data - let mod = this.state.mod || repository.modules[0] - let itf = this.state.itf || mod.interfaces[0] - return ( -
      -
      - 模块: - {repository.modules.map(item => - this.switchMod(e, item)} className={item.id === mod.id ? 'active' : ''}>{item.name} - )} -
      -
      - 接口: - {mod.interfaces.map(item => - this.switchItf(e, item)} className={item.id === itf.id ? 'active' : ''}>{item.name} - )} -
      -
      - this.setState({ target: e.target.value })} className='form-control' /> -
      -
      -
      {`${serve}/app/mock/data/${itf.id}`}
      -
      {JSON.stringify(this.state.result, null, 2)}
      -
      -
      - ) - } - componentWillReceiveProps (nextProps) { - let { repository } = nextProps - repository = repository.data - if (!repository.id) return - let mod = this.state.mod || repository.modules[0] - let itf = this.state.itf || mod.interfaces[0] - fetch(`${serve}/app/mock/data/${itf.id}`) - .then(res => res.json()) - .then(json => { - this.setState({ result: json }) - }) - } - switchMod = (e, mod) => { - e.preventDefault() - this.setState({ mod }) - } - switchItf = (e, itf) => { - e.preventDefault() - this.setState({ itf }, () => { - this.handleRequest() - }) - } - handleRequest = () => { - let { repositoryId, method, url } = this.state.itf - let target = `${this.state.target}/app/mock/${repositoryId}/${method}/${url}` - let proxy = `${serve}/proxy?target=${target}` - let requests = [ - fetch(`${serve}/app/mock/schema/${this.state.itf.id}`).then(res => res.json()), - fetch(proxy).then(res => res.json()) - ] - Promise.all(requests).then(([schema, data]) => { - let { Diff, Assert } = Mock.valid - let nextMatch = Assert.match - Assert.match = function (type, path, actual, expected, result, message) { - if (typeof expected === 'string') expected = eval('(' + expected + ')') // eslint-disable-line no-eval - nextMatch(type, path, actual, expected, result, message) - } - var result = Diff.diff(schema, data) - for (var i = 0; i < result.length; i++) { - console.warn(Assert.message(result[i])) - } - }) - } -} -// 容器组件 -const mapStateToProps = (state) => ({ - auth: state.auth, - repository: state.repository -}) -const mapDispatchToProps = ({}) -export default connect( - mapStateToProps, - mapDispatchToProps -)(Checker) diff --git a/src/components/checker/Checker.tsx b/src/components/checker/Checker.tsx new file mode 100644 index 0000000..b56ae32 --- /dev/null +++ b/src/components/checker/Checker.tsx @@ -0,0 +1,110 @@ +import React, { Component } from 'react' +// eslint-disable-next-line +import { connect, Link } from '../../family' +import { Spin } from '../utils' +import { serve } from '../../relatives/services/constant' +import Mock from 'mockjs' +import './Checker.css' +import { RootState } from 'actions/types' +type CheckerProps = { + store: object; + repository: any; +} +type CheckerState = { + result?: any; + itf?: any; + mod: any; + target: any +} +class Checker extends Component { + constructor(props: any) { + super(props) + this.state = { + mod: null, + itf: null, + target: serve, + } + } + render() { + let { repository } = this.props + if (repository.fetching) { return } + repository = repository.data + const mod = this.state.mod || repository.modules[0] + const itf = this.state.itf || mod.interfaces[0] + return ( +
      +
      + 模块: + {repository.modules.map((item: any) => ( + this.switchMod(e, item)} className={item.id === mod.id ? 'active' : ''}> + {item.name} + + ))} +
      +
      + 接口: + {mod.interfaces.map((item: any) => ( + this.switchItf(e, item)} className={item.id === itf.id ? 'active' : ''}> + {item.name} + + ))} +
      +
      + this.setState({ target: e.target.value })} className="form-control" /> +
      +
      +
      {`${serve}/app/mock/data/${itf.id}`}
      +
      {JSON.stringify(this.state.result, null, 2)}
      +
      +
      + ) + } + componentWillReceiveProps(nextProps: any) { + let { repository } = nextProps + repository = repository.data + if (!repository.id) { return } + const mod = this.state.mod || repository.modules[0] + const itf = this.state.itf || mod.interfaces[0] + fetch(`${serve}/app/mock/data/${itf.id}`) + .then(res => res.json()) + .then(json => { + this.setState({ result: json }) + }) + } + switchMod = (e: any, mod: any) => { + e.preventDefault() + this.setState({ mod }) + }; + switchItf = (e: any, itf: any) => { + e.preventDefault() + this.setState({ itf }, () => { + this.handleRequest() + }) + }; + handleRequest = () => { + const { repositoryId, method, url } = this.state.itf + const target = `${this.state.target}/app/mock/${repositoryId}/${method}/${url}` + const proxy = `${serve}/proxy?target=${target}` + const requests = [fetch(`${serve}/app/mock/schema/${this.state.itf.id}`).then(res => res.json()), fetch(proxy).then(res => res.json())] + Promise.all(requests).then(([schema, data]) => { + const { Diff, Assert } = Mock.valid as any + const nextMatch = Assert.match + Assert.match = (type: any, path: any, actual: any, expected: any, result: any, message: any) => { + // eslint-disable-next-line + if (typeof expected === 'string') { expected = eval('(' + expected + ')') } + nextMatch(type, path, actual, expected, result, message) + } + const result = Diff.diff(schema, data) + for (const i of result) { + console.warn(Assert.message(i)) + } + }) + }; +} +// 容器组件 +const mapStateToProps = (state: RootState) => ({ + auth: state.auth, + repository: state.repository, +}) +const mapDispatchToProps = {} +export default connect(mapStateToProps, mapDispatchToProps)(Checker) diff --git a/src/components/common/Footer.jsx b/src/components/common/Footer.jsx deleted file mode 100644 index cd0f50f..0000000 --- a/src/components/common/Footer.jsx +++ /dev/null @@ -1,29 +0,0 @@ -import React from 'react' -import { connect } from 'react-redux' -import './Footer.css' - -const Footer = ({ counter = {} }) => ( -
      - v{counter.version} - {/* | - {counter.users} 人正在使用 RAP - | - 今日 Mock 服务被调用 {counter.mock} 次 */} - -
      -) - -const mapStateToProps = (state) => ({ - counter: state.counter -}) -const mapDispatchToProps = ({}) - -export default connect( - mapStateToProps, - mapDispatchToProps -)(Footer) diff --git a/src/components/common/Footer.tsx b/src/components/common/Footer.tsx new file mode 100644 index 0000000..06371e1 --- /dev/null +++ b/src/components/common/Footer.tsx @@ -0,0 +1,30 @@ +import React from 'react' +import { connect } from 'react-redux' +import './Footer.css' +import { RootState } from 'actions/types' + +const Footer = ({ counter = {} }: { counter: any }) => ( +
      + {counter && counter.version} + {/* | + {counter.users} 人正在使用 RAP + | + 今日 Mock 服务被调用 {counter.mock} 次 */} + +
      +) + +const mapStateToProps = (state: RootState) => ({ + counter: state.counter, +}) +const mapDispatchToProps = ({}) + +export default connect( + mapStateToProps, + mapDispatchToProps +)(Footer) diff --git a/src/components/common/GlobalStyles.tsx b/src/components/common/GlobalStyles.tsx new file mode 100644 index 0000000..45c9636 --- /dev/null +++ b/src/components/common/GlobalStyles.tsx @@ -0,0 +1,30 @@ +import grey from '@material-ui/core/colors/grey' +import { StyleRules, Theme } from '@material-ui/core/styles' + +const GlobalStyles = (theme: Theme) => ({ + '@global': { + 'body': { + margin: 0, + padding: 0, + backgroundColor: grey[200], + fontFamily: '"Roboto", "Helvetica", "Arial", sans-serif;', + }, + '.ml1': { + marginLeft: theme.spacing(1), + }, + '.mr1': { + marginRight: theme.spacing(1), + }, + 'ol': { + padding: `0 ${theme.spacing(2)}px`, + }, + 'ul': { + padding: `0 ${theme.spacing(2)}px`, + }, + 'li': { + padding: `${theme.spacing(1)}px 0`, + }, + }, +}) as StyleRules + +export default GlobalStyles diff --git a/src/components/common/Header.jsx b/src/components/common/Header.jsx deleted file mode 100644 index 75594c0..0000000 --- a/src/components/common/Header.jsx +++ /dev/null @@ -1,61 +0,0 @@ -import React from 'react' -import { connect } from 'react-redux' -import { Link } from 'react-router-dom' -import { login, logout } from '../../actions/account' -import Navigation from './Navigation' -import './Header.css' - -import NProgress from 'nprogress' -import 'nprogress/nprogress.css' -import './nprogress.css' - -const LoginAction = () => ( - Sign in -) -const RegisterAction = () => ( - Sign up -) -/* eslint-enable no-unused-vars */ - -const Header = ({ match, location, onLogout, fetching, user = {} }) => { - document.body.style.cursor = fetching ? 'wait' : 'default' // TODO 2.3 应该有更好的方式监听整个 APP 是否有未完成的请求! - fetching ? NProgress.start() : NProgress.done() - return ( -
      - -
      - ) -} - -// 容器组件 -const mapStateToProps = (state) => ({ - fetching: (() => { - let fetching = 0 - for (let key in state) { - if (state[key].fetching) fetching += 1 - } - return fetching - })(), // state.fetching - user: state.auth -}) -const mapDispatchToProps = ({ - onLogin: login, - onLogout: logout -}) -export default connect( - mapStateToProps, - mapDispatchToProps -)(Header) diff --git a/src/components/common/Header.sass b/src/components/common/Header.sass deleted file mode 100644 index d346456..0000000 --- a/src/components/common/Header.sass +++ /dev/null @@ -1,38 +0,0 @@ -@import "../../assets/variables.sass"; - -.Header - background-color: $nav-bg; - color: white; - font-size: 1.4rem; - line-height: 4rem; - vertical-align: middle; - // margin-bottom: 2rem; - padding: 0 2rem; - ul.nav-links - float: left; - ul.nav-actions - float: right; - ul.nav-links, - ul.nav-actions - margin: 0; - padding: 0; - list-style: none; - > li - float: left; - padding: 0 1rem; - > a - color: white; - opacity: 0.5; - &:hover, &.selected - border: none; - font-weight: bold; - opacity: 1; - > a.logo - opacity: 1; - font-weight: bold; - .avatar - width: 2.5rem; - height: 2.5rem; - border-radius: 50%; - .name - margin-left: .5rem; diff --git a/src/components/common/Header.tsx b/src/components/common/Header.tsx new file mode 100644 index 0000000..ce00ae7 --- /dev/null +++ b/src/components/common/Header.tsx @@ -0,0 +1,39 @@ +import React from 'react' +import { connect } from 'react-redux' +import NProgress from 'nprogress' +import 'nprogress/nprogress.css' +import './nprogress.css' +import MainMenu from 'components/layout/MainMenu' +const Header = ({ fetching, user = {} }: any) => { + if (!user || !user.id) { + return null + } + document.body.style.cursor = fetching ? 'wait' : 'default' // TODO 2.3 应该有更好的方式监听整个 APP 是否有未完成的请求! + fetching ? NProgress.start() : NProgress.done() + return ( +
      + +
      + ) +} + +const mapStateToProps = (state: any) => ({ + fetching: (() => { + let fetching = 0 + for (const key in state) { + if (state[key] && state[key].fetching) { fetching += 1 } + } + return fetching + })(), // state.fetching + user: state.auth, +}) + +const mapDispatchToProps = ({ +}) + +export default connect( + mapStateToProps, + mapDispatchToProps +)(Header) diff --git a/src/components/common/LoadingButton.tsx b/src/components/common/LoadingButton.tsx new file mode 100644 index 0000000..0f1481e --- /dev/null +++ b/src/components/common/LoadingButton.tsx @@ -0,0 +1,42 @@ +import React from 'react' +import { Button, makeStyles, createStyles, CircularProgress } from '@material-ui/core' +import { ButtonProps } from '@material-ui/core/Button' +import { green } from '@material-ui/core/colors' + +const useStyles = makeStyles(() => + createStyles({ + root: { + position: 'relative', + display: 'inline', + }, + buttonProgress: { + color: green[500], + position: 'absolute', + top: '50%', + left: '50%', + marginTop: -12, + marginLeft: -12, + }, + }) +) + +interface Props extends ButtonProps { + label: string +} + +export default function LoadingButton(props: Props) { + const { children, label, ...rest } = props + const classes = useStyles() + const loading = props.disabled + return ( +
      + + {loading && } +
      + ) +} diff --git a/src/components/common/MuiTheme.tsx b/src/components/common/MuiTheme.tsx new file mode 100644 index 0000000..5286703 --- /dev/null +++ b/src/components/common/MuiTheme.tsx @@ -0,0 +1,21 @@ +import createMuiTheme from '@material-ui/core/styles/createMuiTheme' + +export const theme = { + palette: { + }, + overrides: { + }, +} + +const MuiTheme = createMuiTheme({ + overrides: { + MuiTableCell: { + root: { + padding: '0 16px', + }, + }, + }, + ...theme, +}) + +export default MuiTheme diff --git a/src/components/common/Navigation.jsx b/src/components/common/Navigation.jsx deleted file mode 100644 index d066efb..0000000 --- a/src/components/common/Navigation.jsx +++ /dev/null @@ -1,19 +0,0 @@ -import React from 'react' -import { NavLink } from 'react-router-dom' -import { GoHome, GoRepo, GoOrganization, GoPulse, GoPlug } from 'react-icons/lib/go' - -export default () => ( -
        -
      • 首页
      • -
      • 仓库
      • -
      • 团队
      • -
      • 接口
      • -
      • 状态
      • - {/*
      • 管理
      • */} - {/*
      • Users
      • -
      • Organization
      • -
      • Workspace
      • -
      • Analytics
      • -
      • Utils
      • */} -
      -) diff --git a/src/components/common/Navigation.tsx b/src/components/common/Navigation.tsx new file mode 100644 index 0000000..d5bf3df --- /dev/null +++ b/src/components/common/Navigation.tsx @@ -0,0 +1,13 @@ +import React from 'react' +import { NavLink } from 'react-router-dom' +import { GoHome, GoRepo, GoOrganization, GoPulse, GoPlug } from 'react-icons/go' + +export default () => ( +
        +
      • 首页
      • +
      • 仓库
      • +
      • 团队
      • +
      • 接口
      • +
      • 状态
      • +
      +) diff --git a/src/components/common/UserList.tsx b/src/components/common/UserList.tsx new file mode 100644 index 0000000..ae02c6e --- /dev/null +++ b/src/components/common/UserList.tsx @@ -0,0 +1,275 @@ +import React, { HTMLAttributes } from 'react' +import { Chip, Typography, MenuItem, TextField, NoSsr, Paper } from '@material-ui/core' +import { emphasize } from '@material-ui/core/styles/colorManipulator' +import CancelIcon from '@material-ui/icons/Cancel' +import { createStyles, makeStyles, useTheme, Theme } from '@material-ui/core/styles' +import { BaseTextFieldProps } from '@material-ui/core/TextField' +import Select from 'react-select' +import clsx from 'clsx' +import { NoticeProps, MenuProps } from 'react-select/src/components/Menu' +import { ControlProps } from 'react-select/src/components/Control' +import { PlaceholderProps } from 'react-select/src/components/Placeholder' +import { SingleValueProps } from 'react-select/src/components/SingleValue' +import { ValueContainerProps } from 'react-select/src/components/containers' +import { MultiValueProps } from 'react-select/src/components/MultiValue' +import AsyncSelect from 'react-select/async' +import { OptionProps } from 'react-select/src/components/Option' +import { INumItem, User } from '../../actions/types' + +const debounce = require('debounce-promise') + +interface OptionType { + label: string + value: string +} + +export type SelectedItem = Pick + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + root: { + flexGrow: 1, + marginBottom: theme.spacing(1), + }, + input: { + display: 'flex', + padding: 0, + height: 'auto', + }, + valueContainer: { + display: 'flex', + flexWrap: 'wrap', + flex: 1, + alignItems: 'center', + overflow: 'hidden', + }, + chip: { + margin: theme.spacing(0.5, 0.25), + }, + chipFocused: { + backgroundColor: emphasize( + theme.palette.type === 'light' ? theme.palette.grey[300] : theme.palette.grey[700], + 0.08 + ), + }, + noOptionsMessage: { + padding: theme.spacing(1, 2), + }, + singleValue: { + fontSize: 16, + }, + placeholder: { + position: 'absolute', + left: 2, + bottom: 6, + fontSize: 16, + }, + paper: { + position: 'absolute', + zIndex: 1, + marginTop: theme.spacing(1), + left: 0, + right: 0, + }, + divider: { + height: theme.spacing(2), + }, + }) +) + +function NoOptionsMessage(props: NoticeProps) { + return ( + + {props.children} + + ) +} + +type InputComponentProps = Pick & HTMLAttributes + +function inputComponent({ inputRef, ...props }: InputComponentProps) { + return
      +} + +function Control(props: ControlProps) { + return ( + + ) +} + +function Option(props: OptionProps) { + return ( + + {props.children} + + ) +} + +function Placeholder(props: PlaceholderProps) { + return ( + + {props.children} + + ) +} + +function SingleValue(props: SingleValueProps) { + return ( + + {props.children} + + ) +} + +function ValueContainer(props: ValueContainerProps) { + return
      {props.children}
      +} + +function MultiValue(props: MultiValueProps) { + return ( + } + /> + ) +} + +function Menu(props: MenuProps) { + return ( + + {props.children} + + ) +} + +const components = { + Control, + Menu, + MultiValue, + NoOptionsMessage, + Option, + Placeholder, + SingleValue, + ValueContainer, +} + +interface Props { + loadOptions?: (input: string) => Promise + options?: INumItem[] + isMulti?: boolean + selected?: INumItem[] + value?: INumItem[] + minWidth?: number + onChange: (selected: SelectedItem[]) => void +} + +function UserList(props: Props) { + const classes = useStyles() + const theme = useTheme() + const { loadOptions, isMulti, onChange, options, selected, value, minWidth } = props + const selectStyles = { + input: (base: any) => ({ + ...base, + color: theme.palette.text.primary, + '& input': { + font: 'inherit', + }, + }), + } + const commonProps: any = { + minWidth, + cacheOptions: true, + defaultValue: selected || [], + isMulti: isMulti || false, + noOptionsMessage: ({ inputValue }: { inputValue: string }) => { + return inputValue && inputValue.trim() + ? '搜不到数据' + : '请输入检索关键字' + }, + onChange: (vals: INumItem[] | INumItem) => { + if (Array.isArray(vals)) { + onChange(vals.map(x => ({ id: x.value, fullname: x.label }))) + } else { + onChange([{ id: vals.value, fullname: vals.label }]) + } + }, + placeholder: `请选择(${isMulti ? '多选' : '单选'})`, + } + + if (value) { + (commonProps as any).value = value + } + + if (loadOptions) { + return ( +
      + + + +
      + ) + } else if (options) { + return ( +
      + + this.setState({ name: e.target.value })} className='form-control' placeholder='Name' spellCheck='false' autoFocus='true' required /> -
      -
      -
      - -
      - this.setState({ url: e.target.value })} className='form-control' placeholder='URI' spellCheck='false' required /> -
      -
      -
      - -
      - -
      -
      -
      - -
      - -
      -
      -
      - -
      - this.setState({ description: e.target.value })} className='form-control' placeholder='Description' spellCheck='false' rows='5' /> -
      -
      - -
      -
      -
      -
      - - - ) - } - componentDidUpdate () { - this.context.rmodal.reposition() - } - handleSubmit = (e) => { - e.preventDefault() - let { onAddInterface, onUpdateInterface } = this.context - let { auth, repository, mod } = this.props - let onAddOrUpdateInterface = this.state.id ? onUpdateInterface : onAddInterface - let itf = Object.assign({}, this.state, { - creatorId: auth.id, - repositoryId: repository.id, - moduleId: mod.id, - lockerId: this.state.locker ? this.state.locker.id : null - }) - onAddOrUpdateInterface(itf, () => { - let { rmodal } = this.context - if (rmodal) rmodal.resolve() - }) - } -} - -const mapStateToProps = (state) => ({ - auth: state.auth -}) -const mapDispatchToProps = ({}) -export default connect( - mapStateToProps, - mapDispatchToProps -)(InterfaceForm) diff --git a/src/components/editor/InterfaceForm.tsx b/src/components/editor/InterfaceForm.tsx new file mode 100644 index 0000000..c25ce84 --- /dev/null +++ b/src/components/editor/InterfaceForm.tsx @@ -0,0 +1,173 @@ +import React, { Component } from 'react' +import { PropTypes, connect, Mock } from '../../family' +import { SmartTextarea } from '../utils' +import { RootState } from 'actions/types' +import { Button } from '@material-ui/core' +export const METHODS = ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS', 'PATCH', 'HEAD'] +export const STATUS_LIST = [200, 301, 403, 404, 500, 502, 503, 504] +// 模拟数据 +const mockInterface = + process.env.NODE_ENV === 'development' + ? () => + Mock.mock({ + name: '接口@CTITLE(4)', + url: '@URL', + 'method|1': METHODS, + description: '@CPARAGRAPH', + repositoryId: undefined, + moduleId: undefined, + }) + : () => ({ + name: '', + url: '', + method: 'GET', + description: '', + repositoryId: undefined, + moduleId: undefined, + }) + +type InterfaceFormProps = any +type InterfaceFormState = any +class InterfaceForm extends Component { + static contextTypes = { + rmodal: PropTypes.object.isRequired, + onAddInterface: PropTypes.func.isRequired, + onUpdateInterface: PropTypes.func.isRequired, + } + constructor(props: any) { + super(props) + const itf = this.props.itf + this.state = itf ? { ...itf } : mockInterface() + } + render() { + const { rmodal } = this.context + return ( +
      +
      + {this.props.title} +
      +
      +
      +
      + +
      + this.setState({ name: e.target.value })} + className="form-control" + placeholder="Name" + spellCheck={false} + autoFocus={true} + required={true} + /> +
      +
      +
      + +
      + this.setState({ url: e.target.value })} + className="form-control" + placeholder="URI" + spellCheck={false} + required={true} + /> +
      +
      +
      + +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      + this.setState({ description: e.target.value })} + className="form-control" + placeholder="Description" + spellCheck={false} + rows="5" + /> +
      +
      +
      +
      +
      +
      +
      + +
      + ) + } + componentDidUpdate() { + this.context.rmodal.reposition() + } + handleSubmit = (e: any) => { + e.preventDefault() + const { onAddInterface, onUpdateInterface } = this.context + const { auth, repository, mod } = this.props + const onAddOrUpdateInterface = this.state.id ? onUpdateInterface : onAddInterface + const itf = Object.assign({}, this.state, { + creatorId: auth.id, + repositoryId: repository.id, + moduleId: mod.id, + lockerId: this.state.locker ? this.state.locker.id : null, + }) + onAddOrUpdateInterface(itf, () => { + const { rmodal } = this.context + if (rmodal) { rmodal.resolve() } + }) + }; +} +const mapStateToProps = (state: RootState) => ({ + auth: state.auth, +}) +const mapDispatchToProps = {} +export default connect(mapStateToProps, mapDispatchToProps)(InterfaceForm) diff --git a/src/components/editor/InterfaceList.jsx b/src/components/editor/InterfaceList.jsx deleted file mode 100644 index c1145cd..0000000 --- a/src/components/editor/InterfaceList.jsx +++ /dev/null @@ -1,147 +0,0 @@ -import React, { Component } from 'react' -import { PropTypes, connect, Link, replace, StoreStateRouterLocationURI } from '../../family' -import { RModal, RSortable } from '../utils' -import InterfaceForm from './InterfaceForm' -import { GoPencil, GoTrashcan, GoRocket, GoLock } from 'react-icons/lib/go' -import { getCurrentInterface } from '../../selectors/interface'; -import './InterfaceList.css' - -class Interface extends Component { - static contextTypes = { - store: PropTypes.object.isRequired, - onDeleteInterface: PropTypes.func.isRequired - } - static propTypes = { - auth: PropTypes.object.isRequired, - repository: PropTypes.object.isRequired, - mod: PropTypes.object.isRequired, - itf: PropTypes.object.isRequired, - active: PropTypes.bool.isRequired, - curItf: PropTypes.object, - } - constructor(props) { - super(props) - this.state = { update: false } - } - render() { - let { store } = this.context - let { auth, repository, mod, itf } = this.props - let selectHref = StoreStateRouterLocationURI(store).setSearch('itf', itf.id).href() - let isOwned = repository.owner.id === auth.id - let isJoined = repository.members.find(itme => itme.id === auth.id) - return ( - - ) - } - handleDelete = (e, itf) => { - e.preventDefault() - let message = `接口被删除后不可恢复!\n确认继续删除『#${itf.id} ${itf.name}』吗?` - if (window.confirm(message)) { - let { onDeleteInterface } = this.context - onDeleteInterface(itf.id, () => { - let { store } = this.context - let uri = StoreStateRouterLocationURI(store) - let deleteHref = this.props.active ? uri.removeSearch('itf').href() : uri.href() - store.dispatch(replace(deleteHref)) - }) - } - } - handleUpdate = (e) => { - } -} - -class InterfaceList extends Component { - static contextTypes = { - store: PropTypes.object.isRequired, - onSortInterfaceList: PropTypes.func.isRequired - } - static propTypes = { - auth: PropTypes.object.isRequired, - repository: PropTypes.object.isRequired, - mod: PropTypes.object.isRequired, - itfs: PropTypes.array, - itf: PropTypes.object, - curItf: PropTypes.object, - } - constructor(props) { - super(props) - this.state = { create: false } - } - render() { - let { auth, repository, mod, itfs = [], itf, curItf } = this.props - if (!mod.id) return null - let isOwned = repository.owner.id === auth.id - let isJoined = repository.members.find(itme => itme.id === auth.id) - return ( -
      - -
        - {itfs.map(item => -
      • - -
      • - )} -
      -
      - {isOwned || isJoined - ?
      - {/* DONE 2.2 反复 setState() 还是很繁琐,需要提取一个类似 DialogController 的组件 */} - {/* DONE 2.2 如何重构为高阶组件 */} - this.setState({ create: true })}> - 新建接口 - - this.setState({ create: false })} onResolve={this.handleCreate}> - - -
      - : null} -
      - ) - } - handleCreate = (e) => { - } - handleSort = (e, sortable) => { - let { onSortInterfaceList } = this.context - onSortInterfaceList(sortable.toArray()) - } -} - -const mapStateToProps = (state) => ({ - auth: state.auth, - curItf: getCurrentInterface(state), -}) - -const mapDispatchToProps = ({}) - -export default connect( - mapStateToProps, - mapDispatchToProps -)(InterfaceList) diff --git a/src/components/editor/InterfaceList.tsx b/src/components/editor/InterfaceList.tsx new file mode 100644 index 0000000..6e5f23c --- /dev/null +++ b/src/components/editor/InterfaceList.tsx @@ -0,0 +1,140 @@ +import React, { Component } from 'react' +import { connect, Link, replace, StoreStateRouterLocationURI } from '../../family' +import { RModal, RSortable } from '../utils' +import InterfaceForm from './InterfaceForm' +import { GoPencil, GoTrashcan, GoRocket, GoLock } from 'react-icons/go' +import { getCurrentInterface } from '../../selectors/interface' +import PropTypes from 'prop-types' + +import './InterfaceList.css' +import { RootState } from 'actions/types' +type InterfaceProps = any +type InterfaceState = any +class InterfaceBase extends Component { + static contextTypes = { + store: PropTypes.object, + onDeleteInterface: PropTypes.func.isRequired, + } + constructor(props: any) { + super(props) + this.state = { update: false } + } + render() { + const { auth, repository, mod, itf, router } = this.props + const selectHref = StoreStateRouterLocationURI(router) + .setSearch('itf', itf.id) + .href() + const isOwned = repository.owner.id === auth.id + const isJoined = repository.members.find((i: any) => i.id === auth.id) + return ( +
      + + {itf.locker ? ( + + + + ) : null} + { + if ( + this.props.curItf && + this.props.curItf.locker && + !window.confirm('编辑模式下切换接口,会导致编辑中的资料丢失,是否确定切换接口?') + ) { + e.preventDefault() + } + }} + > + {itf.name} + + + {isOwned || isJoined ? ( +
      + {!itf.locker || itf.locker.id === auth.id ? ( + this.setState({ update: true })}> + + + ) : null} + this.setState({ update: false })} onResolve={this.handleUpdate}> + + + {!itf.locker ? ( + this.handleDelete(e, itf)}> + + + ) : null} +
      + ) : null} +
      + ) + } + handleDelete = (e: any, itf: any) => { + e.preventDefault() + const message = `接口被删除后不可恢复!\n确认继续删除『#${itf.id} ${itf.name}』吗?` + if (window.confirm(message)) { + const { onDeleteInterface } = this.context + onDeleteInterface(itf.id, () => { + const { store } = this.context + const uri = StoreStateRouterLocationURI(store) + const deleteHref = this.props.active ? uri.removeSearch('itf').href() : uri.href() + store.dispatch(replace(deleteHref)) + }) + } + }; + handleUpdate = () => { /** test */ } +} + +const Interface = connect((state: any) => ({ router: state.router }))(InterfaceBase) +type InterfaceListProps = any +type InterfaceListState = any +class InterfaceList extends Component { + constructor(props: any) { + super(props) + this.state = { create: false } + } + render() { + const { auth, repository, mod, itfs = [], itf, curItf } = this.props + if (!mod.id) { return null } + const isOwned = repository.owner.id === auth.id + const isJoined = repository.members.find((i: any) => i.id === auth.id) + return ( +
      + +
        + {itfs.map((item: any) => ( +
      • + +
      • + ))} +
      +
      + {isOwned || isJoined ? ( +
      + this.setState({ create: true })}> + + + {' '} + 新建接口 + + this.setState({ create: false })} onResolve={this.handleCreate}> + + +
      + ) : null} +
      + ) + } + handleCreate = () => { /** empty */ } + handleSort = (_: any, sortable: any) => { + const { onSortInterfaceList } = this.context + onSortInterfaceList(sortable.toArray()) + }; +} +const mapStateToProps = (state: RootState) => ({ + auth: state.auth, + curItf: getCurrentInterface(state), + router: state.router, +}) +const mapDispatchToProps = {} +export default connect(mapStateToProps, mapDispatchToProps)(InterfaceList) diff --git a/src/components/editor/InterfacePreviewer.jsx b/src/components/editor/InterfacePreviewer.tsx similarity index 50% rename from src/components/editor/InterfacePreviewer.jsx rename to src/components/editor/InterfacePreviewer.tsx index 2120432..e665885 100644 --- a/src/components/editor/InterfacePreviewer.jsx +++ b/src/components/editor/InterfacePreviewer.tsx @@ -2,96 +2,96 @@ import React, { Component } from 'react' import { PropTypes, Link, Mock, _ } from '../../family' import { Tree } from '../utils' import { serve } from '../../relatives/services/constant' -import { GoLink, GoSync, GoBeer, GoBug } from 'react-icons/lib/go' -import { RE_KEY } from 'mockjs/src/mock/constant' +import { GoLink, GoSync, GoBeaker, GoBug } from 'react-icons/go' -class Previewer extends Component { +/*eslint no-useless-escape: 0*/ +const RE_KEY = /(.+)\|(?:\+(\d+)|([\+\-]?\d+-?[\+\-]?\d*)?(?:\.(\d+-?\d*))?)/ + +class Previewer extends Component { static propTypes = { label: PropTypes.string.isRequired, scope: PropTypes.string.isRequired, properties: PropTypes.array.isRequired, - itf: PropTypes.object.isRequired + itf: PropTypes.object.isRequired, } render() { let scopedTemplate let scopedProperties - let scopedData + let scopedData: any let scopedKeys let extraKeys - let { label, scope, properties, itf } = this.props + const { label, scope, properties, itf } = this.props try { // DONE 2.2 支持引用请求参数 scopedProperties = { - request: properties.map(property => ({ ...property })).filter(property => property.scope === 'request'), - response: properties.map(property => ({ ...property })).filter(property => property.scope === 'response') - }; + request: properties.map((property: any) => ({ ...property })).filter((property: any) => property.scope === 'request'), + response: properties.map((property: any) => ({ ...property })).filter((property: any) => property.scope === 'response'), + } scopedTemplate = { request: Tree.treeToJson(Tree.arrayToTree(scopedProperties.request)), - response: Tree.treeToJson(Tree.arrayToTree(scopedProperties.response)) - }; + response: Tree.treeToJson(Tree.arrayToTree(scopedProperties.response)), + } scopedKeys = { request: Object.keys(scopedTemplate.request).map(item => item.replace(RE_KEY, '$1')), - response: Object.keys(scopedTemplate.response).map(item => item.replace(RE_KEY, '$1')) + response: Object.keys(scopedTemplate.response).map(item => item.replace(RE_KEY, '$1')), } extraKeys = _.difference(scopedKeys.request, scopedKeys.response) scopedData = { - request: Mock.mock(scopedTemplate.request) + request: Mock.mock(scopedTemplate.request), } scopedData.response = Mock.mock( Object.assign({}, _.pick(scopedData.request, extraKeys), scopedTemplate.response) ) scopedData.response = _.pick(scopedData.response, scopedKeys.response) - - let template = scopedTemplate[scope] + const template = (scopedTemplate as any)[scope] let data = scopedData[scope] if (data._root_) { data = data._root_ } // DONE 2.1 支持虚拟属性 __root__ √服务端 √前端 √迁移测试 - let keys = Object.keys(data) - if (keys.length === 1 && keys[0] === '__root__') data = data.__root__ + const keys = Object.keys(data) + if (keys.length === 1 && keys[0] === '__root__') { data = data.__root__ } - let { Assert } = Mock.valid - let valid = Mock.valid(template, data) - for (var i = 0; i < valid.length; i++) { - console.warn(Assert.message(valid[i])) + const { Assert } = Mock.valid + const valid = Mock.valid(template, data) + for (const i of valid) { + console.warn(Assert.message(i)) } return ( -
      -
      -
      - {label}模板 +
      +
      + -
      {
      -              JSON.stringify(template, (k, v) => {
      -                if (typeof v === 'function') return v.toString()
      -                if (v !== undefined && v !== null && v.exec) return v.toString()
      -                else return v
      +            
      {
      +              JSON.stringify(template, (_: any, v) => {
      +                if (typeof v === 'function') { return v.toString() }
      +                if (v !== undefined && v !== null && v.exec) { return v.toString() } else { return v }
                     }, 2)
                   }
      -
      -
      - {label}数据 +
      +
      + {label}数据 {scope === 'response' - ? + ? : null} - this.remock(e)}> this.removeAnimateClass(e)} /> + this.remock(e)}> this.removeAnimateClass(e)} />
      -
      {JSON.stringify(data, null, 2)}
      +
      {JSON.stringify(data, null, 2)}
      {scope === 'response' - ?
      + ?
      {!valid.length - ? 模板与数据匹配 √ - : 模板与数据不匹配 + ? 模板与数据匹配 √ + : 模板与数据不匹配 }
      : null @@ -103,15 +103,15 @@ class Previewer extends Component { } return
      发生错误...
      } - remock = (e) => { + remock = (e: any) => { e.preventDefault() - let target = e.currentTarget.firstChild + const target = e.currentTarget.firstChild target.classList.add('animated') target.classList.add('rotateIn') this.forceUpdate() } - removeAnimateClass = (e) => { - let target = e.currentTarget + removeAnimateClass = (e: any) => { + const target = e.currentTarget target.classList.remove('animated') target.classList.remove('rotateIn') } diff --git a/src/components/editor/InterfaceSummary.jsx b/src/components/editor/InterfaceSummary.jsx deleted file mode 100644 index 1fedcc1..0000000 --- a/src/components/editor/InterfaceSummary.jsx +++ /dev/null @@ -1,181 +0,0 @@ -import React, { Component } from 'react' -import { PropTypes, Link, replace, StoreStateRouterLocationURI } from '../../family' -import { DialogController } from '../utils' -import { serve } from '../../relatives/services/constant' -import InterfaceForm from './InterfaceForm' -import { getRelativeUrl } from '../../utils/URLUtils' -import './InterfaceSummary.css' - -export const BODY_OPTION = { - FORM_DATA: 'FORM_DATA', - FORM_URLENCODED: 'FORM_URLENCODED', - RAW: 'RAW', - BINARY: 'BINARY' -} - -export const REQUEST_PARAMS_TYPE = { - HEADERS: 'HEADERS', - QUERY_PARAMS: 'QUERY_PARAMS', - BODY_PARAMS: 'BODY_PARAMS' -} - -export function rptFromStr2Num(rpt) { - let pos = 2 - if (rpt === 'HEADERS') { - pos = 1 - } else if (rpt === 'BODY_PARAMS') { - pos = 3 - } - return pos -} - -class InterfaceSummary extends Component { - constructor(props) { - super(props) - this.state = { - bodyOption: BODY_OPTION.FORM_DATA, - requestParamsType: props.itf.method === 'POST' ? REQUEST_PARAMS_TYPE.BODY_PARAMS : REQUEST_PARAMS_TYPE.QUERY_PARAMS - } - this.changeMethod = this.changeMethod.bind(this) - this.changeHandler = this.changeHandler.bind(this) - this.switchBodyOptions = this.switchBodyOption.bind(this) - this.switchRequestParamsType = this.switchRequestParamsType.bind(this) - this.state.requestParamsType === REQUEST_PARAMS_TYPE.BODY_PARAMS && props.stateChangeHandler(this.state) - } - static contextTypes = { - store: PropTypes.object.isRequired, - onDeleteInterface: PropTypes.func.isRequired - } - static propTypes = { - repository: PropTypes.object.isRequired, - mod: PropTypes.object.isRequired, - itf: PropTypes.object.isRequired, - active: PropTypes.bool.isRequired, - editable: PropTypes.bool.isRequired, - stateChangeHandler: PropTypes.func.isRequired - } - componentDidUpdate() { - } - switchBodyOption(val) { - return () => { - this.setState({ - bodyOption: val - }, () => { - this.props.stateChangeHandler(this.state) - }) - } - } - switchRequestParamsType(val) { - return () => { - this.setState({ - requestParamsType: val - }, () => { - this.props.stateChangeHandler(this.state) - }) - } - } - - changeMethod(method) { - this.setState({ method }) - } - - changeStatus(status) { - this.setState({ status }) - } - - changeHandler(e) { - this.setState({ - [e.target.name]: e.target.value - }) - } - - render() { - const { repository = {}, mod = {}, itf = {}, editable } = this.props - const { requestParamsType } = this.state - if (!itf.id) return null - - return ( -
      -
      - - {itf.name} - - {/* TODO 2.2 √模板接口、√数据接口、JSONSchema 接口 */} - {/* TODO 2.2 权限控制,被别人锁定时不能编辑和删除 */} - {/* TODO 2.2 这里的接口编辑和右侧的编辑容易引起歧义,很难受 */} - - } onResolved={this.handleUpdate}> - e.preventDefault()} title='修改接口' className='edit'>编辑 - - this.handleDelete(e, itf)} className='delete'>删除 - -
      -
        -
      • - 地址: - {itf.url} -
      • -
      • 类型: - {itf.method} -
      • -
      • 状态码: - {itf.status} -
      • - {itf.description && -
      • 简介:{itf.description}
      • - } - {editable && - - } -
      - {editable && requestParamsType === REQUEST_PARAMS_TYPE.BODY_PARAMS - ?
      -
      - - -
      -
      - - -
      -
      - - -
      -
      - - -
      -
      : null - } -
      - ) - } - handleDelete = (e, itf) => { - e.preventDefault() - let message = '接口被删除后不可恢复!\n确认继续删除吗?' - if (window.confirm(message)) { - let { onDeleteInterface } = this.context - onDeleteInterface(itf.id, () => { - let { store } = this.context - let uri = StoreStateRouterLocationURI(store) - let deleteHref = this.props.active ? uri.removeSearch('itf').href() : uri.href() - store.dispatch(replace(deleteHref)) - }) - } - } - handleUpdate = () => { - } -} - -export default InterfaceSummary diff --git a/src/components/editor/InterfaceSummary.sass b/src/components/editor/InterfaceSummary.sass index f184144..b00ec39 100644 --- a/src/components/editor/InterfaceSummary.sass +++ b/src/components/editor/InterfaceSummary.sass @@ -4,4 +4,11 @@ .form-control max-width: 500px .body-options - margin: 8px \ No newline at end of file + margin: 8px + ul.summary + padding: 0 + li + list-style: none + margin: 0 + padding: 0 + margin-bottom: 5px \ No newline at end of file diff --git a/src/components/editor/InterfaceSummary.tsx b/src/components/editor/InterfaceSummary.tsx new file mode 100644 index 0000000..c0e0878 --- /dev/null +++ b/src/components/editor/InterfaceSummary.tsx @@ -0,0 +1,285 @@ +import React, { Component } from 'react' +import { connect } from 'react-redux' +import { Link, replace, StoreStateRouterLocationURI, PropTypes } from '../../family' +import { DialogController } from '../utils' +import { serve } from '../../relatives/services/constant' +import InterfaceForm from './InterfaceForm' +import { GoDiffAdded } from 'react-icons/go' +import SpinInline from '../utils/SpinInline' +import { getRelativeUrl } from '../../utils/URLUtils' +import './InterfaceSummary.css' +import { RootState } from 'actions/types' + +export const BODY_OPTION = { + FORM_DATA: 'FORM_DATA', + FORM_URLENCODED: 'FORM_URLENCODED', + RAW: 'RAW', + BINARY: 'BINARY', +} +export const REQUEST_PARAMS_TYPE = { + HEADERS: 'HEADERS', + QUERY_PARAMS: 'QUERY_PARAMS', + BODY_PARAMS: 'BODY_PARAMS', +} +export function rptFromStr2Num(rpt: any) { + let pos = 2 + if (rpt === 'HEADERS') { + pos = 1 + } else if (rpt === 'BODY_PARAMS') { + pos = 3 + } + return pos +} +type InterfaceSummaryProps = { + store: object, + [x: string]: any, +} +type InterfaceSummaryState = { + bodyOption?: any, + requestParamsType?: any, + method?: any, + status?: any, + [x: string]: any, +} +class InterfaceSummary extends Component { + static contextTypes = { + onAddForeignRoomCase: PropTypes.func.isRequired, + onDeleteInterface: PropTypes.func.isRequired, + } + constructor(props: any) { + super(props) + this.state = { + bodyOption: BODY_OPTION.FORM_DATA, + requestParamsType: props.itf.method === 'POST' ? REQUEST_PARAMS_TYPE.BODY_PARAMS : REQUEST_PARAMS_TYPE.QUERY_PARAMS, + } + this.changeMethod = this.changeMethod.bind(this) + this.changeHandler = this.changeHandler.bind(this) + this.switchBodyOption = this.switchBodyOption.bind(this) + this.switchRequestParamsType = this.switchRequestParamsType.bind(this) + this.state.requestParamsType === REQUEST_PARAMS_TYPE.BODY_PARAMS && props.stateChangeHandler(this.state) + } + switchBodyOption(val: any) { + return () => { + this.setState( + { + bodyOption: val, + }, + () => { + this.props.stateChangeHandler(this.state) + } + ) + } + } + switchRequestParamsType(val: any) { + return () => { + this.setState( + { + requestParamsType: val, + }, + () => { + this.props.stateChangeHandler(this.state) + } + ) + } + } + renderRoom() { + const { itf = {}, room = {}, repository = {} } = this.props + if (!room || !room[repository.id + '_' + itf.id] || !room[repository.id] || room[repository.id] === 'pending') { + return + } + if (room[repository.id + '_' + itf.id] === 'pending') { + return ( +
    • + Room用例: + +
    • + ) + } + const roomData = room[repository.id + '_' + itf.id] + const requestStatus = room['+' + repository.id + '_' + itf.id] + return ( +
    • + Room用例: + {roomData.coverage ? ( + + 存在 + + ) : ( + !requestStatus && 不存在 + )} + {!requestStatus && ( + + this.createCase(e, repository.id, itf.id, '普通')}> + 创建普通用例 + + this.createCase(e, repository.id, itf.id, '边界')}> + 创建边界用例 + + + )} + {requestStatus === 'pending' && ( + + + 创建{this.state.name}用例{' '} + + + + )} + {requestStatus === 'failed' && ( + + + 创建{this.state.name}用例失败 + this.createCase(e, repository.id, itf.id, this.state.name)}> + {' '} + 重试 + + + )} + {requestStatus === 'success' && ( + + 创建{this.state.name}用例成功 + + 查看 + + + )} +
    • + ) + } + changeMethod(method: any) { + this.setState({ method }) + } + changeStatus(status: any) { + this.setState({ status }) + } + changeHandler(e: any) { + this.setState({ + [e.target.name]: e.target.value, + }) + } + render() { + const { repository = {}, mod = {}, itf = {}, editable } = this.props + const { requestParamsType } = this.state + if (!itf.id) { return null } + return ( +
      +
      + {itf.name} + + + } onResolved={this.handleUpdate}> + e.preventDefault()} title="修改接口" className="edit"> + 编辑 + + + this.handleDelete(e, itf)} className="delete"> + 删除 + + +
      +
        +
      • + 地址: + + {itf.url} + +
      • +
      • + 类型: + {itf.method} +
      • +
      • + 状态码: + {itf.status} +
      • + {itf.description && ( +
      • + 简介: + {itf.description} +
      • + )} + {this.renderRoom()} + {editable && ( +
          +
        • + +
        • +
        • + +
        • +
        • + +
        • +
        + )} +
      + {editable && requestParamsType === REQUEST_PARAMS_TYPE.BODY_PARAMS ? ( +
      +
      + + +
      +
      + + +
      +
      + + +
      +
      + + +
      +
      + ) : null} +
      + ) + } + handleDelete = (e: any, itf: any) => { + e.preventDefault() + const message = '接口被删除后不可恢复!\n确认继续删除吗?' + if (window.confirm(message)) { + const { onDeleteInterface } = this.context + onDeleteInterface(itf.id, () => { + const { router, replace } = this.props + const uri = StoreStateRouterLocationURI(router) + const deleteHref = this.props.active ? uri.removeSearch('itf').href() : uri.href() + replace(deleteHref) + }) + } + } + handleUpdate = () => { /** empty */ } + createCase = (e: any, repositoryId: any, interfaceId: any, name: any) => { + e.preventDefault() + this.setState({ name }) + const { onAddForeignRoomCase } = this.context + onAddForeignRoomCase({ + id: repositoryId, + itf: interfaceId, + name, + }) + // itf.repositoryId + } +} +const mapStateToProps = (state: RootState) => ({ + room: state.foreign, +}) +const mapDispatchToProps = { + replace, +} +export default connect(mapStateToProps, mapDispatchToProps)(InterfaceSummary) diff --git a/src/components/editor/ModuleForm.jsx b/src/components/editor/ModuleForm.jsx deleted file mode 100644 index 14caeec..0000000 --- a/src/components/editor/ModuleForm.jsx +++ /dev/null @@ -1,95 +0,0 @@ -import React, { Component } from 'react' -import { PropTypes, connect, Link, Mock } from '../../family' -import { SmartTextarea } from '../utils' - -// 模拟数据 -const mockModule = process.env.NODE_ENV === 'development' - ? () => Mock.mock({ - name: '模块@CTITLE(4)', - description: '@CPARAGRAPH', - repositoryId: undefined - }) - : () => ({ - name: '', - description: '', - repositoryId: undefined - }) - -// 展示组件 -class ModuleForm extends Component { - static contextTypes = { - rmodal: PropTypes.object.isRequired, - onAddModule: PropTypes.func.isRequired, - onUpdateModule: PropTypes.func.isRequired - } - static propTypes = { - auth: PropTypes.object.isRequired, - repository: PropTypes.object.isRequired, - mod: PropTypes.object - } - constructor (props) { - super(props) - let { mod } = this.props - this.state = mod ? { ...mod } : mockModule() - } - render () { - const { rmodal } = this.context - return ( -
      -
      - {this.props.title} -
      -
      -
      -
      - -
      - this.setState({ name: e.target.value })} className='form-control' placeholder='Name' spellCheck='false' autoFocus='true' required /> -
      -
      -
      - -
      - this.setState({ description: e.target.value })} className='form-control' placeholder='Description' spellCheck='false' rows='5' /> -
      -
      -
      -
      -
      -
      -
      - -
      - ) - } - handleSubmit = (e) => { - e.preventDefault() - let { onAddModule, onUpdateModule } = this.context - let { auth, repository } = this.props - let onAddOrUpdateModule = this.state.id ? onUpdateModule : onAddModule - let mod = Object.assign({}, this.state, { - creatorId: auth.id, - repositoryId: repository.id - }) - let { rmodal } = this.context - rmodal.close() - onAddOrUpdateModule(mod, () => { - if (rmodal) rmodal.resolve() - }) - } -} - -// 容器组件 -const mapStateToProps = (state) => ({ - auth: state.auth -}) -const mapDispatchToProps = ({}) -export default connect( - mapStateToProps, - mapDispatchToProps -)(ModuleForm) diff --git a/src/components/editor/ModuleForm.tsx b/src/components/editor/ModuleForm.tsx new file mode 100644 index 0000000..65fe51f --- /dev/null +++ b/src/components/editor/ModuleForm.tsx @@ -0,0 +1,114 @@ +import React, { Component } from 'react' +import { PropTypes, connect, Mock } from '../../family' +import { SmartTextarea } from '../utils' +import { RootState } from 'actions/types' +import { Button } from '@material-ui/core' +// 模拟数据 +const mockModule = process.env.NODE_ENV === 'development' + ? () => Mock.mock({ + name: '模块@CTITLE(4)', + description: '@CPARAGRAPH', + repositoryId: undefined, + }) + : () => ({ + name: '', + description: '', + repositoryId: undefined, + }) + +// 展示组件 +class ModuleForm extends Component { + static contextTypes = { + rmodal: PropTypes.object.isRequired, + onAddModule: PropTypes.func.isRequired, + onUpdateModule: PropTypes.func.isRequired, + } + static propTypes = { + auth: PropTypes.object.isRequired, + repository: PropTypes.object.isRequired, + mod: PropTypes.object, + } + constructor(props: any) { + super(props) + const { mod } = this.props + this.state = mod ? { ...mod } : mockModule() + } + render() { + const { rmodal } = this.context + return ( +
      +
      + {this.props.title} +
      +
      +
      +
      + +
      + this.setState({ name: e.target.value })} + className="form-control" + placeholder="Name" + spellCheck={false} + autoFocus={true} + required={true} + /> +
      +
      +
      + +
      + this.setState({ description: e.target.value })} + className="form-control" + placeholder="Description" + spellCheck={false} + rows="5" + /> +
      +
      +
      +
      +
      +
      +
      + +
      + ) + } + handleSubmit = (e: any) => { + e.preventDefault() + const { onAddModule, onUpdateModule } = this.context + const { auth, repository } = this.props + const onAddOrUpdateModule = this.state.id ? onUpdateModule : onAddModule + const mod = Object.assign({}, this.state, { + creatorId: auth.id, + repositoryId: repository.id, + }) + const { rmodal } = this.context + rmodal.close() + onAddOrUpdateModule(mod, () => { + if (rmodal) { rmodal.resolve() } + }) + } +} + +const mapStateToProps = (state: RootState) => ({ + auth: state.auth, +}) +const mapDispatchToProps = ({}) +export default connect( + mapStateToProps, + mapDispatchToProps +)(ModuleForm) diff --git a/src/components/editor/ModuleList.jsx b/src/components/editor/ModuleList.jsx deleted file mode 100644 index 3fccdda..0000000 --- a/src/components/editor/ModuleList.jsx +++ /dev/null @@ -1,126 +0,0 @@ -import React, { Component } from 'react' -import { PropTypes, connect, Link, replace, URI, StoreStateRouterLocationURI } from '../../family' -import { RModal, RSortable } from '../utils' -import ModuleForm from './ModuleForm' -import { GoPencil, GoTrashcan, GoPackage } from 'react-icons/lib/go' - -class Module extends Component { - static propTypes = { - auth: PropTypes.object.isRequired, - repository: PropTypes.object.isRequired, - mod: PropTypes.object.isRequired - } - static contextTypes = { - store: PropTypes.object, - onDeleteModule: PropTypes.func - } - constructor (props) { - super(props) - this.state = { update: false } - } - render () { - let { store } = this.context - let { auth, repository, mod } = this.props - let uri = StoreStateRouterLocationURI(store).removeSearch('itf') - let selectHref = URI(uri).setSearch('mod', mod.id).href() - return ( -
      - {mod.name} -
      - {/* 编辑权限:拥有者或者成员 */} - {repository.owner.id === auth.id || repository.members.find(itme => itme.id === auth.id) - ? this.setState({ update: true })}> - : null - } - {repository.owner.id === auth.id || repository.members.find(itme => itme.id === auth.id) - ? this.handleDelete(e, mod)}> - : null - } -
      - this.setState({ update: false })} onResolve={this.handleUpdate}> - - -
      - ) - } - handleUpdate = (e) => { - let { store } = this.context - store.dispatch(replace(StoreStateRouterLocationURI(store).href())) - } - handleDelete = (e, mod) => { - e.preventDefault() - let message = `模块被删除后不可恢复,并且会删除相关的接口!\n确认继续删除『#${mod.id} ${mod.name}』吗?` - if (window.confirm(message)) { - this.context.onDeleteModule(this.props.mod.id, () => { - let { store } = this.context - let uri = StoreStateRouterLocationURI(store) - let deleteHref = this.props.active ? URI(uri).removeSearch('mod').href() : uri.href() - store.dispatch(replace(deleteHref)) - }, this.props.repository.id) - } - } -} - -class ModuleList extends Component { - static contextTypes = { - store: PropTypes.object.isRequired, - onSortModuleList: PropTypes.func.isRequired - } - static propTypes = { - auth: PropTypes.object.isRequired, - repository: PropTypes.object.isRequired, - mods: PropTypes.array, - mod: PropTypes.object - } - static childContextTypes = {} - getChildContext () {} - constructor (props) { - super(props) - this.state = { create: false } - } - render () { - let { auth, repository = {}, mods = [], mod = {} } = this.props - let isOwned = repository.owner.id === auth.id - let isJoined = repository.members.find(itme => itme.id === auth.id) - return ( - -
        - {mods.map((item, index) => -
      • - -
      • - )} - {/* 编辑权限:拥有者或者成员 */} - {isOwned || isJoined - ?
      • - this.setState({ create: true })}> - 新建模块 - - this.setState({ create: false })} onResolve={this.handleCreate}> - - -
      • - : null - } -
      -
      - ) - } - handleCreate = () => { - let { store } = this.context - store.dispatch(replace(StoreStateRouterLocationURI(store).href())) - } - handleSort = (e, sortable) => { - let { onSortModuleList } = this.context - onSortModuleList(sortable.toArray()) - } -} - -const mapStateToProps = (state) => ({ - auth: state.auth -}) -const mapDispatchToProps = ({}) -export default connect( - mapStateToProps, - mapDispatchToProps -)(ModuleList) diff --git a/src/components/editor/ModuleList.tsx b/src/components/editor/ModuleList.tsx new file mode 100644 index 0000000..0f57f4f --- /dev/null +++ b/src/components/editor/ModuleList.tsx @@ -0,0 +1,112 @@ +import React, { Component } from 'react' +import { connect, Link, replace, StoreStateRouterLocationURI } from '../../family' +import { RModal, RSortable } from '../utils' +import ModuleForm from './ModuleForm' +import { GoPencil, GoTrashcan, GoPackage } from 'react-icons/go' +import { RootState } from 'actions/types' + +class ModuleBase extends Component { + constructor(props: any) { + super(props) + this.state = { update: false } + } + render() { + const { auth, repository, mod, router } = this.props + const uri = StoreStateRouterLocationURI(router).removeSearch('itf') + const selectHref = uri.setSearch('mod', mod.id).href() + return ( +
      + {mod.name} +
      + {/* 编辑权限:拥有者或者成员 */} + {repository.owner.id === auth.id || repository.members.find((item: any) => item.id === auth.id) + ? this.setState({ update: true })}> + : null + } + {repository.owner.id === auth.id || repository.members.find((item: any) => item.id === auth.id) + ? this.handleDelete(e, mod)}> + : null + } +
      + this.setState({ update: false })} onResolve={this.handleUpdate}> + + +
      + ) + } + handleUpdate = () => { + this.props.replace(StoreStateRouterLocationURI(this.props.router).href()) + } + handleDelete = (e: any, mod: any) => { + const { router } = this.props + e.preventDefault() + const message = `模块被删除后不可恢复,并且会删除相关的接口!\n确认继续删除『#${mod.id} ${mod.name}』吗?` + if (window.confirm(message)) { + this.context.onDeleteModule(this.props.mod.id, () => { + const { store } = this.context + const uri = StoreStateRouterLocationURI(router) + const deleteHref = this.props.active ? uri.removeSearch('mod').href() : uri.href() + store.dispatch(replace(deleteHref)) + }, this.props.repository.id) + } + } +} + +const Module = connect((state: any) => ({ + router: state.router, +}))(ModuleBase) + +class ModuleList extends Component { + constructor(props: any) { + super(props) + this.state = { create: false } + } + render() { + const { auth, repository = {}, mods = [], mod = {} } = this.props + const isOwned = repository.owner.id === auth.id + const isJoined = repository.members.find((item: any) => item.id === auth.id) + return ( + +
        + {mods.map((item: any) => +
      • + +
      • + )} + {/* 编辑权限:拥有者或者成员 */} + {isOwned || isJoined + ?
      • + this.setState({ create: true })}> + 新建模块 + + this.setState({ create: false })} onResolve={this.handleCreate}> + + +
      • + : null + } +
      +
      + ) + } + handleCreate = () => { + const { router, replace } = this.props + replace(StoreStateRouterLocationURI(router).href()) + } + handleSort = (_: any, sortable: any) => { + const { onSortModuleList } = this.context + onSortModuleList(sortable.toArray()) + } +} + +const mapStateToProps = (state: RootState) => ({ + auth: state.auth, + router: state.router, +}) +const mapDispatchToProps = ({ + replace, +}) +export default connect( + mapStateToProps, + mapDispatchToProps +)(ModuleList) diff --git a/src/components/editor/MoveInterfaceForm.jsx b/src/components/editor/MoveInterfaceForm.jsx deleted file mode 100644 index c31a8eb..0000000 --- a/src/components/editor/MoveInterfaceForm.jsx +++ /dev/null @@ -1,106 +0,0 @@ -import React, { Component } from 'react' -import { PropTypes, connect, Link } from '../../family' -import { moveInterface } from '../../actions/interface' - -const OP_MOVE = 1 -const OP_COPY = 2 - -class MoveInterfaceForm extends Component { - static contextTypes = { - rmodal: PropTypes.instanceOf(Component), - onAddInterface: PropTypes.func.isRequired, - onUpdateInterface: PropTypes.func.isRequired - } - static propTypes = { - title: PropTypes.string.isRequired, - repository: PropTypes.object.isRequired, - itfId: PropTypes.number.isRequired, - moveInterface: PropTypes.func.isRequired - } - constructor (props) { - super(props) - const { repository } = props - let modId = 0 - if (repository.modules.length > 0) { - modId = repository.modules[0].id - } - this.state = { - op: OP_MOVE, // 1 move, 2 copy - modId - } - } - render () { - const { rmodal } = this.context - const { repository } = this.props - const { modId, op } = this.state - return ( -
      -
      - {this.props.title} -
      -
      -
      -
      - -
      - -
      -
      -
      - -
      -
      -
      - { this.setState({ op: OP_MOVE }) }} /> - -
      -
      - { this.setState({ op: OP_COPY }) }} /> - -
      -
      -
      -
      -
      -
      -
      -
      -
      - -
      - ) - } - componentDidUpdate () { - this.context.rmodal.reposition() - } - handleSubmit = (e) => { - e.preventDefault() - const params = { - modId: this.state.modId, - op: this.state.op, - itfId: this.props.itfId, - repoId: this.props.repository.id - } - this.props.moveInterface(params, () => { - let { rmodal } = this.context - if (rmodal) rmodal.resolve() - }) - } -} - -const mapStateToProps = (state) => ({ -}) -const mapDispatchToProps = ({ - moveInterface -}) -export default connect( - mapStateToProps, - mapDispatchToProps -)(MoveInterfaceForm) diff --git a/src/components/editor/MoveInterfaceForm.tsx b/src/components/editor/MoveInterfaceForm.tsx new file mode 100644 index 0000000..e78e640 --- /dev/null +++ b/src/components/editor/MoveInterfaceForm.tsx @@ -0,0 +1,115 @@ +import React, { useState, useContext, useEffect } from 'react' +import { moveInterface } from '../../actions/interface' +import { Button, Select, MenuItem, FormControl, RadioGroup, FormControlLabel, Radio, Theme, makeStyles } from '@material-ui/core' +import { useDispatch } from 'react-redux' +import { ModalContext } from 'components/utils/RModal' + +export const OP_MOVE = 1 +export const OP_COPY = 2 + +const useStyles = makeStyles(({ spacing }: Theme) => ({ + root: { + }, + appBar: { + position: 'relative', + }, + title: { + marginLeft: spacing(2), + flex: 1, + }, + preview: { + marginTop: spacing(1), + }, + form: { + minWidth: 500, + minHeight: 300, + }, + formTitle: { + color: 'rgba(0, 0, 0, 0.54)', + fontSize: 9, + }, + formItem: { + marginBottom: spacing(1), + }, + ctl: { + marginTop: spacing(3), + }, +})) + +interface Props { + title: string + repository: any + itfId: number +} + +// constructor(props: any) { +// super(props) +// const { repository } = props + +// this.state = { modId, op: OP_MOVE } +// } +export default function MoveInterfaceForm(props: Props) { + const { repository, title, itfId } = props + const classes = useStyles() + let modIdInit = 0 + if (repository.modules.length > 0) { + modIdInit = repository.modules[0].id + } + const [modId, setModId] = useState(modIdInit) + const [op, setOp] = useState(OP_MOVE) + + const dispatch = useDispatch() + const rmodal = useContext(ModalContext) + + useEffect(() => { + rmodal && rmodal.reposition() + }, [rmodal]) + + const handleSubmit = (e: any) => { + e.preventDefault() + const params = { + modId, + op, + itfId, + } + dispatch(moveInterface(params, () => { + rmodal && rmodal.resolve() + })) + } + return ( +
      +
      + {title} +
      +
      +
      +
      选择目标模块:
      + + + +
      选择目标模块:
      + {setOp(+(e.target as any).value) }} + row={true} + > + } label="复制" /> + } label="移动" /> + +
      +
      +
      +
      +
      + +
      + ) +} diff --git a/src/components/editor/PropertyForm.jsx b/src/components/editor/PropertyForm.jsx deleted file mode 100644 index 8dfbf4d..0000000 --- a/src/components/editor/PropertyForm.jsx +++ /dev/null @@ -1,137 +0,0 @@ -import React, { Component } from 'react' -import PropTypes from 'prop-types' -import { connect } from 'react-redux' -import { Link } from 'react-router-dom' -import Mock from 'mockjs' -import SmartTextarea from '../utils/SmartTextarea' - -export const TYPES = ['String', 'Number', 'Boolean', 'Object', 'Array', 'Function', 'RegExp'] - -// 模拟数据 -const mockProperty = process.env.NODE_ENV === 'development' - ? () => Mock.mock({ - 'scope|1': ['request', 'response'], - name: '@WORD(6)', - 'type|1': TYPES, - 'value|1': ['@INT', '@FLOAT', '@TITLE', '@NAME'], - description: '@CSENTENCE', - parentId: -1, - interfaceId: '@NATURAL', - moduleId: '@NATURAL', - repositoryId: '@NATURAL' - }) - : () => ({ - scope: 'response', - name: '', - type: 'String', - value: '', - description: '', - parentId: -1, - interfaceId: undefined, - moduleId: undefined, - repositoryId: undefined - }) - -class PropertyForm extends Component { - static propTypes = { - scope: PropTypes.string.isRequired, - parent: PropTypes.object, - repository: PropTypes.object.isRequired, - mod: PropTypes.object.isRequired, - itf: PropTypes.object.isRequired - } - static contextTypes = { - rmodal: PropTypes.instanceOf(Component), - handleAddMemoryProperty: PropTypes.func.isRequired - } - constructor (props) { - super(props) - this.state = mockProperty() - } - render () { - const { rmodal } = this.context - return ( -
      -
      - {this.props.title} -
      -
      -
      -
      - -
      - this.setState({ name: e.target.value })} className='form-control' placeholder='Name' spellCheck='false' autoFocus='true' required /> -
      -
      -
      - -
      - -
      -
      -
      - -
      - this.setState({ rule: e.target.value })} className='form-control' placeholder='Rule' spellCheck='false' /> -
      -
      -
      - -
      - this.setState({ value: e.target.value })} className='form-control' placeholder='Value' spellCheck='false' /> -
      -
      -
      - -
      - this.setState({ description: e.target.value })} className='form-control' placeholder='Description' spellCheck='false' rows='5' /> -
      -
      -
      -
      -
      -
      -
      - -
      - ) - } - componentDidUpdate () { - this.context.rmodal.reposition() - } - handleSubmit = (e) => { - e.preventDefault() - let { auth, repository, mod, itf, scope, parent = { id: -1 } } = this.props - let { handleAddMemoryProperty } = this.context - let property = Object.assign({}, this.state, { - creatorId: auth.id, - repositoryId: repository.id, - moduleId: mod.id, - interfaceId: itf.id, - scope, - parentId: parent.id - }) - handleAddMemoryProperty(property, () => { - let { rmodal } = this.context - if (rmodal) rmodal.resolve() - }) - } -} - -const mapStateToProps = (state) => ({ - auth: state.auth -}) -const mapDispatchToProps = ({}) -export default connect( - mapStateToProps, - mapDispatchToProps -)(PropertyForm) diff --git a/src/components/editor/PropertyForm.tsx b/src/components/editor/PropertyForm.tsx new file mode 100644 index 0000000..e0b6f70 --- /dev/null +++ b/src/components/editor/PropertyForm.tsx @@ -0,0 +1,178 @@ +import React, { Component } from 'react' +import PropTypes from 'prop-types' +import { connect } from 'react-redux' +import Mock from 'mockjs' +import SmartTextarea from '../utils/SmartTextarea' +import { Button } from '@material-ui/core' + +export const TYPES = ['String', 'Number', 'Boolean', 'Object', 'Array', 'Function', 'RegExp'] + +// 模拟数据 +const mockProperty = process.env.NODE_ENV === 'development' + ? () => Mock.mock({ + 'scope|1': ['request', 'response'], + name: '@WORD(6)', + 'type|1': TYPES, + 'value|1': ['@INT', '@FLOAT', '@TITLE', '@NAME'], + description: '@CSENTENCE', + parentId: -1, + interfaceId: '@NATURAL', + moduleId: '@NATURAL', + repositoryId: '@NATURAL', + }) + : () => ({ + scope: 'response', + name: '', + type: 'String', + value: '', + description: '', + parentId: -1, + interfaceId: undefined, + moduleId: undefined, + repositoryId: undefined, + }) + +class PropertyForm extends Component { + static propTypes = { + scope: PropTypes.string.isRequired, + parent: PropTypes.object, + repository: PropTypes.object.isRequired, + mod: PropTypes.object.isRequired, + itf: PropTypes.object.isRequired, + } + static contextTypes = { + rmodal: PropTypes.instanceOf(Component), + handleAddMemoryProperty: PropTypes.func.isRequired, + } + constructor(props: any) { + super(props) + this.state = mockProperty() + } + render() { + const { rmodal } = this.context + return ( +
      +
      + {this.props.title} +
      +
      +
      +
      + +
      + this.setState({ name: e.target.value })} + className="form-control" + placeholder="Name" + spellCheck={false} + autoFocus={true} + required={true} + /> +
      +
      +
      + +
      + +
      +
      +
      + +
      + this.setState({ rule: e.target.value })} + className="form-control" + placeholder="Rule" + spellCheck={false} + /> +
      +
      +
      + +
      + this.setState({ value: e.target.value })} + className="form-control" + placeholder="Value" + spellCheck={false} + /> +
      +
      +
      + +
      + this.setState({ description: e.target.value })} + className="form-control" + placeholder="Description" + spellCheck={false} + rows="5" + /> +
      +
      +
      +
      +
      +
      +
      + +
      + ) + } + componentDidUpdate() { + this.context.rmodal.reposition() + } + handleSubmit = (e: any) => { + e.preventDefault() + const { auth, repository, mod, itf, scope, parent = { id: -1 } } = this.props + const { handleAddMemoryProperty } = this.context + const property = Object.assign({}, this.state, { + creatorId: auth.id, + repositoryId: repository.id, + moduleId: mod.id, + interfaceId: itf.id, + scope, + parentId: parent.id, + }) + handleAddMemoryProperty(property, () => { + const { rmodal } = this.context + if (rmodal) { rmodal.resolve() } + }) + } +} + +const mapStateToProps = (state: any) => ({ + auth: state.auth, +}) +const mapDispatchToProps = ({}) +export default connect( + mapStateToProps, + mapDispatchToProps +)(PropertyForm) diff --git a/src/components/editor/PropertyList.jsx b/src/components/editor/PropertyList.jsx deleted file mode 100644 index caacde7..0000000 --- a/src/components/editor/PropertyList.jsx +++ /dev/null @@ -1,267 +0,0 @@ -import React, { Component } from 'react' -import { PropTypes, connect, Link } from '../../family' -import { Tree, SmartTextarea, RModal, RSortable } from '../utils' -import PropertyForm from './PropertyForm' -import Importer from './Importer' -import Previewer from './InterfacePreviewer' -import { GoMention, GoFileCode, GoEye, GoPlus, GoTrashcan, GoQuestion } from 'react-icons/lib/go' -import { rptFromStr2Num } from './InterfaceSummary' -import './PropertyList.css' - -export const RequestPropertyListPreviewer = (props) => ( - -) - -export const ResponsePropertyListPreviewer = (props) => ( - -) - -// DONE 2.2 请求属性有什么用?有必要吗?有,用于订制响应数据。 -// DONE 2.2 如何过滤模拟 URL 中额外的请求属性?解析 URL 中的参数到请求属性列表吗?可以在响应数据中引用 配置的请求参数 和 URL 中的额外参数。 -// DONE 2.2 支持对属性排序 -// DONE 2.2 支持对模块排序 -// DONE 2.2 支持对接口排序 -// TODO 2.3 检测重复属性 - -class SortableTreeTableHeader extends Component { - render() { - let { editable } = this.props - return ( -
      -
      - {/* DONE 2.1 每列增加帮助 Tip */} - {editable &&
      } -
      名称
      -
      必选
      -
      类型
      - {/* TODO 2.3 规则编辑器 */} -
      - 生成规则 - -
      -
      初始值
      {/* 对象和数组也允许设置初始值 */} -
      简介
      -
      -
      - ) - } -} - -const PropertyLabel = (props) => { - const { pos } = props - if (pos === 1) { - return - } else if (pos === 3) { - return - } else { - return - } -} - -class SortableTreeTableRow extends Component { - render() { - let { property, editable } = this.props - let { handleClickCreateChildPropertyButton, handleDeleteMemoryProperty, handleChangePropertyField, handleSortProperties } = this.props - return ( - -
      - {property.children.sort((a, b) => a.priority - b.priority).map(item => -
      -
      - {editable && -
      - {(item.type === 'Object' || item.type === 'Array') - ? { e.preventDefault(); handleClickCreateChildPropertyButton(item) }}> - : null} - handleDeleteMemoryProperty(e, item)}> -
      - } -
      - {!editable - ? {item.name}{item.scope === 'request' && item.depth === 0 ? : null} - : handleChangePropertyField(item.id, 'name', e.target.value)} className='form-control editable' spellCheck='false' placeholder='' /> - } -
      -
      - {!editable - ? {item.required ? '✔️' : ''} - : handleChangePropertyField(item.id, 'required', e.target.checked)} /> - } -
      - -
      - {!editable - ? {item.type} - : - } -
      -
      - {!editable - ? {item.rule} - : handleChangePropertyField(item.id, 'rule', e.target.value)} className='form-control editable' spellCheck='false' placeholder='' /> - } -
      -
      - {!editable - ? {item.value} - : handleChangePropertyField(item.id, 'value', e.target.value)} rows='1' className='form-control editable' spellCheck='false' placeholder='' /> - } -
      -
      - {!editable - ? {item.description} - : handleChangePropertyField(item.id, 'description', e.target.value)} rows='1' className='form-control editable' spellCheck='false' placeholder='' /> - } -
      -
      - {item.children && item.children.length ? : null} -
      - )} -
      -
      - ) - } -} - -class SortableTreeTable extends Component { - render() { - let { root, editable } = this.props - return ( -
      - - -
      - ) - } -} - -class PropertyList extends Component { - static contextTypes = { - store: PropTypes.object.isRequired, - handleDeleteMemoryProperty: PropTypes.func.isRequired, - handleChangeProperty: PropTypes.func.isRequired, - onDeleteProperty: PropTypes.func.isRequired, - onSortPropertyList: PropTypes.func.isRequired - } - static propTypes = { - title: PropTypes.string.isRequired, - label: PropTypes.string.isRequired, - scope: PropTypes.string.isRequired, - properties: PropTypes.array, - repository: PropTypes.object.isRequired, - mod: PropTypes.object.isRequired, - itf: PropTypes.object.isRequired, - editable: PropTypes.bool.isRequired, - - /** optional */ - bodyOption: PropTypes.string, - requestParamsType: PropTypes.string - } - constructor(props) { - super(props) - this.state = { - createProperty: false, - createChildProperty: false, - previewer: props.scope === 'response', - importer: false - } - } - render() { - let { title, label, scope, properties = [], repository = {}, mod = {}, itf = {} } = this.props - if (!itf.id) return null - let { editable, requestParamsType } = this.props // itf.locker && (itf.locker.id === auth.id) - const pos = rptFromStr2Num(requestParamsType) - let scopedProperties = properties.map(property => ({ ...property })).filter(property => property.scope === scope) - if (scope === 'request' && editable) { - scopedProperties = scopedProperties.filter(s => s.pos === pos) - } - - return ( -
      -
      - {title || `${label}属性`} - {/* DONE 2.2 新建按钮暂时合并到按扭组中,单独放出来有点混乱 */} -
      -
      - {editable && ( - - )} - {editable && ( - - )} - -
      -
      -
      -
      - -
      -
      - {this.state.previewer && } -
      - this.setState({ createProperty: false })} onResolve={this.handleCreatePropertySucceeded}> - - - this.setState({ createChildProperty: false })} onResolve={this.handleCreatePropertySucceeded}> - - - this.setState({ importer: false })} onResolve={this.handleCreatePropertySucceeded}> - - -
      - ) - } - handleClickCreatePropertyButton = () => { - this.setState({ createProperty: true }) - } - handleClickCreateChildPropertyButton = (item) => { - this.setState({ createChildProperty: item }) - } - handleClickImporterButton = () => { - this.setState({ importer: true }) - } - handleClickPreviewerButton = () => { - this.setState({ previewer: !this.state.previewer }) - } - handleChangePropertyField = (id, key, value) => { - let { handleChangeProperty } = this.context - let { properties } = this.props - let property = properties.find(property => property.id === id) - handleChangeProperty({ ...property, [key]: value }) - } - handleCreatePropertySucceeded = () => { - } - handleDeleteMemoryProperty = (e, property) => { - e.preventDefault() - let { handleDeleteMemoryProperty } = this.context - handleDeleteMemoryProperty(property) - } - handleSortProperties = (e, sortable) => { - let { properties } = this.props - let ids = sortable.toArray() - ids.forEach((id, index) => { - let property = properties.find(item => item.id === id || item.id === +id) - property.priority = index + 1 - }) - } -} -const mapStateToProps = (state) => ({}) -const mapDispatchToProps = ({}) -export default connect( - mapStateToProps, - mapDispatchToProps -)(PropertyList) diff --git a/src/components/editor/PropertyList.tsx b/src/components/editor/PropertyList.tsx new file mode 100644 index 0000000..67d90d1 --- /dev/null +++ b/src/components/editor/PropertyList.tsx @@ -0,0 +1,311 @@ +import React, { Component } from 'react' +import { PropTypes, Link } from '../../family' +import { Tree, SmartTextarea, RModal, RSortable } from '../utils' +import PropertyForm from './PropertyForm' +import Importer from './Importer' +import Previewer from './InterfacePreviewer' +import { GoPlus, GoTrashcan, GoQuestion } from 'react-icons/go' +import { rptFromStr2Num } from './InterfaceSummary' +import './PropertyList.css' +import { ButtonGroup, Button } from '@material-ui/core' + +export const RequestPropertyListPreviewer = (props: any) => ( + +) + +export const ResponsePropertyListPreviewer = (props: any) => ( + +) + +// DONE 2.2 请求属性有什么用?有必要吗?有,用于订制响应数据。 +// DONE 2.2 如何过滤模拟 URL 中额外的请求属性?解析 URL 中的参数到请求属性列表吗?可以在响应数据中引用 配置的请求参数 和 URL 中的额外参数。 +// DONE 2.2 支持对属性排序 +// DONE 2.2 支持对模块排序 +// DONE 2.2 支持对接口排序 +// TODO 2.3 检测重复属性 + +class SortableTreeTableHeader extends Component { + render() { + const { editable } = this.props + return ( +
      +
      + {/* DONE 2.1 每列增加帮助 Tip */} + {editable &&
      } +
      名称
      +
      必选
      +
      类型
      + {/* TODO 2.3 规则编辑器 */} +
      + 生成规则 + + + +
      +
      初始值
      {/* 对象和数组也允许设置初始值 */} +
      简介
      +
      +
      + ) + } +} + +const PropertyLabel = (props: any) => { + const { pos } = props + if (pos === 1) { + return + } else if (pos === 3) { + return + } else { + return + } +} + +class SortableTreeTableRow extends Component { + render() { + const { property, editable } = this.props + const { handleClickCreateChildPropertyButton, handleDeleteMemoryProperty, handleChangePropertyField, handleSortProperties } = this.props + return ( + +
      + {property.children.sort((a: any, b: any) => a.priority - b.priority).map((item: any) => +
      +
      + {editable && +
      + {(item.type === 'Object' || item.type === 'Array') + ? { e.preventDefault(); handleClickCreateChildPropertyButton(item) }} + > + + + : null} + handleDeleteMemoryProperty(e, item)}> +
      + } +
      + {!editable + ? + {item.name} + {item.scope === 'request' && item.depth === 0 ? : null} + + : handleChangePropertyField(item.id, 'name', e.target.value)} + className="form-control editable" + spellCheck={false} + placeholder="" + /> + } +
      +
      + {!editable + ? {item.required ? '✔️' : ''} + : handleChangePropertyField(item.id, 'required', e.target.checked)} + /> + } +
      + +
      + {!editable + ? {item.type} + : + } +
      +
      + {!editable + ? {item.rule} + : handleChangePropertyField(item.id, 'rule', e.target.value)} + className="form-control editable" + spellCheck={false} + placeholder="" + /> + } +
      +
      + {!editable + ? {item.value} + : handleChangePropertyField(item.id, 'value', e.target.value)} + rows="1" + className="form-control editable" + spellCheck={false} + placeholder="" + /> + } +
      +
      + {!editable + ? {item.description} + : handleChangePropertyField(item.id, 'description', e.target.value)} + rows="1" + className="form-control editable" + spellCheck={false} + placeholder="" + /> + } +
      +
      + {item.children && item.children.length ? : null} +
      + )} +
      +
      + ) + } +} + +class SortableTreeTable extends Component { + render() { + const { root, editable } = this.props + return ( +
      + + +
      + ) + } +} + +class PropertyList extends Component { + static propTypes = { + title: PropTypes.string.isRequired, + label: PropTypes.string.isRequired, + scope: PropTypes.string.isRequired, + properties: PropTypes.array, + repository: PropTypes.object.isRequired, + mod: PropTypes.object.isRequired, + itf: PropTypes.object.isRequired, + editable: PropTypes.bool.isRequired, + + /** optional */ + bodyOption: PropTypes.string, + requestParamsType: PropTypes.string, + } + constructor(props: any) { + super(props) + this.state = { + createProperty: false, + createChildProperty: false, + previewer: props.scope === 'response', + importer: false, + } + } + render() { + const { title, label, scope, properties = [], repository = {}, mod = {}, itf = {} } = this.props + if (!itf.id) { return null } + const { editable, requestParamsType } = this.props // itf.locker && (itf.locker.id === auth.id) + const pos = rptFromStr2Num(requestParamsType) + let scopedProperties = properties.map((property: any) => ({ ...property })).filter((property: any) => property.scope === scope) + if (scope === 'request' && editable) { + scopedProperties = scopedProperties.filter((s: any) => s.pos === pos) + } + + return ( +
      +
      + {title || `${label}属性`} +
      + + {editable && [ + , + , + ]} + + +
      +
      +
      + +
      +
      + {this.state.previewer && } +
      + this.setState({ createProperty: false })} + onResolve={this.handleCreatePropertySucceeded} + > + + + this.setState({ createChildProperty: false })} + onResolve={this.handleCreatePropertySucceeded} + > + + + this.setState({ importer: false })} onResolve={this.handleCreatePropertySucceeded}> + + +
      + ) + } + handleClickCreatePropertyButton = () => { + this.setState({ createProperty: true }) + } + handleClickCreateChildPropertyButton = (item: any) => { + this.setState({ createChildProperty: item }) + } + handleClickImporterButton = () => { + this.setState({ importer: true }) + } + handleClickPreviewerButton = () => { + this.setState({ previewer: !this.state.previewer }) + } + handleChangePropertyField = (id: any, key: any, value: any) => { + const { handleChangeProperty } = this.context + const { properties } = this.props + const property = properties.find((property: any) => property.id === id) + handleChangeProperty({ ...property, [key]: value }) + } + handleCreatePropertySucceeded = () => { + /** empty */ + } + handleDeleteMemoryProperty = (e: any, property: any) => { + e.preventDefault() + const { handleDeleteMemoryProperty } = this.context + handleDeleteMemoryProperty(property) + } + handleSortProperties = (_: any, sortable: any) => { + const { properties } = this.props + const ids = sortable.toArray() + ids.forEach((id: any, index: any) => { + const property = properties.find((item: any) => item.id === id || item.id === +id) + property.priority = index + 1 + }) + } +} + +export default PropertyList diff --git a/src/components/editor/RepositoryEditor.sass b/src/components/editor/RepositoryEditor.sass index 67c823f..65c4aef 100644 --- a/src/components/editor/RepositoryEditor.sass +++ b/src/components/editor/RepositoryEditor.sass @@ -82,7 +82,7 @@ margin-right: 1rem; .ModuleList - margin: 0 0 1.5rem 0; + margin: 0; padding: 0 2rem; list-style: none; border-bottom: 1px solid #e1e4e8; @@ -130,7 +130,8 @@ .InterfaceWrapper display: flex; flex-direction: row; - padding: 0 2rem; + background-color: #FFFFFF; + padding: 2rem; .InterfaceList flex-basis: 16rem; flex-shrink: 0; diff --git a/src/components/editor/RepositoryEditor.jsx b/src/components/editor/RepositoryEditor.tsx similarity index 61% rename from src/components/editor/RepositoryEditor.jsx rename to src/components/editor/RepositoryEditor.tsx index 5c84386..6638fbe 100644 --- a/src/components/editor/RepositoryEditor.jsx +++ b/src/components/editor/RepositoryEditor.tsx @@ -12,10 +12,11 @@ import { addRepository, updateRepository, clearRepository, fetchRepository } fro import { addModule, updateModule, deleteModule, sortModuleList } from '../../actions/module' import { addInterface, updateInterface, deleteInterface, lockInterface, unlockInterface, sortInterfaceList } from '../../actions/interface' import { addProperty, updateProperty, deleteProperty, updateProperties, sortPropertyList } from '../../actions/property' -import { GoRepo, GoPencil, GoPlug, GoDatabase, GoJersey, GoLinkExternal } from 'react-icons/lib/go' +import { GoRepo, GoPencil, GoPlug, GoDatabase, GoJersey, GoLinkExternal } from 'react-icons/go' import './RepositoryEditor.css' import ExportPostmanForm from '../repository/ExportPostmanForm' +import { RootState } from 'actions/types' // DONE 2.1 import Spin from '../utils/Spin' // TODO 2.2 缺少测试器 @@ -23,15 +24,12 @@ import ExportPostmanForm from '../repository/ExportPostmanForm' // TODO 2.1 大数据测试,含有大量模块、接口、属性的仓库 // 展示组件 -class RepositoryEditor extends Component { - static contextTypes = { - store: PropTypes.object.isRequired - } +class RepositoryEditor extends Component { static propTypes = { auth: PropTypes.object.isRequired, repository: PropTypes.object.isRequired, location: PropTypes.object.isRequired, - onClearRepository: PropTypes.func.isRequired + onClearRepository: PropTypes.func.isRequired, } static childContextTypes = { onAddRepository: PropTypes.func.isRequired, @@ -50,7 +48,15 @@ class RepositoryEditor extends Component { onUpdateProperty: PropTypes.func.isRequired, onUpdateProperties: PropTypes.func.isRequired, onDeleteProperty: PropTypes.func.isRequired, - onSortPropertyList: PropTypes.func.isRequired + onSortPropertyList: PropTypes.func.isRequired, + } + + constructor(props: any) { + super(props) + this.state = { + update: false, + exportPostman: false, + } } getChildContext() { return _.pick(this.props, Object.keys(RepositoryEditor.childContextTypes)) @@ -62,71 +68,68 @@ class RepositoryEditor extends Component { this.props.onFetchRepository({ id }) } } - - constructor(props) { - super(props) - this.state = { - update: false, - exportPostman: false - } - } render() { - let { location: { params }, auth, repository } = this.props - if (repository.data.name) { - document.title = `RAP2 ${repository.data.name}` - } - if (!repository.fetching && !repository.data) return
      404
      + const { location: { params }, auth } = this.props + let { repository } = this.props + if (!repository.fetching && !repository.data) { return
      404
      } repository = repository.data - if (!repository.id) return // // DONE 2.2 每次获取仓库都显示加载动画不合理,应该只在初始加载时显示动画。 + if (repository.name) { + document.title = `RAP2 ${repository.name}` + } + if (!repository.id) { return } // // DONE 2.2 每次获取仓库都显示加载动画不合理,应该只在初始加载时显示动画。 - let mod = repository && repository.modules && repository.modules.length - ? (repository.modules.find(item => item.id === +params.mod) || repository.modules[0]) : {} - let itf = mod.interfaces && mod.interfaces.length - ? (mod.interfaces.find(item => item.id === +params.itf) || mod.interfaces[0]) : {} - let properties = itf.properties || [] + const mod = repository && repository.modules && repository.modules.length + ? (repository.modules.find((item: any) => item.id === +params.mod) || repository.modules[0]) : {} + const itf = mod.interfaces && mod.interfaces.length + ? (mod.interfaces.find((item: any) => item.id === +params.itf) || mod.interfaces[0]) : {} + const properties = itf.properties || [] - let ownerlink = repository.organization + const ownerlink = repository.organization ? `/organization/repository?organization=${repository.organization.id}` : `/repository/joined?user=${repository.owner.id}` - let isOwned = repository.owner.id === auth.id - let isJoined = repository.members.find(itme => itme.id === auth.id) + const isOwned = repository.owner.id === auth.id + const isJoined = repository.members.find((item: any) => item.id === auth.id) return ( -
      -
      - - +
      +
      + + {repository.organization ? repository.organization.name : repository.owner.fullname} - / + / {repository.name} -
      +
      {/* 编辑权限:拥有者或者成员 */} {isOwned || isJoined - ? this.setState({ update: true })}> 编辑 + ? this.setState({ update: true })}> 编辑 : null } - this.setState({ update: false })} onResolve={this.handleUpdate}> - + this.setState({ update: false })} onResolve={this.handleUpdate}> + - 插件 - 数据 - 测试 - this.setState({ exportPostman: true })}> 导出Postman Collection - this.setState({ exportPostman: false })} onResolve={e => this.setState({ exportPostman: false })}> - + 插件 + 数据 + 测试 + this.setState({ exportPostman: true })}> 导出Postman Collection + this.setState({ exportPostman: false })} + onResolve={() => this.setState({ exportPostman: false })} + > +
      -
      {repository.description}
      +
      {repository.description}
      -
      +
      -
      +
      @@ -134,20 +137,17 @@ class RepositoryEditor extends Component {
      ) } - handleUpdate = (e) => { - let { store } = this.context - let { pathname, hash, search } = store.getState().router.location - store.dispatch(replace(pathname + search + hash)) - } - componentWillUnmount() { - // this.props.onClearRepository() + handleUpdate = () => { + const { pathname, hash, search } = this.props.router.location + this.props.replace(pathname + search + hash) } } // 容器组件 -const mapStateToProps = (state) => ({ +const mapStateToProps = (state: RootState) => ({ auth: state.auth, - repository: state.repository + repository: state.repository, + router: state.router, }) const mapDispatchToProps = ({ onFetchRepository: fetchRepository, @@ -168,7 +168,8 @@ const mapDispatchToProps = ({ onUpdateProperty: updateProperty, onUpdateProperties: updateProperties, onDeleteProperty: deleteProperty, - onSortPropertyList: sortPropertyList + onSortPropertyList: sortPropertyList, + replace, }) export default connect( mapStateToProps, diff --git a/src/components/editor/RepositorySearcher.jsx b/src/components/editor/RepositorySearcher.jsx deleted file mode 100644 index 6f573b8..0000000 --- a/src/components/editor/RepositorySearcher.jsx +++ /dev/null @@ -1,132 +0,0 @@ -import React, { Component } from 'react' -import { PropTypes, Link, StoreStateRouterLocationURI, URI } from '../../family' - -class Highlight extends Component { - static replace = (clip, seed) => { - if (!seed) return clip - let rseed = new RegExp(seed, 'ig') - return ('' + clip).replace(rseed, (matched) => - `${matched}` - ) - } - render () { - let { clip, seed } = this.props - let highlighted = { __html: Highlight.replace(clip, seed) } - return ( - - ) - } -} - -class DropdownMenu extends Component { - static filter = (respository, seed) => { - let nextRespository = { ...respository, modules: [] } - let counter = 0 - respository.modules.forEach(mod => { - let nextModule = { ...mod, interfaces: [] } - let matchModule = nextModule.name.indexOf(seed) !== -1 - if (matchModule) { - counter++ - nextRespository.modules.push(nextModule) - } - - mod.interfaces.forEach(itf => { - let nextInterface = { ...itf, properties: [] } - let matchInterface = nextInterface.name.indexOf(seed) !== -1 || nextInterface.url.indexOf(seed) !== -1 || nextInterface.method === seed - if (matchInterface) { - counter++ - if (!matchModule) { - matchModule = true - nextRespository.modules.push(nextModule) - } - nextModule.interfaces.push(nextInterface) - } - - itf.properties.forEach(property => { - let nextProperty = { ...property } - let matchProperty = nextProperty.name.indexOf(seed) !== -1 - if (matchProperty) { - counter++ - if (!matchModule) { - matchModule = true - nextRespository.modules.push(nextModule) - } - if (!matchInterface) { - matchInterface = true - nextModule.interfaces.push(nextInterface) - } - nextInterface.properties.push(nextProperty) - } - }) - }) - }) - return { nextRespository, counter } - } - static highlight = (clip, seed) => { - if (!seed) return clip - let rseed = new RegExp(seed, 'ig') - return ('' + clip).replace(rseed, (matched) => - `${matched}` - ) - } - static contextTypes = { - store: PropTypes.object - } - render () { - let { repository, seed, onSelect } = this.props - let uri = StoreStateRouterLocationURI(this.context.store).removeSearch('mod').removeSearch('itf') - let { nextRespository, counter } = DropdownMenu.filter(repository, seed) - if (counter === 0) return null - return ( -
      - {nextRespository.modules.map((mod, index, modules) => -
      - - 模块 - - - {mod.interfaces.map(itf => -
      - - 接口 - - - - - {itf.properties.map(property => - - 属性 - - - )} -
      - )} - {index < modules.length - 1 &&
      } -
      - )} -
      - ) - } -} - -// TODO 2.2 自动隐藏,高阶组件 -class RepositorySearcher extends Component { - constructor (props) { - super(props) - this.state = { seed: '' } - } - render () { - let { repository } = this.props - return ( -
      - { this.setState({ seed: e.target.value }) }} className='dropdown-input form-control' placeholder='工作区搜索' /> - {this.state.seed && } -
      - ) - } - clearSeed = (e) => { - this.setState({ seed: '' }) - } -} - -export default RepositorySearcher diff --git a/src/components/editor/RepositorySearcher.tsx b/src/components/editor/RepositorySearcher.tsx new file mode 100644 index 0000000..6c6cc97 --- /dev/null +++ b/src/components/editor/RepositorySearcher.tsx @@ -0,0 +1,145 @@ +import React, { Component } from 'react' +import { PropTypes, Link, StoreStateRouterLocationURI, connect } from '../../family' + +class Highlight extends Component { + static replace = (clip: any, seed: any) => { + if (!seed) { return clip } + const rseed = new RegExp(seed, 'ig') + return ('' + clip).replace(rseed, (matched) => + `${matched}` + ) + } + render() { + const { clip, seed } = this.props + const highlighted = { __html: Highlight.replace(clip, seed) } + return ( + + ) + } +} + +class DropdownMenuBase extends Component { + static contextTypes = { + store: PropTypes.object, + } + static filter = (respository: any, seed: any) => { + const nextRespository = { ...respository, modules: [] } + let counter = 0 + respository.modules.forEach((mod: any) => { + const nextModule = { ...mod, interfaces: [] } + let matchModule = nextModule.name.indexOf(seed) !== -1 + if (matchModule) { + counter++ + nextRespository.modules.push(nextModule) + } + + mod.interfaces.forEach((itf: any) => { + const nextInterface = { ...itf, properties: [] } + let matchInterface = nextInterface.name.indexOf(seed) !== -1 || nextInterface.url.indexOf(seed) !== -1 || nextInterface.method === seed + if (matchInterface) { + counter++ + if (!matchModule) { + matchModule = true + nextRespository.modules.push(nextModule) + } + nextModule.interfaces.push(nextInterface) + } + + itf.properties.forEach((property: any) => { + const nextProperty = { ...property } + const matchProperty = nextProperty.name.indexOf(seed) !== -1 + if (matchProperty) { + counter++ + if (!matchModule) { + matchModule = true + nextRespository.modules.push(nextModule) + } + if (!matchInterface) { + matchInterface = true + nextModule.interfaces.push(nextInterface) + } + nextInterface.properties.push(nextProperty) + } + }) + }) + }) + return { nextRespository, counter } + } + static highlight = (clip: any, seed: any) => { + if (!seed) { return clip } + const rseed = new RegExp(seed, 'ig') + return ('' + clip).replace(rseed, (matched) => + `${matched}` + ) + } + render() { + const { repository, seed, onSelect, router } = this.props + const uri = StoreStateRouterLocationURI(router).removeSearch('mod').removeSearch('itf') + const { nextRespository, counter } = DropdownMenu.filter(repository, seed) + if (counter === 0) { return null } + return ( +
      + {nextRespository.modules.map((mod: any, index: any, modules: any) => +
      + + 模块 + + + {mod.interfaces.map((itf: any) => +
      + + 接口 + + + + + {itf.properties.map((property: any) => + + 属性 + + + )} +
      + )} + {index < modules.length - 1 &&
      } +
      + )} +
      + ) + } +} + +const DropdownMenu = connect((state: any) => ({ router: state.router }))(DropdownMenuBase) + +// TODO 2.2 自动隐藏,高阶组件 +class RepositorySearcher extends Component { + constructor(props: any) { + super(props) + this.state = { seed: '' } + } + render() { + const { repository } = this.props + return ( +
      + { this.setState({ seed: e.target.value }) }} + className="dropdown-input form-control" + placeholder="工作区搜索" + /> + {this.state.seed && } +
      + ) + } + clearSeed = () => { + this.setState({ seed: '' }) + } +} + +export default connect((state: any) => ({ + router: state.router, +}))(RepositorySearcher) diff --git a/src/components/home/Home.jsx b/src/components/home/Home.jsx deleted file mode 100644 index 33db3e7..0000000 --- a/src/components/home/Home.jsx +++ /dev/null @@ -1,53 +0,0 @@ -import React from 'react' -import { connect, Link } from '../../family' -import { Spin } from '../utils' -import OwnedRepositoriesCard from './OwnedRepositoriesCard' -import JoinedRepositoriesCard from './JoinedRepositoriesCard' -import LogsCard from './LogsCard' -import './Home.css' -import { GoRepo } from 'react-icons/lib/go' - -const Maiden = () => ( -
      - 新建仓库 -
      -) - -// 展示组件 -const Home = ({ auth, owned, joined, logs }) => { - if (owned.fetching || joined.fetching || logs.fetching) return - - if (!owned.data.length && !joined.data.length) { - return ( -
      - -
      - ) - } - return ( -
      -
      -
      - -
      -
      - - -
      -
      -
      - ) -} - -// 容器组件 -const mapStateToProps = (state) => ({ - auth: state.auth, - owned: state.ownedRepositories, - joined: state.joinedRepositories, - logs: state.logs -}) -const mapDispatchToProps = ({}) -export default connect( - mapStateToProps, - mapDispatchToProps -)(Home) diff --git a/src/components/home/Home.tsx b/src/components/home/Home.tsx new file mode 100644 index 0000000..1c9ab43 --- /dev/null +++ b/src/components/home/Home.tsx @@ -0,0 +1,56 @@ +import React from 'react' +import { Link } from '../../family' +import { connect } from 'react-redux' +import { Spin } from '../utils' +import OwnedRepositoriesCard from './OwnedRepositoriesCard' +import JoinedRepositoriesCard from './JoinedRepositoriesCard' +import LogsCard from './LogsCard' +import './Home.css' +import { GoRepo } from 'react-icons/go' +import { RootState } from 'actions/types' + +const Maiden = () => ( +
      + 新建仓库 +
      +) + +// 展示组件 +const Home = ({ owned, joined, logs }: any) => { + if (owned.fetching || joined.fetching || logs.fetching) { return } + + if (!owned.data.length && !joined.data.length) { + return ( +
      + +
      + ) + } + return ( +
      +
      +
      + +
      +
      + +
      + +
      +
      +
      +
      + ) +} + +const mapStateToProps = (state: RootState) => ({ + auth: state.auth, + owned: state.ownedRepositories, + joined: state.joinedRepositories, + logs: state.logs, +}) +const mapDispatchToProps = ({}) +export default connect( + mapStateToProps, + mapDispatchToProps +)(Home) diff --git a/src/components/home/JoinedRepositoriesCard.jsx b/src/components/home/JoinedRepositoriesCard.tsx similarity index 63% rename from src/components/home/JoinedRepositoriesCard.jsx rename to src/components/home/JoinedRepositoriesCard.tsx index 55f32b0..92b8e5e 100644 --- a/src/components/home/JoinedRepositoriesCard.jsx +++ b/src/components/home/JoinedRepositoriesCard.tsx @@ -1,25 +1,26 @@ import React from 'react' import { Link } from '../../family' import { Spin } from '../utils' +import { Card } from '@material-ui/core' -const JoinedRepositoriesCard = ({ repositories }) => ( -
      -
      我加入的仓库
      +const JoinedRepositoriesCard = ({ repositories }: any) => ( + +
      我加入的仓库
      {repositories.fetching ? : ( -
      - {repositories.data.slice(0, 10).map(repository => +
      + {repositories.data.slice(0, 10).map((repository: any) =>

      )} {repositories.data.length === 0 ? - : null} {repositories.data.length > 10 - ? => 查看全部 {repositories.data.length} 个仓库 + ? => 查看全部 {repositories.data.length} 个仓库 : null }
      ) } -
      +
      ) -const JoinedRepositoryLink = ({ repository }) => ( +const JoinedRepositoryLink = ({ repository }: any) => ( {repository.organization ? repository.organization.name : repository.owner.fullname} / diff --git a/src/components/home/LogsCard.jsx b/src/components/home/LogsCard.tsx similarity index 59% rename from src/components/home/LogsCard.jsx rename to src/components/home/LogsCard.tsx index c76b785..4811858 100644 --- a/src/components/home/LogsCard.jsx +++ b/src/components/home/LogsCard.tsx @@ -1,6 +1,7 @@ import React from 'react' import { Link, moment } from '../../family' import { Spin } from '../utils' +import { Card } from '@material-ui/core' // DONE 2.3 重构 LogView // 1. √ 旧逻辑把 targe 和 type 混在一起,各种判断看的好蛋疼 @@ -10,56 +11,56 @@ import { Spin } from '../utils' // 2. 墨智邀请麦少加入群聊 // 4. √ 用户头像应该提成组件 UserAvatar,而不是混在 HTML 代码里 // 5. √ FromNow 应该提成组件 -const UserAvatar = ({ user }) => ( +const UserAvatar = ({ user }: any) => ( user - ? {user.empId} + ? {user.empId} : null ) -const UserLink = ({ user }) => ( - {user.fullname} +const UserLink = ({ user }: any) => ( + {user.fullname} ) -const LogUserView = ({ user }) => { +const LogUserView = ({ user }: any) => { return ( - + ) } -const LogTypeView = ({ type }) => { - let typeName = { +const LogTypeView = ({ type }: any) => { + const typeName: any = ({ create: '创建了', update: '修改了', delete: '删除了', lock: '锁定了', unlock: '释放了', join: '加入了', - exit: '退出了' - }[type] - return {typeName} + exit: '退出了', + } as any)[type] + return {typeName} } -const LogTargetView = ({ log }) => { - let targetType = (log.organization && 'organization') || // 团队 +const LogTargetView = ({ log }: any) => { + const targetType = (log.organization && 'organization') || // 团队 (log.repository && !log.module && !log.interface && 'repository') || // 仓库 (log.repository && log.module && !log.interface && 'module') || // 模块 (log.repository && log.module && log.interface && 'interface') // 接口 - switch (targetType) { + switch (targetType as any) { case 'organization': return !log.organization.deletedAt - ? {log.organization.name} + ? {log.organization.name} : {log.organization.name} case 'repository': return !log.repository.deletedAt - ? {log.repository.name} + ? {log.repository.name} : {log.repository.name} case 'module': return ( - + {!log.repository.deletedAt ? {log.repository.name} : {log.repository.name} } - / + / {!log.module.deletedAt ? {log.module.name} : {log.module.name} @@ -68,17 +69,17 @@ const LogTargetView = ({ log }) => { ) case 'interface': return ( - + {!log.repository.deletedAt ? {log.repository.name} : {log.repository.name} } - / + / {!log.module.deletedAt ? {log.module.name} : {log.module.name} } - / + / {!log.interface.deletedAt ? {log.interface.name} : {log.interface.name} @@ -86,27 +87,27 @@ const LogTargetView = ({ log }) => { ) default: - return
      + return
      } } -const FromNow = ({ date }) => ( - {moment(date).fromNow()} +const FromNow = ({ date }: any) => ( + {moment(date).fromNow()} ) -const LogView = ({ log }) => { +const LogView = ({ log }: any) => { if (log.creator && /join|exit/.test(log.type) && log.creator.id !== log.user.id) { // if (log.creator.id === log.user.id) return null - if (log.type === 'join') return - if (log.type === 'exit') return + if (log.type === 'join') { return } + if (log.type === 'exit') { return } } return ( -
      -
      +
      +
      -
      +
      @@ -115,45 +116,45 @@ const LogView = ({ log }) => { // DONE 2.3 支持『上帝之手』`log.creator` // 墨智邀请麦少加入群聊 -const JoinLogView = ({ log }) => { +const JoinLogView = ({ log }: any) => { return ( -
      -
      +
      +
      - 邀请 + 邀请 - 加入了 + 加入了
      -
      +
      ) } // 墨智将麦少移出群聊 -const ExitLogView = ({ log }) => { +const ExitLogView = ({ log }: any) => { return ( -
      -
      +
      +
      - + - 移出了 + 移出了
      -
      +
      ) } -const Log = ({ log }) => { // eslint-disable-line no-unused-vars - const userAvatar = {log.user.empId} - const userLink = {log.user.fullname} - const fromNow = {moment(log.updatedAt).fromNow()} - let targetName, targetLink, typeName +export const Log = ({ log }: any) => { + const userAvatar = {log.user.empId} + const userLink = {log.user.fullname} + const fromNow = {moment(log.updatedAt).fromNow()} + let targetName: any, targetLink: any, typeName: any if (log.organization) { // 团队 targetName = log.organization.name targetLink = !log.organization.deletedAt @@ -180,49 +181,56 @@ const Log = ({ log }) => { // eslint-disable-line no-unused-vars } switch (log.type) { case 'create': - return targetLink ?
      - {userAvatar} {userLink} 创建了 {targetLink} {fromNow} -
      : null + return targetLink ? ( +
      + {userAvatar} {userLink} 创建了 {targetLink} {fromNow} +
      + ) : null case 'update': - return targetLink ?
      - {userAvatar} {userLink} 修改了 {targetLink} {fromNow} -
      : null + return targetLink ? ( +
      + {userAvatar} {userLink} 修改了 {targetLink} {fromNow} +
      + ) : null case 'delete': - return targetName ?
      - {userAvatar} {userLink} 删除了 {targetName} {fromNow} -
      : null + return targetName ? ( +
      + {userAvatar} {userLink} 删除了 {targetName} {fromNow} +
      + ) : null case 'lock': case 'unlock': - typeName = { lock: '锁定', unlock: '释放' }[log.type] - if (!log.repository || !log.module || !log.interface) return null - return
      - {userAvatar} {userLink} {typeName}了 {targetLink} {fromNow} -
      + typeName = ({ lock: '锁定', unlock: '释放' } as any)[log.type] + if (!log.repository || !log.module || !log.interface) { return null } + return ( +
      + {userAvatar} {userLink} {typeName}了 {targetLink} {fromNow} +
      + ) case 'join': case 'exit': - typeName = { join: '加入', exit: '退出' }[log.type] - return targetName ?
      - {userAvatar} {userLink} {typeName}了 {targetLink} {fromNow} -
      : null + typeName = ({ join: '加入', exit: '退出' } as any)[log.type] + return targetName ? ( +
      + {userAvatar} {userLink} {typeName}了 {targetLink} {fromNow} +
      + ) : null default: return null - // return

      {JSON.stringify(log)}

      + // return

      {JSON.stringify(log)}

      } } -const LogsCard = ({ logs }) => ( -
      - {/* DONE 2.1 不屏蔽首页 */} - {/* DONE 2.2 √用户、√团队、√仓库的动态(简) */} - {/* DONE 2.2 团队-仓库、团队-用户的动态 */} - {/*
      React + Redux + Router + Saga
      */} +const LogsCard = ({ logs }: any) => ( + +
      最近活动
      {logs.fetching ? : ( -
      - {logs.data.map(log => +
      + {logs.data.map((log: any) => )}
      )} -
      +
      ) export default LogsCard diff --git a/src/components/home/OwnedRepositoriesCard.jsx b/src/components/home/OwnedRepositoriesCard.tsx similarity index 62% rename from src/components/home/OwnedRepositoriesCard.jsx rename to src/components/home/OwnedRepositoriesCard.tsx index e083c5f..b41ec5b 100644 --- a/src/components/home/OwnedRepositoriesCard.jsx +++ b/src/components/home/OwnedRepositoriesCard.tsx @@ -1,25 +1,26 @@ import React from 'react' import { Link } from '../../family' import { Spin } from '../utils' +import { Card } from '@material-ui/core' -const OwnedRepositoriesCard = ({ repositories }) => ( -
      -
      我拥有的仓库
      +const OwnedRepositoriesCard = ({ repositories }: any) => ( + +
      我拥有的仓库
      {repositories.fetching ? : ( -
      - {repositories.data.slice(0, 10).map(repository => +
      + {repositories.data.slice(0, 10).map((repository: any) =>

      )} {repositories.data.length === 0 ? - : null} {repositories.data.length > 10 - ? => 查看全部 {repositories.data.length} 个仓库 + ? => 查看全部 {repositories.data.length} 个仓库 : null }
      ) } -
      +
      ) -const OwnedRepositoryLink = ({ repository }) => ( +const OwnedRepositoryLink = ({ repository }: any) => ( {repository.organization ? repository.organization.name + ' / ' : ''} {repository.name} diff --git a/src/components/layout/Logo.tsx b/src/components/layout/Logo.tsx new file mode 100644 index 0000000..9d840af --- /dev/null +++ b/src/components/layout/Logo.tsx @@ -0,0 +1,9 @@ +import React from 'react' + +export default function Logo(props: {color?: string}) { + const color = props.color || '#FFFFFF' + return ( + // tslint:disable-next-line: max-line-length + + ) +} diff --git a/src/components/layout/MainMenu.tsx b/src/components/layout/MainMenu.tsx new file mode 100644 index 0000000..9c6ad0e --- /dev/null +++ b/src/components/layout/MainMenu.tsx @@ -0,0 +1,72 @@ +import React from 'react' +import { createStyles, makeStyles, Theme } from '@material-ui/core/styles' +import AppBar from '@material-ui/core/AppBar' +import Toolbar from '@material-ui/core/Toolbar' +import Button from '@material-ui/core/Button' +import { Link } from 'react-router-dom' +import { User } from 'actions/types' +import Logo from './Logo' +import { useDispatch } from 'react-redux' +import { logout } from 'actions/account' + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + root: { + flexGrow: 1, + width: '100%', + }, + menuButton: { + marginRight: theme.spacing(2), + }, + title: { + flexGrow: 1, + }, + link: { + color: '#FFFFFF', + '&:hover': { + color: '#FFFFFF', + }, + }, + right: { + float: 'right', + }, + toolbar: { + display: 'flex', + justifyContent: 'space-between', + }, + logo: { + marginRight: theme.spacing(2), + display: 'inline', + }, + }) +) + +interface Props { + user: User +} + +export default function MainMenu(props: Props) { + const { user } = props + const classes = useStyles() + const dispatch = useDispatch() + + return ( +
      + + +
      +
      + + + + + +
      +
      + {user.id && } +
      +
      +
      +
      + ) +} diff --git a/src/components/organization/AllOrganizationList.jsx b/src/components/organization/AllOrganizationList.tsx similarity index 69% rename from src/components/organization/AllOrganizationList.jsx rename to src/components/organization/AllOrganizationList.tsx index 1de5bb4..90fd521 100644 --- a/src/components/organization/AllOrganizationList.jsx +++ b/src/components/organization/AllOrganizationList.tsx @@ -2,25 +2,26 @@ import React, { Component } from 'react' import { connect } from 'react-redux' import { contextTypes, childContextTypes, getChildContext, CreateButton, OrganizationsTypeDropdown, SearchGroup, OrganizationListWithSpin, PaginationWithLocation, mapDispatchToProps } from './OrganizationListParts' import './Organization.css' +import { RootState } from 'actions/types' // 所有团队 -class JoinedOrganizationList extends Component { +class JoinedOrganizationList extends Component { static contextTypes = contextTypes static childContextTypes = childContextTypes getChildContext = getChildContext - render () { - let { location, match, organizations } = this.props + render() { + const { location, match, organizations } = this.props return ( -
      -
      scopescope 描述
      +
      +
      @@ -84,30 +85,30 @@ class StringRuleEditor extends Component { @@ -115,24 +116,23 @@ class StringRuleEditor extends Component { ) } - componentDidUpdate () { - } onChange = () => { + /** empty */ } } -class IntegerRuleEditor extends Component { - constructor (props) { +class IntegerRuleEditor extends Component { + constructor(props: any) { super(props) this.state = { type: '', count: 1, min: 3, max: 7, - value: '' + value: '', } } - get () { + get() { switch (this.state.type) { case '': return { [`name`]: this.state.value } @@ -141,43 +141,44 @@ class IntegerRuleEditor extends Component { default: console.warn('错误的生成规则') } + return '' } - render () { + render() { return ( -
      +
      整数 - this.setState({ type: e.target.value })} className="type"> + +
      {this.state.type === '' ? - this.setState({ value: e.target.value })} className='value' /> + this.setState({ value: e.target.value })} className="value" /> : null} {this.state.type === '|min-max' ? 大于等于 - this.setState({ min: e.target.value })} className='min' /> + this.setState({ min: e.target.value })} className="min" /> ,小于等于 - this.setState({ max: e.target.value })} className='max' /> + this.setState({ max: e.target.value })} className="max" /> : null}
      ) } - componentDidUpdate () { - let template = this.get() + componentDidUpdate() { + const template = this.get() console.log(template, '=>', mock(template)) } onChange = () => { console.log(this.get()) } } -class FloatRuleEditor extends Component { - constructor (props) { +class FloatRuleEditor extends Component { + constructor(props: any) { super(props) this.state = { type: '', @@ -186,10 +187,10 @@ class FloatRuleEditor extends Component { max: 7, dmin: 3, dmax: 7, - value: '' + value: '', } } - get () { + get() { switch (this.state.type) { case '': return { [`name`]: +this.state.value } @@ -198,32 +199,33 @@ class FloatRuleEditor extends Component { default: console.warn('错误的生成规则') } + return '' } - render () { + render() { return ( -
      +
      浮点数 - this.setState({ type: e.target.value })} className="type"> + +
      {this.state.type === '' ? - this.setState({ value: e.target.value })} className='value' /> + this.setState({ value: e.target.value })} className="value" /> : null} {this.state.type === '|min-max.dmin-dmax' ? 整数部分 大于等于 - this.setState({ min: e.target.value })} className='min' /> + this.setState({ min: e.target.value })} className="min" /> ,小于等于 - this.setState({ max: e.target.value })} className='max' /> + this.setState({ max: e.target.value })} className="max" /> ,小数部分保留 - this.setState({ dmin: e.target.value })} className='min' /> + this.setState({ dmin: e.target.value })} className="min" /> - this.setState({ dmax: e.target.value })} className='max' /> + this.setState({ dmax: e.target.value })} className="max" /> : null} @@ -231,8 +233,8 @@ class FloatRuleEditor extends Component {
      ) } - componentDidUpdate () { - let template = this.get() + componentDidUpdate() { + const template = this.get() console.log(template, '=>', mock(template)) } onChange = () => { @@ -240,52 +242,52 @@ class FloatRuleEditor extends Component { } } -class PropertyEditor extends Component { - constructor (props) { +class PropertyEditor extends Component { + constructor(props: any) { super(props) this.state = { name: 'name', type: 'String', rule: '', - value: '' + value: '', } } - render () { - let template = { - [`${this.state.name}|${this.state.rule}`]: fixValue(this.state) + render() { + const template = { + [`${this.state.name}|${this.state.rule}`]: fixValue(this.state), } - let data = mock(template) + const data = mock(template) return ( -
      -
      +
      +

      -
      -
      - - this.setState({ name: e.target.value })} className='form-control' /> +
      +
      + + this.setState({ name: e.target.value })} className="form-control" />
      -
      - - this.setState({ type: e.target.value })} className="form-control"> {TYPES.map(type => )}
      -
      - - this.setState({ rule: e.target.value })} className='form-control' /> +
      + + this.setState({ rule: e.target.value })} className="form-control" />
      -
      - - this.setState({ value: e.target.value })} className='form-control' /> +
      + + this.setState({ value: e.target.value })} className="form-control" />
      -
      +
      {JSON.stringify(template, null, 2)}
      {JSON.stringify(data, null, 2)}
      diff --git a/src/components/utils/RChart.jsx b/src/components/utils/RChart.tsx similarity index 69% rename from src/components/utils/RChart.jsx rename to src/components/utils/RChart.tsx index aacc5ab..95e89d6 100644 --- a/src/components/utils/RChart.jsx +++ b/src/components/utils/RChart.tsx @@ -2,13 +2,13 @@ import React, { Component } from 'react' import PropTypes from 'prop-types' import Chart from 'chart.js' -class RChart extends Component { +class RChart extends Component { static propTypes = { type: PropTypes.string.isRequired, width: PropTypes.string, // TODO 非必须,有默认值 16 height: PropTypes.string, // TODO 非必须,有默认值 9 data: PropTypes.object.isRequired, - options: PropTypes.object.isRequired + options: PropTypes.object.isRequired, } static COLORS = { red: 'rgb(255, 99, 132)', @@ -17,21 +17,23 @@ class RChart extends Component { green: 'rgb(75, 192, 192)', blue: 'rgb(54, 162, 235)', purple: 'rgb(153, 102, 255)', - grey: 'rgb(201, 203, 207)' + grey: 'rgb(201, 203, 207)', } - render () { - let width = this.props.width || 16 - let height = this.props.height || 9 + $canvas: any + $chart: any + render() { + const width = this.props.width || 16 + const height = this.props.height || 9 return ( { this.$canvas = $canvas }} width={width} height={height} /> ) } - componentDidUpdate () { - var ctx = this.$canvas.getContext('2d') + componentDidUpdate() { + const ctx = this.$canvas.getContext('2d') this.$chart = new Chart(ctx, { type: this.props.type, data: this.props.data, - options: this.props.options + options: this.props.options, }) } } diff --git a/src/components/utils/RCodeMirror.jsx b/src/components/utils/RCodeMirror.jsx deleted file mode 100644 index 504bb8f..0000000 --- a/src/components/utils/RCodeMirror.jsx +++ /dev/null @@ -1,26 +0,0 @@ -import React, { Component } from 'react' -import CodeMirror from 'codemirror' -import 'codemirror/mode/javascript/javascript' -import 'codemirror/lib/codemirror.css' -import './RCodeMirror.css' - -class RCodeMirror extends Component { - static propTypes = {} - render () { - return
      -
      字符串 - this.setState({ rule: e.target.value })} className="rule"> + + + {this.state.rule === '|count' && 重复 - this.setState({ count: e.target.value })} className='count' /> + this.setState({ count: e.target.value })} className="count" /> } {this.state.rule === '|min-max' && 重复次数 -
      大于等于 this.setState({ min: e.target.value })} className='min' />
      -
      小于等于 this.setState({ max: e.target.value })} className='max' />
      +
      大于等于 this.setState({ min: e.target.value })} className="min" />
      +
      小于等于 this.setState({ max: e.target.value })} className="max" />
      }
      - this.setState({ value: e.target.value })} className='value' /> + this.setState({ value: e.target.value })} className="value" />