From bd1c3692c5fd50708c9b48e2bfa37d6fcf993c50 Mon Sep 17 00:00:00 2001 From: alirehmani Date: Fri, 10 May 2024 17:50:17 +0500 Subject: [PATCH] update --- src/Managing.WebApp/.env | 6 - src/Managing.WebApp/.eslintignore | 2 - src/Managing.WebApp/.eslintrc | 98 - src/Managing.WebApp/.gitattributes | 3 - .../.github/workflows/build.yml | 18 - .../.github/workflows/lint.yml | 18 - .../.github/workflows/test.yml | 18 - .../.github/workflows/typecheck.yml | 18 - src/Managing.WebApp/.gitignore | 5 - src/Managing.WebApp/.prettierignore | 5 - src/Managing.WebApp/.prettierrc | 4 - src/Managing.WebApp/Dockerfile | 39 - src/Managing.WebApp/Dockerfile-web-ui-dev | 43 - src/Managing.WebApp/LICENSE | 21 - src/Managing.WebApp/README.md | 94 - src/Managing.WebApp/index.html | 16 - .../install_problematic_packages.sh | 32 - src/Managing.WebApp/jest.config.js | 35 - src/Managing.WebApp/mockServiceWorker.js | 338 --- src/Managing.WebApp/package.json | 104 - src/Managing.WebApp/postcss.config.js | 6 - src/Managing.WebApp/prettier.config.js | 4 - src/Managing.WebApp/scripts/validate | 11 - src/Managing.WebApp/src/app/index.tsx | 13 - .../src/app/providers/Hubs.tsx | 18 - src/Managing.WebApp/src/app/routes/index.tsx | 118 - .../src/app/store/accountStore.tsx | 22 - .../src/app/store/apiStore.tsx | 28 - .../src/app/store/flowStore.tsx | 28 - .../app/store/selectors/workflowSelector.tsx | 13 - .../src/app/store/workflowStore.tsx | 113 - src/Managing.WebApp/src/assets/img/logo.png | Bin 33041 -> 0 bytes .../src/assets/img/tradingview.png | Bin 95197 -> 0 bytes .../src/components/atoms/List/List.tsx | 7 - .../src/components/atoms/Loader/Loader.tsx | 16 - .../src/components/atoms/MyLink/MyLink.tsx | 42 - .../src/components/atoms/Select/Select.tsx | 7 - .../src/components/atoms/Slider/Slider.tsx | 30 - .../src/components/atoms/index.tsx | 5 - .../src/components/mollecules/Card/Card.tsx | 45 - .../mollecules/CardText/CardText.tsx | 77 - .../mollecules/FormInput/FormInput.tsx | 24 - .../mollecules/GridTile/GridTile.tsx | 38 - .../src/components/mollecules/LogIn/LogIn.tsx | 101 - .../components/mollecules/LogIn/Profile.tsx | 26 - .../src/components/mollecules/Modal/Modal.tsx | 34 - .../mollecules/Modal/ModalHeader.tsx | 19 - .../components/mollecules/NavBar/NavBar.tsx | 130 - .../components/mollecules/NavItem/NavItem.tsx | 33 - .../mollecules/PieChart/PieChart.tsx | 46 - .../mollecules/Table/SelectColumnFilter.tsx | 36 - .../src/components/mollecules/Table/Table.tsx | 231 -- .../src/components/mollecules/Tabs/Tabs.tsx | 77 - .../ThemeSelector/ThemeSelector.tsx | 21 - .../src/components/mollecules/Toast/Toast.tsx | 31 - .../src/components/mollecules/index.tsx | 14 - .../components/organism/Account/Account.tsx | 13 - .../organism/ActiveBots/ActiveBots.tsx | 211 -- .../organism/Backtest/backtestCards.tsx | 309 --- .../organism/Backtest/backtestModal.tsx | 361 --- .../organism/Backtest/backtestRowDetails.tsx | 47 - .../organism/Backtest/backtestTable.tsx | 275 -- .../CustomMoneyManagement.tsx | 104 - .../organism/Positions/PositionList.tsx | 128 - .../Positions/PositionStatusBadge.tsx | 30 - .../SpotLightBadge/SpotLightBadge.tsx | 31 - .../organism/StatusBadge/StatusBadge.tsx | 14 - .../organism/Trading/CardPositionItem.tsx | 34 - .../components/organism/Trading/Summary.tsx | 133 - .../Trading/TradeChart/TradeChart.tsx | 302 --- .../components/organism/Workflow/flowItem.tsx | 22 - .../organism/Workflow/flows/Flow.tsx | 40 - .../Workflow/flows/feed/feedTicker.tsx | 62 - .../flows/strategies/RsiDivergenceFlow.tsx | 58 - .../flows/trading/OpenPositionFlow.tsx | 96 - .../organism/Workflow/flows/trading/test.tsx | 24 - .../organism/Workflow/workflowCanvas.tsx | 255 -- .../organism/Workflow/workflowForm.tsx | 69 - .../organism/Workflow/workflowSidebar.tsx | 35 - .../src/components/organism/index.tsx | 10 - src/Managing.WebApp/src/favicon.svg | 15 - .../src/generated/AuthorizedApiBase.ts | 30 - src/Managing.WebApp/src/generated/IConfig.ts | 7 - .../src/generated/ManagingApi.ts | 2310 ----------------- .../src/generated/ManagingWorkerApi.tsx | 158 -- src/Managing.WebApp/src/global/enum.tsx | 0 src/Managing.WebApp/src/global/helpers.tsx | 9 - src/Managing.WebApp/src/global/type.tsx | 289 --- src/Managing.WebApp/src/hooks/useAccounts.tsx | 27 - src/Managing.WebApp/src/hooks/useCookie.ts | 40 - .../src/hooks/useCustomEffect.tsx | 6 - .../src/hooks/useDidUpdateEffect.ts | 21 - .../src/hooks/useLocalStorage.tsx | 39 - src/Managing.WebApp/src/hooks/useModal.tsx | 11 - .../src/hooks/useOutsideClick.tsx | 38 - .../src/hooks/usePositionHub.tsx | 47 - src/Managing.WebApp/src/hooks/useTheme.tsx | 149 -- src/Managing.WebApp/src/hooks/useToggle.tsx | 9 - src/Managing.WebApp/src/layouts/index.tsx | 31 - src/Managing.WebApp/src/logo.svg | 7 - src/Managing.WebApp/src/main.tsx | 38 - .../src/pages/authPage/auth.tsx | 30 - .../src/pages/backtestPage/backtest.tsx | 40 - .../src/pages/backtestPage/backtestLoop.tsx | 36 - .../pages/backtestPage/backtestPlayground.tsx | 35 - .../pages/backtestPage/backtestScanner.tsx | 189 -- .../src/pages/botsPage/botList.tsx | 265 -- .../src/pages/botsPage/bots.tsx | 280 -- .../dashboardPage/analytics/analytics.tsx | 52 - .../src/pages/dashboardPage/analytics/cme.tsx | 46 - .../pages/dashboardPage/analytics/futures.tsx | 46 - .../pages/dashboardPage/analytics/options.tsx | 37 - .../pages/dashboardPage/analytics/prices.tsx | 37 - .../pages/dashboardPage/analytics/spot.tsx | 28 - .../src/pages/dashboardPage/dashboard.tsx | 35 - .../src/pages/dashboardPage/monitoring.tsx | 16 - .../src/pages/desk/deskWidget.tsx | 23 - .../pages/desk/widgets/OpenPositionWidget.tsx | 163 -- .../pages/desk/widgets/PositionsWidget.tsx | 32 - .../pages/desk/widgets/TradeChartWidget.tsx | 91 - .../src/pages/desk/widgets/WorkersWidget.tsx | 246 -- .../src/pages/scenarioPage/scenario.tsx | 38 - .../src/pages/scenarioPage/scenarioList.tsx | 110 - .../src/pages/scenarioPage/scenarioTable.tsx | 83 - .../src/pages/scenarioPage/strategyList.tsx | 401 --- .../src/pages/scenarioPage/strategyTable.tsx | 94 - .../settingsPage/account/accountModal.tsx | 178 -- .../account/accountRowDetails.tsx | 63 - .../settingsPage/account/accountSettings.tsx | 47 - .../settingsPage/account/accountTable.tsx | 163 -- .../moneymanagement/moneyManagement.tsx | 52 - .../moneymanagement/moneyManagementModal.tsx | 190 -- .../moneymanagement/moneymanagementTable.tsx | 133 - .../src/pages/settingsPage/settings.tsx | 46 - .../src/pages/settingsPage/theme.tsx | 14 - .../src/pages/toolsPage/rektFees.tsx | 158 -- .../pages/toolsPage/spotlight/spotlight.tsx | 76 - .../toolsPage/spotlight/spotlightSummary.tsx | 210 -- .../toolsPage/spotlight/spotlightTable.tsx | 94 - .../src/pages/toolsPage/tools.tsx | 36 - .../src/pages/web3Page/web3.tsx | 104 - .../src/pages/workflow/workflows.tsx | 98 - src/Managing.WebApp/src/polyfills.ts | 4 - .../src/smartcontracts/courses/mood.sol | 16 - .../src/smartcontracts/courses/odaNFT.sol | 14 - .../src/smartcontracts/courses/odaToken.sol | 10 - src/Managing.WebApp/src/stores/store.tsx | 3 - src/Managing.WebApp/src/styles/app.css | 46 - src/Managing.WebApp/src/styles/globals.css | 23 - src/Managing.WebApp/src/vite-env.d.ts | 3 - src/Managing.WebApp/tailwind.config.js | 12 - src/Managing.WebApp/tsconfig.json | 21 - src/Managing.WebApp/vite.config.ts | 20 - 153 files changed, 12612 deletions(-) delete mode 100644 src/Managing.WebApp/.env delete mode 100644 src/Managing.WebApp/.eslintignore delete mode 100644 src/Managing.WebApp/.eslintrc delete mode 100644 src/Managing.WebApp/.gitattributes delete mode 100644 src/Managing.WebApp/.github/workflows/build.yml delete mode 100644 src/Managing.WebApp/.github/workflows/lint.yml delete mode 100644 src/Managing.WebApp/.github/workflows/test.yml delete mode 100644 src/Managing.WebApp/.github/workflows/typecheck.yml delete mode 100644 src/Managing.WebApp/.gitignore delete mode 100644 src/Managing.WebApp/.prettierignore delete mode 100644 src/Managing.WebApp/.prettierrc delete mode 100644 src/Managing.WebApp/Dockerfile delete mode 100644 src/Managing.WebApp/Dockerfile-web-ui-dev delete mode 100644 src/Managing.WebApp/LICENSE delete mode 100644 src/Managing.WebApp/README.md delete mode 100644 src/Managing.WebApp/index.html delete mode 100644 src/Managing.WebApp/install_problematic_packages.sh delete mode 100644 src/Managing.WebApp/jest.config.js delete mode 100644 src/Managing.WebApp/mockServiceWorker.js delete mode 100644 src/Managing.WebApp/package.json delete mode 100644 src/Managing.WebApp/postcss.config.js delete mode 100644 src/Managing.WebApp/prettier.config.js delete mode 100644 src/Managing.WebApp/scripts/validate delete mode 100644 src/Managing.WebApp/src/app/index.tsx delete mode 100644 src/Managing.WebApp/src/app/providers/Hubs.tsx delete mode 100644 src/Managing.WebApp/src/app/routes/index.tsx delete mode 100644 src/Managing.WebApp/src/app/store/accountStore.tsx delete mode 100644 src/Managing.WebApp/src/app/store/apiStore.tsx delete mode 100644 src/Managing.WebApp/src/app/store/flowStore.tsx delete mode 100644 src/Managing.WebApp/src/app/store/selectors/workflowSelector.tsx delete mode 100644 src/Managing.WebApp/src/app/store/workflowStore.tsx delete mode 100644 src/Managing.WebApp/src/assets/img/logo.png delete mode 100644 src/Managing.WebApp/src/assets/img/tradingview.png delete mode 100644 src/Managing.WebApp/src/components/atoms/List/List.tsx delete mode 100644 src/Managing.WebApp/src/components/atoms/Loader/Loader.tsx delete mode 100644 src/Managing.WebApp/src/components/atoms/MyLink/MyLink.tsx delete mode 100644 src/Managing.WebApp/src/components/atoms/Select/Select.tsx delete mode 100644 src/Managing.WebApp/src/components/atoms/Slider/Slider.tsx delete mode 100644 src/Managing.WebApp/src/components/atoms/index.tsx delete mode 100644 src/Managing.WebApp/src/components/mollecules/Card/Card.tsx delete mode 100644 src/Managing.WebApp/src/components/mollecules/CardText/CardText.tsx delete mode 100644 src/Managing.WebApp/src/components/mollecules/FormInput/FormInput.tsx delete mode 100644 src/Managing.WebApp/src/components/mollecules/GridTile/GridTile.tsx delete mode 100644 src/Managing.WebApp/src/components/mollecules/LogIn/LogIn.tsx delete mode 100644 src/Managing.WebApp/src/components/mollecules/LogIn/Profile.tsx delete mode 100644 src/Managing.WebApp/src/components/mollecules/Modal/Modal.tsx delete mode 100644 src/Managing.WebApp/src/components/mollecules/Modal/ModalHeader.tsx delete mode 100644 src/Managing.WebApp/src/components/mollecules/NavBar/NavBar.tsx delete mode 100644 src/Managing.WebApp/src/components/mollecules/NavItem/NavItem.tsx delete mode 100644 src/Managing.WebApp/src/components/mollecules/PieChart/PieChart.tsx delete mode 100644 src/Managing.WebApp/src/components/mollecules/Table/SelectColumnFilter.tsx delete mode 100644 src/Managing.WebApp/src/components/mollecules/Table/Table.tsx delete mode 100644 src/Managing.WebApp/src/components/mollecules/Tabs/Tabs.tsx delete mode 100644 src/Managing.WebApp/src/components/mollecules/ThemeSelector/ThemeSelector.tsx delete mode 100644 src/Managing.WebApp/src/components/mollecules/Toast/Toast.tsx delete mode 100644 src/Managing.WebApp/src/components/mollecules/index.tsx delete mode 100644 src/Managing.WebApp/src/components/organism/Account/Account.tsx delete mode 100644 src/Managing.WebApp/src/components/organism/ActiveBots/ActiveBots.tsx delete mode 100644 src/Managing.WebApp/src/components/organism/Backtest/backtestCards.tsx delete mode 100644 src/Managing.WebApp/src/components/organism/Backtest/backtestModal.tsx delete mode 100644 src/Managing.WebApp/src/components/organism/Backtest/backtestRowDetails.tsx delete mode 100644 src/Managing.WebApp/src/components/organism/Backtest/backtestTable.tsx delete mode 100644 src/Managing.WebApp/src/components/organism/CustomMoneyManagement/CustomMoneyManagement.tsx delete mode 100644 src/Managing.WebApp/src/components/organism/Positions/PositionList.tsx delete mode 100644 src/Managing.WebApp/src/components/organism/Positions/PositionStatusBadge.tsx delete mode 100644 src/Managing.WebApp/src/components/organism/SpotLightBadge/SpotLightBadge.tsx delete mode 100644 src/Managing.WebApp/src/components/organism/StatusBadge/StatusBadge.tsx delete mode 100644 src/Managing.WebApp/src/components/organism/Trading/CardPositionItem.tsx delete mode 100644 src/Managing.WebApp/src/components/organism/Trading/Summary.tsx delete mode 100644 src/Managing.WebApp/src/components/organism/Trading/TradeChart/TradeChart.tsx delete mode 100644 src/Managing.WebApp/src/components/organism/Workflow/flowItem.tsx delete mode 100644 src/Managing.WebApp/src/components/organism/Workflow/flows/Flow.tsx delete mode 100644 src/Managing.WebApp/src/components/organism/Workflow/flows/feed/feedTicker.tsx delete mode 100644 src/Managing.WebApp/src/components/organism/Workflow/flows/strategies/RsiDivergenceFlow.tsx delete mode 100644 src/Managing.WebApp/src/components/organism/Workflow/flows/trading/OpenPositionFlow.tsx delete mode 100644 src/Managing.WebApp/src/components/organism/Workflow/flows/trading/test.tsx delete mode 100644 src/Managing.WebApp/src/components/organism/Workflow/workflowCanvas.tsx delete mode 100644 src/Managing.WebApp/src/components/organism/Workflow/workflowForm.tsx delete mode 100644 src/Managing.WebApp/src/components/organism/Workflow/workflowSidebar.tsx delete mode 100644 src/Managing.WebApp/src/components/organism/index.tsx delete mode 100644 src/Managing.WebApp/src/favicon.svg delete mode 100644 src/Managing.WebApp/src/generated/AuthorizedApiBase.ts delete mode 100644 src/Managing.WebApp/src/generated/IConfig.ts delete mode 100644 src/Managing.WebApp/src/generated/ManagingApi.ts delete mode 100644 src/Managing.WebApp/src/generated/ManagingWorkerApi.tsx delete mode 100644 src/Managing.WebApp/src/global/enum.tsx delete mode 100644 src/Managing.WebApp/src/global/helpers.tsx delete mode 100644 src/Managing.WebApp/src/global/type.tsx delete mode 100644 src/Managing.WebApp/src/hooks/useAccounts.tsx delete mode 100644 src/Managing.WebApp/src/hooks/useCookie.ts delete mode 100644 src/Managing.WebApp/src/hooks/useCustomEffect.tsx delete mode 100644 src/Managing.WebApp/src/hooks/useDidUpdateEffect.ts delete mode 100644 src/Managing.WebApp/src/hooks/useLocalStorage.tsx delete mode 100644 src/Managing.WebApp/src/hooks/useModal.tsx delete mode 100644 src/Managing.WebApp/src/hooks/useOutsideClick.tsx delete mode 100644 src/Managing.WebApp/src/hooks/usePositionHub.tsx delete mode 100644 src/Managing.WebApp/src/hooks/useTheme.tsx delete mode 100644 src/Managing.WebApp/src/hooks/useToggle.tsx delete mode 100644 src/Managing.WebApp/src/layouts/index.tsx delete mode 100644 src/Managing.WebApp/src/logo.svg delete mode 100644 src/Managing.WebApp/src/main.tsx delete mode 100644 src/Managing.WebApp/src/pages/authPage/auth.tsx delete mode 100644 src/Managing.WebApp/src/pages/backtestPage/backtest.tsx delete mode 100644 src/Managing.WebApp/src/pages/backtestPage/backtestLoop.tsx delete mode 100644 src/Managing.WebApp/src/pages/backtestPage/backtestPlayground.tsx delete mode 100644 src/Managing.WebApp/src/pages/backtestPage/backtestScanner.tsx delete mode 100644 src/Managing.WebApp/src/pages/botsPage/botList.tsx delete mode 100644 src/Managing.WebApp/src/pages/botsPage/bots.tsx delete mode 100644 src/Managing.WebApp/src/pages/dashboardPage/analytics/analytics.tsx delete mode 100644 src/Managing.WebApp/src/pages/dashboardPage/analytics/cme.tsx delete mode 100644 src/Managing.WebApp/src/pages/dashboardPage/analytics/futures.tsx delete mode 100644 src/Managing.WebApp/src/pages/dashboardPage/analytics/options.tsx delete mode 100644 src/Managing.WebApp/src/pages/dashboardPage/analytics/prices.tsx delete mode 100644 src/Managing.WebApp/src/pages/dashboardPage/analytics/spot.tsx delete mode 100644 src/Managing.WebApp/src/pages/dashboardPage/dashboard.tsx delete mode 100644 src/Managing.WebApp/src/pages/dashboardPage/monitoring.tsx delete mode 100644 src/Managing.WebApp/src/pages/desk/deskWidget.tsx delete mode 100644 src/Managing.WebApp/src/pages/desk/widgets/OpenPositionWidget.tsx delete mode 100644 src/Managing.WebApp/src/pages/desk/widgets/PositionsWidget.tsx delete mode 100644 src/Managing.WebApp/src/pages/desk/widgets/TradeChartWidget.tsx delete mode 100644 src/Managing.WebApp/src/pages/desk/widgets/WorkersWidget.tsx delete mode 100644 src/Managing.WebApp/src/pages/scenarioPage/scenario.tsx delete mode 100644 src/Managing.WebApp/src/pages/scenarioPage/scenarioList.tsx delete mode 100644 src/Managing.WebApp/src/pages/scenarioPage/scenarioTable.tsx delete mode 100644 src/Managing.WebApp/src/pages/scenarioPage/strategyList.tsx delete mode 100644 src/Managing.WebApp/src/pages/scenarioPage/strategyTable.tsx delete mode 100644 src/Managing.WebApp/src/pages/settingsPage/account/accountModal.tsx delete mode 100644 src/Managing.WebApp/src/pages/settingsPage/account/accountRowDetails.tsx delete mode 100644 src/Managing.WebApp/src/pages/settingsPage/account/accountSettings.tsx delete mode 100644 src/Managing.WebApp/src/pages/settingsPage/account/accountTable.tsx delete mode 100644 src/Managing.WebApp/src/pages/settingsPage/moneymanagement/moneyManagement.tsx delete mode 100644 src/Managing.WebApp/src/pages/settingsPage/moneymanagement/moneyManagementModal.tsx delete mode 100644 src/Managing.WebApp/src/pages/settingsPage/moneymanagement/moneymanagementTable.tsx delete mode 100644 src/Managing.WebApp/src/pages/settingsPage/settings.tsx delete mode 100644 src/Managing.WebApp/src/pages/settingsPage/theme.tsx delete mode 100644 src/Managing.WebApp/src/pages/toolsPage/rektFees.tsx delete mode 100644 src/Managing.WebApp/src/pages/toolsPage/spotlight/spotlight.tsx delete mode 100644 src/Managing.WebApp/src/pages/toolsPage/spotlight/spotlightSummary.tsx delete mode 100644 src/Managing.WebApp/src/pages/toolsPage/spotlight/spotlightTable.tsx delete mode 100644 src/Managing.WebApp/src/pages/toolsPage/tools.tsx delete mode 100644 src/Managing.WebApp/src/pages/web3Page/web3.tsx delete mode 100644 src/Managing.WebApp/src/pages/workflow/workflows.tsx delete mode 100644 src/Managing.WebApp/src/polyfills.ts delete mode 100644 src/Managing.WebApp/src/smartcontracts/courses/mood.sol delete mode 100644 src/Managing.WebApp/src/smartcontracts/courses/odaNFT.sol delete mode 100644 src/Managing.WebApp/src/smartcontracts/courses/odaToken.sol delete mode 100644 src/Managing.WebApp/src/stores/store.tsx delete mode 100644 src/Managing.WebApp/src/styles/app.css delete mode 100644 src/Managing.WebApp/src/styles/globals.css delete mode 100644 src/Managing.WebApp/src/vite-env.d.ts delete mode 100644 src/Managing.WebApp/tailwind.config.js delete mode 100644 src/Managing.WebApp/tsconfig.json delete mode 100644 src/Managing.WebApp/vite.config.ts diff --git a/src/Managing.WebApp/.env b/src/Managing.WebApp/.env deleted file mode 100644 index a673da2..0000000 --- a/src/Managing.WebApp/.env +++ /dev/null @@ -1,6 +0,0 @@ -VITE_API_URL_LOCAL=https://localhost:5001 -VITE_API_URL_SERVER=https://localhost -VITE_WORKER_URL_LOCAL=https://localhost:5002 -VITE_WORKER_URL_SERVER=https://localhost:444 -ALCHEMY_ID=Bao7OirVe4bmYiDbPh0l8cs5gYb5D4_9 -WALLET_CONNECT_PROJECT_ID=363bf09c10fec2293b21ee199b2ce8d5 \ No newline at end of file diff --git a/src/Managing.WebApp/.eslintignore b/src/Managing.WebApp/.eslintignore deleted file mode 100644 index f06235c..0000000 --- a/src/Managing.WebApp/.eslintignore +++ /dev/null @@ -1,2 +0,0 @@ -node_modules -dist diff --git a/src/Managing.WebApp/.eslintrc b/src/Managing.WebApp/.eslintrc deleted file mode 100644 index 2d7c78b..0000000 --- a/src/Managing.WebApp/.eslintrc +++ /dev/null @@ -1,98 +0,0 @@ -{ - "root": true, - "extends": [ - "plugin:@typescript-eslint/recommended", - "plugin:jsx-a11y/recommended" - ], - "parser": "@typescript-eslint/parser", - "plugins": [ - "jsx-a11y", - "import", - "sort-keys-fix", - "react-hooks", - "@typescript-eslint", - "prettier" - ], - "env": { - "browser": true, - "node": true, - "es6": true, - "jest": true - }, - "globals": { - "JSX": "readonly" - }, - "settings": { - "react": { - "version": "detect" - }, - "import/parsers": { - "@typescript-eslint/parser": [".ts", ".tsx"] - }, - "import/resolver": { - "node": { - "extensions": [".js", ".jsx", ".ts", ".tsx"] - }, - "typescript": { - "alwaysTryTypes": true, - // always try to resolve types under `@types` directory even it doesn't contain any source code, like `@types/unist` - "project": ["tsconfig.json"] - } - } - }, - "rules": { - "no-alert": "error", - "no-console": "error", - "react-hooks/rules-of-hooks": "error", - "prettier/prettier": [ - "warn", - {}, - { - "properties": { - "usePrettierrc": true - } - } - ], - "import/order": [ - "warn", - { - "groups": [ - "builtin", - "external", - "internal", - "parent", - "sibling", - "index", - "object" - ], - "newlines-between": "always", - "alphabetize": { - "order": "asc", - "caseInsensitive": true - } - } - ], - "import/named": "error", - "import/default": "error", - "import/export": "error", - "import/no-named-as-default": "warn", - "import/no-duplicates": "error", - "sort-keys-fix/sort-keys-fix": "warn", - "@import/no-named-as-default-member": "off", - "@typescript-eslint/consistent-type-imports": "warn", - "@typescript-eslint/no-non-null-assertion": "off", - "@typescript-eslint/no-explicit-any": "off", - "@typescript-eslint/ban-types": "off", - "@typescript-eslint/ban-ts-comment": "off", - "@typescript-eslint/no-empty-function": "off" - }, - "overrides": [ - { - "files": ["*.js"], - "rules": { - "@typescript-eslint/explicit-module-boundary-types": ["off"], - "@typescript-eslint/no-var-requires": ["off"] - } - } - ] -} diff --git a/src/Managing.WebApp/.gitattributes b/src/Managing.WebApp/.gitattributes deleted file mode 100644 index 2dd6f4d..0000000 --- a/src/Managing.WebApp/.gitattributes +++ /dev/null @@ -1,3 +0,0 @@ -.jest/* linguist-vendored -mocks/* linguist-vendored -mockServiceWorker.js linguist-vendored diff --git a/src/Managing.WebApp/.github/workflows/build.yml b/src/Managing.WebApp/.github/workflows/build.yml deleted file mode 100644 index cf9565c..0000000 --- a/src/Managing.WebApp/.github/workflows/build.yml +++ /dev/null @@ -1,18 +0,0 @@ -name: Build -on: - push: - branches: - - main - pull_request: - branches: - - main -jobs: - build: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - uses: actions/setup-node@v2-beta - with: - node-version: '18.1.0' - - run: yarn install - - run: yarn build diff --git a/src/Managing.WebApp/.github/workflows/lint.yml b/src/Managing.WebApp/.github/workflows/lint.yml deleted file mode 100644 index d84f5b7..0000000 --- a/src/Managing.WebApp/.github/workflows/lint.yml +++ /dev/null @@ -1,18 +0,0 @@ -name: Lint -on: - push: - branches: - - main - pull_request: - branches: - - main -jobs: - lint: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - uses: actions/setup-node@v2-beta - with: - node-version: '18.1.0' - - run: yarn install - - run: yarn lint diff --git a/src/Managing.WebApp/.github/workflows/test.yml b/src/Managing.WebApp/.github/workflows/test.yml deleted file mode 100644 index c409935..0000000 --- a/src/Managing.WebApp/.github/workflows/test.yml +++ /dev/null @@ -1,18 +0,0 @@ -name: Test -on: - push: - branches: - - main - pull_request: - branches: - - main -jobs: - test: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - uses: actions/setup-node@v2-beta - with: - node-version: '18.1.0' - - run: yarn install - - run: yarn test diff --git a/src/Managing.WebApp/.github/workflows/typecheck.yml b/src/Managing.WebApp/.github/workflows/typecheck.yml deleted file mode 100644 index eeb1640..0000000 --- a/src/Managing.WebApp/.github/workflows/typecheck.yml +++ /dev/null @@ -1,18 +0,0 @@ -name: Typecheck -on: - push: - branches: - - main - pull_request: - branches: - - main -jobs: - typecheck: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - uses: actions/setup-node@v2-beta - with: - node-version: '18.1.0' - - run: yarn install - - run: yarn typecheck diff --git a/src/Managing.WebApp/.gitignore b/src/Managing.WebApp/.gitignore deleted file mode 100644 index d451ff1..0000000 --- a/src/Managing.WebApp/.gitignore +++ /dev/null @@ -1,5 +0,0 @@ -node_modules -.DS_Store -dist -dist-ssr -*.local diff --git a/src/Managing.WebApp/.prettierignore b/src/Managing.WebApp/.prettierignore deleted file mode 100644 index 4969764..0000000 --- a/src/Managing.WebApp/.prettierignore +++ /dev/null @@ -1,5 +0,0 @@ -.git -node_modules -.eslintignore -.gitignore -LICENSE diff --git a/src/Managing.WebApp/.prettierrc b/src/Managing.WebApp/.prettierrc deleted file mode 100644 index fd496a8..0000000 --- a/src/Managing.WebApp/.prettierrc +++ /dev/null @@ -1,4 +0,0 @@ -{ - "singleQuote": true, - "semi": false -} diff --git a/src/Managing.WebApp/Dockerfile b/src/Managing.WebApp/Dockerfile deleted file mode 100644 index 9301ac7..0000000 --- a/src/Managing.WebApp/Dockerfile +++ /dev/null @@ -1,39 +0,0 @@ -# Use an official Node.js runtime as a parent image -FROM node:18 - -# Set the working directory inside the container -WORKDIR /app - - -# Install xsel for clipboard access (useful for some applications) -RUN apt-get update && apt-get install -y xsel - -# Copy only package.json and package-lock.json (or yarn.lock) initially -# This takes advantage of cached Docker layers -COPY package*.json ./ - -# Install dependencies -# npm ci is used instead of npm install when you want a clean, exact installation -#RUN npm ci --verbose -# Try to install dependencies with a retry mechanism -RUN for i in 1 2 3; do npm ci --verbose && break || sleep 15; done - -# Copy the rest of your application code -COPY . . - -# Set necessary environment variables (if they are not secrets) -ENV NODE_ENV=development -ENV VITE_API_URL_LOCAL=https://localhost:5001 -ENV VITE_API_URL_SERVER=https://localhost - -# Expose port 3000 for the application -EXPOSE 3000 - -# Install global dependencies if absolutely necessary (generally not recommended to do globally) -RUN npm install -g serve vite - -# Build the application -RUN node --max-old-space-size=4096 node_modules/.bin/vite build - -# Command to run the application -CMD ["npm", "run", "serve"] diff --git a/src/Managing.WebApp/Dockerfile-web-ui-dev b/src/Managing.WebApp/Dockerfile-web-ui-dev deleted file mode 100644 index 791953d..0000000 --- a/src/Managing.WebApp/Dockerfile-web-ui-dev +++ /dev/null @@ -1,43 +0,0 @@ -ο»ΏARG NODE_VERSION=21.4.0 -ARG ALPINE_VERSION=3.19.0 - -FROM node:${NODE_VERSION}-alpine AS node - -FROM alpine:${ALPINE_VERSION} AS builder - -COPY --from=node /usr/lib /usr/lib -COPY --from=node /usr/local/lib /usr/local/lib -COPY --from=node /usr/local/include /usr/local/include -COPY --from=node /usr/local/bin /usr/local/bin - -RUN node -v - -# Set the working directory in the container -WORKDIR /app - -# Copy the package.json and package-lock.json first to leverage Docker's cache -COPY ./src/Managing.WebApp/package*.json ./ - -# Install dependencies -#RUN npm ci --production --loglevel=verbose -RUN npm i --omit=dev --loglevel=verbose -# Copy the application code -COPY . . - -# Build the Vite application -RUN npm run build - -# Stage 2: Create the runtime image -FROM nginx:alpine - -# Copy the built Vite application from the builder stage -COPY --from=builder /app/dist /usr/share/nginx/html - -# Copy a custom Nginx configuration file (if you need one) -# COPY nginx.conf /etc/nginx/nginx.conf - -# Expose port 80 -EXPOSE 80 - -# Start the Nginx server -CMD ["nginx", "-g", "daemon off;"] \ No newline at end of file diff --git a/src/Managing.WebApp/LICENSE b/src/Managing.WebApp/LICENSE deleted file mode 100644 index b229c5f..0000000 --- a/src/Managing.WebApp/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) Managing - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/src/Managing.WebApp/README.md b/src/Managing.WebApp/README.md deleted file mode 100644 index 5be74be..0000000 --- a/src/Managing.WebApp/README.md +++ /dev/null @@ -1,94 +0,0 @@ -# vite-react-ts-extended [![Typecheck](https://github.com/laststance/vite-react-ts-extended/actions/workflows/typecheck.yml/badge.svg)](https://github.com/laststance/vite-react-ts-extended/actions/workflows/typecheck.yml) [![Test](https://github.com/laststance/vite-react-ts-extended/actions/workflows/test.yml/badge.svg)](https://github.com/laststance/vite-react-ts-extended/actions/workflows/test.yml) [![Build](https://github.com/laststance/vite-react-ts-extended/actions/workflows/build.yml/badge.svg)](https://github.com/laststance/vite-react-ts-extended/actions/workflows/build.yml) [![Lint](https://github.com/laststance/vite-react-ts-extended/actions/workflows/lint.yml/badge.svg)](https://github.com/laststance/vite-react-ts-extended/actions/workflows/lint.yml) [![Depfu](https://badges.depfu.com/badges/6c7775918ccc8647160750e168617a65/overview.svg)](https://depfu.com/github/laststance/vite-react-ts-extended?project_id=32682) - -> My CRA alternative. -> Create plain and lightweight React+TS programming environment with familiar pre-setup tooling -> eslint/prettier, jest/TS/react-testing-library/msw, tailwindcss, CI. - -## [Trying this Online!](https://codesandbox.io/s/vite-react-ts-extended-cbgyfz?file=/src/App.tsx) - - - -This is the official [Vite](https://vitejs.dev/) template(`npm init vite@latest myapp -- --template react-ts`) and some extended setup. - -- [eslint-typescript](https://github.com/typescript-eslint/typescript-eslint) and [Prettier](https://prettier.io/) integration. Rules are 100% my personal setup πŸ’… -- [jest](https://jestjs.io/), [React Testing Library](https://testing-library.com/docs/react-testing-library/intro/), [react-hooks-testing-library](https://github.com/testing-library/react-hooks-testing-library), [MSW](https://mswjs.io/) -- [tailwindcss](https://tailwindcss.com/) -- [Github Actions](https://github.com/features/actions) - -All npm package are keeping least release version powered by [Depfu](https://depfu.com/). - -# Installation - -``` -npx degit laststance/vite-react-ts-extended myapp -``` - -### yarn - -```sh -cd myapp -yarn install -yarn validate # The installation was successful if no error occurs after running 'validate'. -yarn dev -``` - -### npm - -```sh -cd myapp -npm install -npm run validate # The installation was successful if no error occurs after running 'validate'. -npm run dev -``` - -### Commands - -```sh -yarn dev # start development server -yarn validate # run test,lint,build,typecheck concurrently -yarn test # run jest -yarn lint # run eslint -yarn lint:fix # run eslint with --fix option -yarn typecheck # run TypeScript compiler check -yarn build # build production bundle to 'dist' directly -yarn prettier # run prettier for json|yml|css|md|mdx files -yarn clean # remove 'node_modules' 'yarn.lock' 'dist' completely -yarn serve # launch server for production bundle in local -``` - -# Background - -The evolution of the React framework is accelerating more than ever before. -[Next.js](https://nextjs.org/), [Remix](https://remix.run/), [RedwoodJS](https://redwoodjs.com/), [Gatsby](https://www.gatsbyjs.com/), [Blitz](https://blitzjs.com/) etc... - -Ahthough I still need plain React programming starter some reason. (.e.g Demo, Experiment like Deep Dive React Core.) -So far, [create-react-app](https://github.com/facebook/create-react-app) **was** it. -In short, [create-react-app](https://github.com/facebook/create-react-app) development couldn't say active. Please read the [Issue](https://github.com/facebook/create-react-app/issues/11180) in details. - -So I created an alternative to [create-react-app](https://github.com/facebook/create-react-app) for myself, based on [Vite](https://github.com/facebook/create-react-app). -This project contains my very opinionted setup, -but I hope it will be a useful tool for people who have similar needs to mine! πŸ˜€ - -# License - -MIT - -## Contributors ✨ - -Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)): - - - - - - - - -

ryota-murakami

πŸ’» πŸ“– ⚠️
- - - - - - -This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome! diff --git a/src/Managing.WebApp/index.html b/src/Managing.WebApp/index.html deleted file mode 100644 index c6488fe..0000000 --- a/src/Managing.WebApp/index.html +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - Managing - - -
- - - - diff --git a/src/Managing.WebApp/install_problematic_packages.sh b/src/Managing.WebApp/install_problematic_packages.sh deleted file mode 100644 index 62a600e..0000000 --- a/src/Managing.WebApp/install_problematic_packages.sh +++ /dev/null @@ -1,32 +0,0 @@ -#!/bin/bash - -# Array of known potentially hanging packages -hanging_packages=("xmlhttprequest-ssl@latest" "engine.io-parser@latest") - -# Timeout in seconds for each package installation attempt - - -install_with_timeout() { - package=$1 - echo "Attempting to install $package with a timeout of $timeout_duration seconds." - # Start npm install in the background - npm install $package --verbose &> $package.log & - - # Get PID of the npm process - pid=$! - - # Wait for the npm process to finish or timeout - (sleep $timeout_duration && kill -0 $pid 2>/dev/null && kill -9 $pid && echo "Timeout reached for $package, process killed." && echo $package >> exclude.log) & - waiter_pid=$! - - # Wait for the npm process to complete - wait $pid - - # Kill the waiter process in case npm finished before the timeout - kill -0 $waiter_pid 2>/dev/null && kill -9 $waiter_pid -} - -# Install potentially hanging packages first with a timeout -for package in "${hanging_packages[@]}"; do - install_with_timeout $package -done diff --git a/src/Managing.WebApp/jest.config.js b/src/Managing.WebApp/jest.config.js deleted file mode 100644 index dd7f4d9..0000000 --- a/src/Managing.WebApp/jest.config.js +++ /dev/null @@ -1,35 +0,0 @@ -const config = { - collectCoverageFrom: ['/src/**/*.{js,jsx,ts,tsx}', '!src/**/*.d.ts'], - moduleDirectories: ['node_modules'], - moduleFileExtensions: ['js', 'mjs', 'jsx', 'ts', 'tsx', 'json'], - moduleNameMapper: { - '^.+\\.module\\.(css|sass|scss)$': 'identity-obj-proxy', - }, - notify: true, - notifyMode: 'success-change', - resetMocks: true, - roots: [''], - setupFilesAfterEnv: ['/jest/setupTests.ts'], - testEnvironment: 'jsdom', - testMatch: [ - '/src/**/*.{spec,test}.{js,jsx,ts,tsx}', - '/src/**/__tests__/**/*.{js,jsx,ts,tsx}', - ], - transform: { - '^(?!.*\\.(js|jsx|mjs|cjs|ts|tsx|css|json)$)': - '/jest/fileTransform.js', - '^.+\\.[jt]sx?$': 'esbuild-jest', - '^.+\\.css$': '/jest/cssTransform.js', - }, - transformIgnorePatterns: [ - '[/\\\\]node_modules[/\\\\].+\\.(js|jsx|mjs|cjs|ts|tsx)$', - '^.+\\.module\\.(css|sass|scss)$', - ], - verbose: true, - watchPlugins: [ - 'jest-watch-typeahead/filename', - 'jest-watch-typeahead/testname', - ], -} - -module.exports = config diff --git a/src/Managing.WebApp/mockServiceWorker.js b/src/Managing.WebApp/mockServiceWorker.js deleted file mode 100644 index 4e18b44..0000000 --- a/src/Managing.WebApp/mockServiceWorker.js +++ /dev/null @@ -1,338 +0,0 @@ -/* eslint-disable */ -/* tslint:disable */ - -/** - * Mock Service Worker (0.36.5). - * @see https://github.com/mswjs/msw - * - Please do NOT modify this file. - * - Please do NOT serve this file on production. - */ - -const INTEGRITY_CHECKSUM = '02f4ad4a2797f85668baf196e553d929' -const bypassHeaderName = 'x-msw-bypass' -const activeClientIds = new Set() - -self.addEventListener('install', function () { - return self.skipWaiting() -}) - -self.addEventListener('activate', async function (event) { - return self.clients.claim() -}) - -self.addEventListener('message', async function (event) { - const clientId = event.source.id - - if (!clientId || !self.clients) { - return - } - - const client = await self.clients.get(clientId) - - if (!client) { - return - } - - const allClients = await self.clients.matchAll() - - switch (event.data) { - case 'KEEPALIVE_REQUEST': { - sendToClient(client, { - type: 'KEEPALIVE_RESPONSE', - }) - break - } - - case 'INTEGRITY_CHECK_REQUEST': { - sendToClient(client, { - type: 'INTEGRITY_CHECK_RESPONSE', - payload: INTEGRITY_CHECKSUM, - }) - break - } - - case 'MOCK_ACTIVATE': { - activeClientIds.add(clientId) - - sendToClient(client, { - type: 'MOCKING_ENABLED', - payload: true, - }) - break - } - - case 'MOCK_DEACTIVATE': { - activeClientIds.delete(clientId) - break - } - - case 'CLIENT_CLOSED': { - activeClientIds.delete(clientId) - - const remainingClients = allClients.filter((client) => { - return client.id !== clientId - }) - - // Unregister itself when there are no more clients - if (remainingClients.length === 0) { - self.registration.unregister() - } - - break - } - } -}) - -// Resolve the "main" client for the given event. -// Client that issues a request doesn't necessarily equal the client -// that registered the worker. It's with the latter the worker should -// communicate with during the response resolving phase. -async function resolveMainClient(event) { - const client = await self.clients.get(event.clientId) - - if (client.frameType === 'top-level') { - return client - } - - const allClients = await self.clients.matchAll() - - return allClients - .filter((client) => { - // Get only those clients that are currently visible. - return client.visibilityState === 'visible' - }) - .find((client) => { - // Find the client ID that's recorded in the - // set of clients that have registered the worker. - return activeClientIds.has(client.id) - }) -} - -async function handleRequest(event, requestId) { - const client = await resolveMainClient(event) - const response = await getResponse(event, client, requestId) - - // Send back the response clone for the "response:*" life-cycle events. - // Ensure MSW is active and ready to handle the message, otherwise - // this message will pend indefinitely. - if (client && activeClientIds.has(client.id)) { - ;(async function () { - const clonedResponse = response.clone() - sendToClient(client, { - type: 'RESPONSE', - payload: { - requestId, - type: clonedResponse.type, - ok: clonedResponse.ok, - status: clonedResponse.status, - statusText: clonedResponse.statusText, - body: - clonedResponse.body === null ? null : await clonedResponse.text(), - headers: serializeHeaders(clonedResponse.headers), - redirected: clonedResponse.redirected, - }, - }) - })() - } - - return response -} - -async function getResponse(event, client, requestId) { - const { request } = event - const requestClone = request.clone() - const getOriginalResponse = () => fetch(requestClone) - - // Bypass mocking when the request client is not active. - if (!client) { - return getOriginalResponse() - } - - // Bypass initial page load requests (i.e. static assets). - // The absence of the immediate/parent client in the map of the active clients - // means that MSW hasn't dispatched the "MOCK_ACTIVATE" event yet - // and is not ready to handle requests. - if (!activeClientIds.has(client.id)) { - return await getOriginalResponse() - } - - // Bypass requests with the explicit bypass header - if (requestClone.headers.get(bypassHeaderName) === 'true') { - const cleanRequestHeaders = serializeHeaders(requestClone.headers) - - // Remove the bypass header to comply with the CORS preflight check. - delete cleanRequestHeaders[bypassHeaderName] - - const originalRequest = new Request(requestClone, { - headers: new Headers(cleanRequestHeaders), - }) - - return fetch(originalRequest) - } - - // Send the request to the client-side MSW. - const reqHeaders = serializeHeaders(request.headers) - const body = await request.text() - - const clientMessage = await sendToClient(client, { - type: 'REQUEST', - payload: { - id: requestId, - url: request.url, - method: request.method, - headers: reqHeaders, - cache: request.cache, - mode: request.mode, - credentials: request.credentials, - destination: request.destination, - integrity: request.integrity, - redirect: request.redirect, - referrer: request.referrer, - referrerPolicy: request.referrerPolicy, - body, - bodyUsed: request.bodyUsed, - keepalive: request.keepalive, - }, - }) - - switch (clientMessage.type) { - case 'MOCK_SUCCESS': { - return delayPromise( - () => respondWithMock(clientMessage), - clientMessage.payload.delay, - ) - } - - case 'MOCK_NOT_FOUND': { - return getOriginalResponse() - } - - case 'NETWORK_ERROR': { - const { name, message } = clientMessage.payload - const networkError = new Error(message) - networkError.name = name - - // Rejecting a request Promise emulates a network error. - throw networkError - } - - case 'INTERNAL_ERROR': { - const parsedBody = JSON.parse(clientMessage.payload.body) - - console.error( - `\ -[MSW] Uncaught exception in the request handler for "%s %s": - -${parsedBody.location} - -This exception has been gracefully handled as a 500 response, however, it's strongly recommended to resolve this error, as it indicates a mistake in your code. If you wish to mock an error response, please see this guide: https://mswjs.io/docs/recipes/mocking-error-responses\ -`, - request.method, - request.url, - ) - - return respondWithMock(clientMessage) - } - } - - return getOriginalResponse() -} - -self.addEventListener('fetch', function (event) { - const { request } = event - const accept = request.headers.get('accept') || '' - - // Bypass server-sent events. - if (accept.includes('text/event-stream')) { - return - } - - // Bypass navigation requests. - if (request.mode === 'navigate') { - return - } - - // Opening the DevTools triggers the "only-if-cached" request - // that cannot be handled by the worker. Bypass such requests. - if (request.cache === 'only-if-cached' && request.mode !== 'same-origin') { - return - } - - // Bypass all requests when there are no active clients. - // Prevents the self-unregistered worked from handling requests - // after it's been deleted (still remains active until the next reload). - if (activeClientIds.size === 0) { - return - } - - const requestId = uuidv4() - - return event.respondWith( - handleRequest(event, requestId).catch((error) => { - if (error.name === 'NetworkError') { - console.warn( - '[MSW] Successfully emulated a network error for the "%s %s" request.', - request.method, - request.url, - ) - return - } - - // At this point, any exception indicates an issue with the original request/response. - console.error( - `\ -[MSW] Caught an exception from the "%s %s" request (%s). This is probably not a problem with Mock Service Worker. There is likely an additional logging output above.`, - request.method, - request.url, - `${error.name}: ${error.message}`, - ) - }), - ) -}) - -function serializeHeaders(headers) { - const reqHeaders = {} - headers.forEach((value, name) => { - reqHeaders[name] = reqHeaders[name] - ? [].concat(reqHeaders[name]).concat(value) - : value - }) - return reqHeaders -} - -function sendToClient(client, message) { - return new Promise((resolve, reject) => { - const channel = new MessageChannel() - - channel.port1.onmessage = (event) => { - if (event.data && event.data.error) { - return reject(event.data.error) - } - - resolve(event.data) - } - - client.postMessage(JSON.stringify(message), [channel.port2]) - }) -} - -function delayPromise(cb, duration) { - return new Promise((resolve) => { - setTimeout(() => resolve(cb()), duration) - }) -} - -function respondWithMock(clientMessage) { - return new Response(clientMessage.payload.body, { - ...clientMessage.payload, - headers: clientMessage.payload.headers, - }) -} - -function uuidv4() { - return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) { - const r = (Math.random() * 16) | 0 - const v = c == 'x' ? r : (r & 0x3) | 0x8 - return v.toString(16) - }) -} diff --git a/src/Managing.WebApp/package.json b/src/Managing.WebApp/package.json deleted file mode 100644 index e808132..0000000 --- a/src/Managing.WebApp/package.json +++ /dev/null @@ -1,104 +0,0 @@ -{ - "name": "managing", - "version": "2.0.0", - "license": "MIT", - "scripts": { - "dev": "vite", - "build": "vite build", - "preview": "vite preview", - "serve": "serve -s dist -p 3000", - "test": "jest", - "lint": "eslint . --ext .ts,.tsx,.js,jsx", - "lint:fix": "eslint . --ext .ts,.tsx,.js,jsx --fix", - "typecheck": "tsc --noEmit", - "prettier": "prettier --write \"**/*.+(json|yml|css|md|mdx)\"", - "clean": "rimraf node_modules yarn.lock dist", - "validate": "./scripts/validate" - }, - "dependencies": { - "@heroicons/react": "^1.0.6", - "@microsoft/signalr": "^6.0.5", - "@tanstack/react-query": "^5.0.0", - "@wagmi/chains": "^0.2.9", - "@wagmi/connectors": "^4.3.2", - "@wagmi/core": "^2.9.0", - "@walletconnect/universal-provider": "^2.8.6", - "axios": "^0.27.2", - "classnames": "^2.3.1", - "connectkit": "^1.7.3", - "date-fns": "^2.30.0", - "jotai": "^1.6.7", - "latest-version": "^9.0.0", - "lightweight-charts": "git+https://github.com/ntf/lightweight-charts.git", - "moment": "^2.29.3", - "plotly.js": "^2.18.1", - "react": "^18.1.0", - "react-cookie": "^4.1.1", - "react-dom": "^18.1.0", - "react-grid-layout": "^1.3.4", - "react-hook-form": "^7.31.2", - "react-hot-toast": "^2.2.0", - "react-icons": "^4.3.1", - "react-iframe": "^1.8.0", - "react-plotly.js": "^2.6.0", - "react-router-dom": "^6.3.0", - "react-slider": "^2.0.1", - "react-table": "^7.8.0", - "react-toastify": "^9.0.1", - "reactflow": "^11.8.3", - "viem": "^2.0.6", - "wagmi": "^2.2.1", - "web3": "^4.0.2", - "zustand": "^4.4.1" - }, - "devDependencies": { - "@tailwindcss/aspect-ratio": "^0.4.0", - "@tailwindcss/forms": "^0.5.1", - "@tailwindcss/line-clamp": "^0.4.0", - "@tailwindcss/typography": "^0.5.2", - "@tanstack/eslint-plugin-query": "^4.34.1", - "@testing-library/dom": "^8.13.0", - "@testing-library/react": "^13.2.0", - "@types/jest": "^27.5.1", - "@types/react": "^18.0.9", - "@types/react-dom": "^18.0.4", - "@types/react-grid-layout": "^1.3.2", - "@types/react-plotly.js": "^2.6.0", - "@types/react-slider": "^1.3.1", - "@types/react-table": "^7.7.12", - "@types/signalr": "^2.2.37", - "@types/testing-library__jest-dom": "^5.14.3", - "@typescript-eslint/eslint-plugin": "^5.23.0", - "@typescript-eslint/parser": "^5.23.0", - "@vitejs/plugin-react": "^1.3.2", - "all-contributors-cli": "^6.20.0", - "autoprefixer": "^10.4.7", - "daisyui": "^3.5.1", - "esbuild-jest": "^0.4.0", - "eslint": "^8.15.0", - "eslint-config-prettier": "^8.5.0", - "eslint-config-typescript": "^3.0.0", - "eslint-import-resolver-typescript": "^2.7.1", - "eslint-plugin-import": "^2.26.0", - "eslint-plugin-jsx-a11y": "^6.5.1", - "eslint-plugin-prettier": "^4.0.0", - "eslint-plugin-react-hooks": "^4.5.0", - "eslint-plugin-sort-keys-fix": "^1.1.2", - "identity-obj-proxy": "^3.0.0", - "jest": "^28.0.0", - "jest-watch-typeahead": "^1.1.0", - "node-notifier": "^10.0.1", - "postcss": "^8.4.13", - "prettier": "^2.6.1", - "prettier-plugin-tailwind-css": "^1.5.0", - "rimraf": "^3.0.2", - "serve": "^14.2.0", - "tailwindcss": "^3.0.23", - "typescript": "^5.0.4", - "vite": "^4.4.9", - "whatwg-fetch": "^3.6.2" - }, - "msw": { - "workerDirectory": "" - } -} diff --git a/src/Managing.WebApp/postcss.config.js b/src/Managing.WebApp/postcss.config.js deleted file mode 100644 index 6e41d95..0000000 --- a/src/Managing.WebApp/postcss.config.js +++ /dev/null @@ -1,6 +0,0 @@ -module.exports = { - plugins: { - autoprefixer: {}, - tailwindcss: {}, - }, -} diff --git a/src/Managing.WebApp/prettier.config.js b/src/Managing.WebApp/prettier.config.js deleted file mode 100644 index 9280030..0000000 --- a/src/Managing.WebApp/prettier.config.js +++ /dev/null @@ -1,4 +0,0 @@ -module.exports = { - plugins: [require('prettier-plugin-tailwindcss')], - tailwindConfig: './tailwind.config.js', -} diff --git a/src/Managing.WebApp/scripts/validate b/src/Managing.WebApp/scripts/validate deleted file mode 100644 index 738fceb..0000000 --- a/src/Managing.WebApp/scripts/validate +++ /dev/null @@ -1,11 +0,0 @@ -#!/bin/sh - -npx concurrently \ - --kill-others-on-fail \ - --prefix "[{name}]" \ - --names "test,lint:fix,typecheck,build" \ - --prefix-colors "bgRed.bold.white,bgGreen.bold.white,bgBlue.bold.white,bgMagenta.bold.white" \ - "npm run test --silent -- --watch=false" \ - "npm run lint:fix --silent" \ - "npm run typecheck --silent" \ - "npm run build --silent" diff --git a/src/Managing.WebApp/src/app/index.tsx b/src/Managing.WebApp/src/app/index.tsx deleted file mode 100644 index 96e64a1..0000000 --- a/src/Managing.WebApp/src/app/index.tsx +++ /dev/null @@ -1,13 +0,0 @@ -import { Auth } from '../pages/authPage/auth' - -import MyRoutes from './routes' - -const App = () => { - return ( - - - - ) -} - -export default App diff --git a/src/Managing.WebApp/src/app/providers/Hubs.tsx b/src/Managing.WebApp/src/app/providers/Hubs.tsx deleted file mode 100644 index f837f26..0000000 --- a/src/Managing.WebApp/src/app/providers/Hubs.tsx +++ /dev/null @@ -1,18 +0,0 @@ -import type { HubConnection } from '@microsoft/signalr' -import { HubConnectionBuilder } from '@microsoft/signalr' -// https://www.abrahamberg.com/blog/aspnet-signalr-and-react/ -export class Hub { - public hub: HubConnection - constructor(name: string, baseUrl: string) { - this.hub = new HubConnectionBuilder() - .withUrl(baseUrl + '/' + name) - .withAutomaticReconnect() - .build() - try { - this.hub.start() - } catch (err) { - // eslint-disable-next-line no-console - console.log(err) - } - } -} diff --git a/src/Managing.WebApp/src/app/routes/index.tsx b/src/Managing.WebApp/src/app/routes/index.tsx deleted file mode 100644 index da67c32..0000000 --- a/src/Managing.WebApp/src/app/routes/index.tsx +++ /dev/null @@ -1,118 +0,0 @@ -import { Suspense, lazy } from 'react' -import { Route, Routes } from 'react-router-dom' - -import LayoutMain from '../../layouts' -import DeskWidget from '../../pages/desk/deskWidget' -import Scenario from '../../pages/scenarioPage/scenario' -import Tools from '../../pages/toolsPage/tools' -import Workflows from '../../pages/workflow/workflows' - -const Backtest = lazy(() => import('../../pages/backtestPage/backtest')) -const Bots = lazy(() => import('../../pages/botsPage/bots')) -const Dashboard = lazy(() => import('../../pages/dashboardPage/dashboard')) -const Settings = lazy(() => import('../../pages/settingsPage/settings')) - -const MyRoutes = () => { - return ( - - }> - - - - } - /> - - - }> - - - - } - /> - - - }> - - - - } - /> - - - }> - - - - } - /> - - - }> - - - - } - /> - - - }> - - - - } - /> - - }> - - - - } - /> - - - }> - - - - } - /> - - {/* }> - - - - } - /> - */} - - ) -} - -export default MyRoutes diff --git a/src/Managing.WebApp/src/app/store/accountStore.tsx b/src/Managing.WebApp/src/app/store/accountStore.tsx deleted file mode 100644 index b264119..0000000 --- a/src/Managing.WebApp/src/app/store/accountStore.tsx +++ /dev/null @@ -1,22 +0,0 @@ -import { create } from 'zustand' - -import type { AccountStore } from '../../global/type' - -export const useAuthStore = create((set) => ({ - accounts: [], - onInitialize: () => { - console.log('useFlowStore onInitialize') - - // const accountClient = new AccountClient({}, apiUrl) - // const accounts = await accountClient.account_GetAccounts() - // if (accounts.length > 0) { - // get().setAccounts(accounts) - // } - }, - setAccounts: (accounts) => { - set((state) => ({ - ...state, - accounts: accounts, - })) - }, -})) diff --git a/src/Managing.WebApp/src/app/store/apiStore.tsx b/src/Managing.WebApp/src/app/store/apiStore.tsx deleted file mode 100644 index 632fb70..0000000 --- a/src/Managing.WebApp/src/app/store/apiStore.tsx +++ /dev/null @@ -1,28 +0,0 @@ -import create from 'zustand' - -type ApiStore = { - isProd: boolean - apiUrl: string - workerUrl: string - toggleApiUrl: () => void -} - -const useApiUrlStore = create((set) => ({ - // Mettez la valeur initiale de isProd ici - apiUrl: import.meta.env.VITE_API_URL_SERVER, - isProd: true, - toggleApiUrl: () => { - set((state) => ({ - apiUrl: state.isProd - ? import.meta.env.VITE_API_URL_LOCAL - : import.meta.env.VITE_API_URL_SERVER, - isProd: !state.isProd, - workerUrl: state.isProd - ? import.meta.env.VITE_WORKER_URL_LOCAL - : import.meta.env.VITE_WORKER_URL_SERVER, - })) - }, - workerUrl: import.meta.env.VITE_WORKER_URL_SERVER, -})) - -export default useApiUrlStore diff --git a/src/Managing.WebApp/src/app/store/flowStore.tsx b/src/Managing.WebApp/src/app/store/flowStore.tsx deleted file mode 100644 index f176552..0000000 --- a/src/Managing.WebApp/src/app/store/flowStore.tsx +++ /dev/null @@ -1,28 +0,0 @@ -import { create } from 'zustand' - -import type { IFlow } from '../../generated/ManagingApi' -import { WorkflowClient } from '../../generated/ManagingApi' - -type FlowStore = { - setFlows: (flows: IFlow[]) => void - getFlows: (apiUrl: string) => void - flows: IFlow[] -} - -export const useFlowStore = create((set) => ({ - flows: [] as IFlow[], - getFlows: async (apiUrl) => { - const client = new WorkflowClient({}, apiUrl) - await client.workflow_GetAvailableFlows().then((data) => { - set(() => ({ - flows: data, - })) - }) - }, - setFlows: (flows) => { - set((state) => ({ - ...state, - flows: flows, - })) - }, -})) diff --git a/src/Managing.WebApp/src/app/store/selectors/workflowSelector.tsx b/src/Managing.WebApp/src/app/store/selectors/workflowSelector.tsx deleted file mode 100644 index 5f8f754..0000000 --- a/src/Managing.WebApp/src/app/store/selectors/workflowSelector.tsx +++ /dev/null @@ -1,13 +0,0 @@ -import type { IWorkflowStore } from '../workflowStore' - -export const WorkflowSelector = (state: IWorkflowStore) => ({ - edges: state.edges, - initWorkFlow: state.initWorkFlow, - nodes: state.nodes, - onConnect: state.onConnect, - onEdgesChange: state.onEdgesChange, - onNodesChange: state.onNodesChange, - resetWorkflow: state.resetWorkflow, - setNodes: state.setNodes, - updateNodeData: state.updateNodeData, -}) diff --git a/src/Managing.WebApp/src/app/store/workflowStore.tsx b/src/Managing.WebApp/src/app/store/workflowStore.tsx deleted file mode 100644 index 834b72a..0000000 --- a/src/Managing.WebApp/src/app/store/workflowStore.tsx +++ /dev/null @@ -1,113 +0,0 @@ -import type { - Connection, - Edge, - EdgeChange, - Node, - NodeChange, - OnNodesChange, - OnEdgesChange, - OnConnect, -} from 'reactflow' -import { addEdge, applyNodeChanges, applyEdgeChanges } from 'reactflow' -import { create } from 'zustand' - -import type { - SyntheticFlowParameter, - FlowParameter, - IFlow, -} from '../../generated/ManagingApi' - -export type IWorkflowStore = { - nodes: Node[] - initialNodes: Node[] - edges: Edge[] - initialEdges: Edge[] - onNodesChange: OnNodesChange - onEdgesChange: OnEdgesChange - onConnect: OnConnect - updateNodeData: (nodeId: string, parameterName: string, value: string) => void - initWorkFlow: (nodes: Node[], edges: Edge[]) => void - setNodes: (nodes: Node[]) => void - resetWorkflow: () => void -} - -// this is our useStore hook that we can use in our components to get parts of the store and call actions -const useWorkflowStore = create((set, get) => ({ - edges: [], - initWorkFlow: (nodes: Node[], edges: Edge[]) => { - set({ - edges: edges, - initialEdges: edges, - initialNodes: nodes, - nodes: nodes, - }) - }, - initialEdges: [], - initialNodes: [], - nodes: [], - onConnect: (connection: Connection, callback: void) => { - set({ - edges: addEdge(connection, get().edges), - }) - }, - onEdgesChange: (changes: EdgeChange[]) => { - set({ - edges: applyEdgeChanges(changes, get().edges), - }) - }, - onNodesChange: (changes: NodeChange[]) => { - set({ - nodes: applyNodeChanges(changes, get().nodes), - }) - }, - resetWorkflow: () => { - set({ - edges: get().initialEdges, - initialEdges: get().initialEdges, - initialNodes: get().initialNodes, - nodes: get().initialNodes, - }) - }, - setNodes: (nodes: Node[]) => { - set({ - nodes: nodes, - }) - }, - updateNodeData: (nodeId: string, parameterName: string, value: string) => { - set({ - nodes: get().nodes.map((node) => { - if (node.id === nodeId) { - node.data.parameters = updateParameters( - node.data.parameters, - parameterName, - value - ) - } - return node - }), - }) - }, -})) - -const updateParameters = ( - parameters: FlowParameter[], - name: string, - value: string -) => { - if (!parameters.find((parameter) => parameter.name === name)) { - parameters.push({ - name: name, - value: value, - } as SyntheticFlowParameter) - } else { - parameters = parameters.map((parameter) => { - if (parameter.name === name) { - parameter.value = value - } - return parameter - }) - } - return parameters -} - -export default useWorkflowStore diff --git a/src/Managing.WebApp/src/assets/img/logo.png b/src/Managing.WebApp/src/assets/img/logo.png deleted file mode 100644 index c4ccd58aeb4f358143206b57d4390129e29da723..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 33041 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 ( -

  • {prop.children}
  • -) - -export default List diff --git a/src/Managing.WebApp/src/components/atoms/Loader/Loader.tsx b/src/Managing.WebApp/src/components/atoms/Loader/Loader.tsx deleted file mode 100644 index 3ddfea1..0000000 --- a/src/Managing.WebApp/src/components/atoms/Loader/Loader.tsx +++ /dev/null @@ -1,16 +0,0 @@ -import type { ILoader } from '../../../global/type' - -const defaultClasses = 'loading loading-ring ' - -const loaderSize = { - lg: defaultClasses + 'loading-lg', - md: defaultClasses + 'loading-md', - sm: defaultClasses + 'loading-sm', - xs: defaultClasses + 'loading-xs', -} - -const Loader = ({ size = 'md' }: ILoader) => { - return -} - -export default Loader diff --git a/src/Managing.WebApp/src/components/atoms/MyLink/MyLink.tsx b/src/Managing.WebApp/src/components/atoms/MyLink/MyLink.tsx deleted file mode 100644 index c979720..0000000 --- a/src/Managing.WebApp/src/components/atoms/MyLink/MyLink.tsx +++ /dev/null @@ -1,42 +0,0 @@ -import { Link, NavLink } from 'react-router-dom' - -import type { IMyLinkProps } from '../../../global/type' - -const MyLink = ({ - children, - href, - as: asof = 'link', - ...props -}: IMyLinkProps): JSX.Element => { - const navLink = asof === 'navlink' - const onlyLink = asof === 'link' - - if (navLink) { - return ( - - {children} - - ) - } - - if (onlyLink) { - return ( - - {children} - - ) - } - - return ( - - {children} - - ) -} - -export default MyLink diff --git a/src/Managing.WebApp/src/components/atoms/Select/Select.tsx b/src/Managing.WebApp/src/components/atoms/Select/Select.tsx deleted file mode 100644 index f31dcf8..0000000 --- a/src/Managing.WebApp/src/components/atoms/Select/Select.tsx +++ /dev/null @@ -1,7 +0,0 @@ -import type { IListProp } from '../../../global/type' - -const Select = (prop: IListProp): JSX.Element => ( - -) - -export default Select diff --git a/src/Managing.WebApp/src/components/atoms/Slider/Slider.tsx b/src/Managing.WebApp/src/components/atoms/Slider/Slider.tsx deleted file mode 100644 index a19e1e8..0000000 --- a/src/Managing.WebApp/src/components/atoms/Slider/Slider.tsx +++ /dev/null @@ -1,30 +0,0 @@ -import type { FunctionComponent } from 'react' - -import type { IPropsComponent } from '../../../global/type' - -const Slider: FunctionComponent = (props: IPropsComponent) => { - return ( - <> -
    - -
    -
    - {props.prefixValue} - {props.value} - {props.suffixValue} -
    - - ) -} - -export default Slider diff --git a/src/Managing.WebApp/src/components/atoms/index.tsx b/src/Managing.WebApp/src/components/atoms/index.tsx deleted file mode 100644 index 77ced26..0000000 --- a/src/Managing.WebApp/src/components/atoms/index.tsx +++ /dev/null @@ -1,5 +0,0 @@ -export { default as List } from './List/List' -export { default as MyLink } from './MyLink/MyLink' -export { default as Select } from './Select/Select' -export { default as Slider } from './Slider/Slider' -export { default as Loader } from './Loader/Loader' diff --git a/src/Managing.WebApp/src/components/mollecules/Card/Card.tsx b/src/Managing.WebApp/src/components/mollecules/Card/Card.tsx deleted file mode 100644 index 7c5c677..0000000 --- a/src/Managing.WebApp/src/components/mollecules/Card/Card.tsx +++ /dev/null @@ -1,45 +0,0 @@ -import type { CardProps } from '../../../global/type' - -const Card = ({ name, children, showCloseButton, info }: CardProps) => { - return ( -
    -
    -
    - {name} - {info && ( -
    - i -
    - )} -
    - -
    - {showCloseButton && ( - - )} -
    -
    -
    {children}
    -
    - ) -} - -export default Card diff --git a/src/Managing.WebApp/src/components/mollecules/CardText/CardText.tsx b/src/Managing.WebApp/src/components/mollecules/CardText/CardText.tsx deleted file mode 100644 index 64f51a3..0000000 --- a/src/Managing.WebApp/src/components/mollecules/CardText/CardText.tsx +++ /dev/null @@ -1,77 +0,0 @@ -import ArrowDownIcon from '@heroicons/react/solid/ArrowDownIcon' -import ArrowUpIcon from '@heroicons/react/solid/ArrowUpIcon' - -import { Position, TradeDirection } from '../../../generated/ManagingApi' -import type { ICardPosition, ICardSignal, ICardText } from '../../../global/type' - -function getItemTextHeaderClass() { - return 'text-xs ' -} -function getItemTextValueClass() { - return 'text-md ' -} - -export function CardText({ title, content }: ICardText) { - return ( -
    -

    {title}

    -

    {content}

    -
    - ) -} - -export function CardPosition({ positions, positivePosition }: ICardPosition) { - return ( - <> -
    -

    - {positivePosition ? 'Winning position' : 'Losing position'} -

    -

    - { - positions.filter((p: Position) => p.originDirection == TradeDirection.Long) - .length - }{' '} - {' '} - { - positions.filter((p) => p.originDirection == TradeDirection.Short) - .length - }{' '} - -

    -
    - - ) -} - -export function CardSignal({ signals }: ICardSignal) { - return ( - <> -
    -

    Signals

    -

    - {signals.filter((p) => p.direction == TradeDirection.Long).length}{' '} - {' '} - {signals.filter((p) => p.direction == TradeDirection.Short).length}{' '} - -

    -
    - - ) -} diff --git a/src/Managing.WebApp/src/components/mollecules/FormInput/FormInput.tsx b/src/Managing.WebApp/src/components/mollecules/FormInput/FormInput.tsx deleted file mode 100644 index beb18c1..0000000 --- a/src/Managing.WebApp/src/components/mollecules/FormInput/FormInput.tsx +++ /dev/null @@ -1,24 +0,0 @@ -import React from 'react' - -import type { IFormInput } from '../../../global/type' - -const FormInput: React.FC = ({ - children, - label, - htmlFor, - inline = false, -}) => { - const groupStyle = inline ? 'flex-wrap' : '' - return ( -
    -
    - - {children} -
    -
    - ) -} - -export default FormInput diff --git a/src/Managing.WebApp/src/components/mollecules/GridTile/GridTile.tsx b/src/Managing.WebApp/src/components/mollecules/GridTile/GridTile.tsx deleted file mode 100644 index a59d89f..0000000 --- a/src/Managing.WebApp/src/components/mollecules/GridTile/GridTile.tsx +++ /dev/null @@ -1,38 +0,0 @@ -import type { IGridTile } from '../../../global/type' - -const GridTile = ({ children, title }: IGridTile) => { - return ( -
    -
    -
    - {title} -
    - -
    - -
    -
    -
    {children}
    -
    - ) -} - -export default GridTile diff --git a/src/Managing.WebApp/src/components/mollecules/LogIn/LogIn.tsx b/src/Managing.WebApp/src/components/mollecules/LogIn/LogIn.tsx deleted file mode 100644 index b80ff7a..0000000 --- a/src/Managing.WebApp/src/components/mollecules/LogIn/LogIn.tsx +++ /dev/null @@ -1,101 +0,0 @@ -import { StatusOfflineIcon } from '@heroicons/react/solid' -import type { SubmitHandler } from 'react-hook-form' -import { useForm } from 'react-hook-form' -import { useAccount, useDisconnect, useSignMessage } from 'wagmi' - -import useApiUrlStore from '../../../app/store/apiStore' -import { UserClient } from '../../../generated/ManagingApi' -import type { ILoginFormInput } from '../../../global/type' -import useCookie from '../../../hooks/useCookie' -import { SecondaryNavbar } from '../NavBar/NavBar' - -const LogIn = () => { - const { apiUrl } = useApiUrlStore() - const { register, handleSubmit } = useForm() - const { disconnect } = useDisconnect() - const { address } = useAccount() - const { isLoading, signMessageAsync } = useSignMessage({}) - const { setCookie } = useCookie() - - const onSubmit: SubmitHandler = async (form) => { - const message = 'wagmi' - const signature = await signMessageAsync({ message }) - - if (signature && address) { - const userClient = new UserClient({}, apiUrl) - await userClient - .user_CreateToken({ - address: address.toString(), - message: message, - name: form.name, - signature: signature, - }) - .then((data) => { - setCookie('token', data, 1) - location.reload() - }) - .catch((err) => { - // eslint-disable-next-line no-console - console.error(err) - }) - } else { - } - } - - return ( - <> -
    - -
    - -
    -
    -
    -
    -

    - Login -

    -
    -
    - - -
    - - -
    -
    -
    -
    -
    - - ) -} - -export default LogIn diff --git a/src/Managing.WebApp/src/components/mollecules/LogIn/Profile.tsx b/src/Managing.WebApp/src/components/mollecules/LogIn/Profile.tsx deleted file mode 100644 index 6292525..0000000 --- a/src/Managing.WebApp/src/components/mollecules/LogIn/Profile.tsx +++ /dev/null @@ -1,26 +0,0 @@ -import { useConnect } from 'wagmi' - -// export function Profile() { -// const { connect, connectors, error, isLoading, pendingConnector } = -// useConnect() - -// return ( -//
    -// {connectors.map((connector) => ( -// -// ))} - -// {error &&
    {error.message}
    } -//
    -// ) -// } diff --git a/src/Managing.WebApp/src/components/mollecules/Modal/Modal.tsx b/src/Managing.WebApp/src/components/mollecules/Modal/Modal.tsx deleted file mode 100644 index 752bc3b..0000000 --- a/src/Managing.WebApp/src/components/mollecules/Modal/Modal.tsx +++ /dev/null @@ -1,34 +0,0 @@ -import React from 'react' - -import type { IModalProps } from '../../../global/type' - -import ModalHeader from './ModalHeader' - -const Modal: React.FC = ({ - showModal, - onSubmit, - onClose, - titleHeader, - children, -}) => { - return ( -
    - {showModal ? ( -
    -
    -
    - - {children} -
    -
    -
    - ) : null} -
    - ) -} - -export default Modal diff --git a/src/Managing.WebApp/src/components/mollecules/Modal/ModalHeader.tsx b/src/Managing.WebApp/src/components/mollecules/Modal/ModalHeader.tsx deleted file mode 100644 index 688ff90..0000000 --- a/src/Managing.WebApp/src/components/mollecules/Modal/ModalHeader.tsx +++ /dev/null @@ -1,19 +0,0 @@ -import React from 'react' - -import type { IModalProps } from '../../../global/type' - -const ModalHeader: React.FC = ({ onClose, titleHeader }: any) => { - return ( -
    - -
    {titleHeader}
    -
    - ) -} - -export default ModalHeader diff --git a/src/Managing.WebApp/src/components/mollecules/NavBar/NavBar.tsx b/src/Managing.WebApp/src/components/mollecules/NavBar/NavBar.tsx deleted file mode 100644 index fd8bc49..0000000 --- a/src/Managing.WebApp/src/components/mollecules/NavBar/NavBar.tsx +++ /dev/null @@ -1,130 +0,0 @@ -import { useIsFetching } from '@tanstack/react-query' -import { ConnectKitButton } from 'connectkit' -import type { ReactNode } from 'react' -import { useState } from 'react' -import { Link } from 'react-router-dom' - -import { NavItem } from '..' -import useApiUrlStore from '../../../app/store/apiStore' -import Logo from '../../../assets/img/logo.png' -import { Loader } from '../../atoms' - -const navigation = [ - { href: '/desk', name: 'Desk' }, - { href: '/bots', name: 'Bots' }, - { href: '/workflow', name: 'Workflows' }, - { href: '/scenarios', name: 'Scenarios' }, - { href: '/backtest', name: 'Backtest' }, - { href: '/tools', name: 'Tools' }, - { href: '/settings', name: 'Settings' }, -] - -function navItems(isMobile = false) { - return navigation.map((item) => ( - - {item.name} - - )) -} - -function PrimaryNavbar() { - return ( -
    - - logo - - {/* */} -
    {navItems()}
    -
    - ) -} - -const GlobalLoader = () => { - const isFetching = useIsFetching() - return isFetching ? : null -} - -export function SecondaryNavbar() { - const { toggleApiUrl, isProd } = useApiUrlStore() - - return ( -
    - -
    - -
    - -
    - ) -} - -type MobileMenuButtonProps = { - onClick: VoidFunction -} - -function MobileMenuButton({ onClick }: MobileMenuButtonProps) { - return ( -
    - -
    - ) -} -type MobileMenuProps = { - isOpen: boolean -} - -function MobileMenu({ isOpen }: MobileMenuProps) { - return ( -
    -
      {navItems(true)}
    -
    - ) -} -type NavContainerProps = { - children: ReactNode - isMenuOpen: boolean -} - -function NavContainer({ children, isMenuOpen }: NavContainerProps) { - return ( - - ) -} -export default function NavBar() { - const [isMenuOpen, setIsMenuOpen] = useState(false) - - return ( - - - - setIsMenuOpen(!isMenuOpen)} /> - - ) -} diff --git a/src/Managing.WebApp/src/components/mollecules/NavItem/NavItem.tsx b/src/Managing.WebApp/src/components/mollecules/NavItem/NavItem.tsx deleted file mode 100644 index a492973..0000000 --- a/src/Managing.WebApp/src/components/mollecules/NavItem/NavItem.tsx +++ /dev/null @@ -1,33 +0,0 @@ -import { NavLink } from 'react-router-dom' - -import type { INavItemProps } from '../../../global/interface' - -function navLinkClasses(isActive: boolean, isMobile: boolean) { - let commonClasses = 'block text-sm px-2 py-4' - if (isMobile) { - return `${commonClasses} ${ - isActive - ? 'text-base-content bg-primary font-semibold' - : 'hover:bg-primary transition duration-300' - }` - } - commonClasses = - 'py-4 px-2 font-semibold hover:text-primary transition duration-300' - return `${commonClasses} ${isActive ? 'text-primary' : 'text-base-content'}` -} - -export default function NavItem({ - children, - href, - isMobile = false, -}: INavItemProps) { - const item = ( - navLinkClasses(isActive, isMobile)} - > - {children} - - ) - return isMobile ?
  • {item}
  • : item -} diff --git a/src/Managing.WebApp/src/components/mollecules/PieChart/PieChart.tsx b/src/Managing.WebApp/src/components/mollecules/PieChart/PieChart.tsx deleted file mode 100644 index 51c6c3b..0000000 --- a/src/Managing.WebApp/src/components/mollecules/PieChart/PieChart.tsx +++ /dev/null @@ -1,46 +0,0 @@ -import * as React from 'react' -import Plot from 'react-plotly.js' - -type IPieChart = { - data: number[] - labels: string[] - colors: string[] -} - -const PieChart: React.FC = ({ data, labels, colors }) => { - return ( - <> - - - ) -} - -export default PieChart diff --git a/src/Managing.WebApp/src/components/mollecules/Table/SelectColumnFilter.tsx b/src/Managing.WebApp/src/components/mollecules/Table/SelectColumnFilter.tsx deleted file mode 100644 index f011b60..0000000 --- a/src/Managing.WebApp/src/components/mollecules/Table/SelectColumnFilter.tsx +++ /dev/null @@ -1,36 +0,0 @@ -// This is a custom filter UI for selecting - -import React from 'react' - -// a unique option from a list -export default function SelectColumnFilter({ - column: { filterValue, setFilter, preFilteredRows, id }, -}: any) { - // Calculate the options for filtering - // using the preFilteredRows - const options = React.useMemo(() => { - const options = new Set() - preFilteredRows.forEach((row: any) => { - options.add(row.values[id]) - }) - return [...options.values()] - }, [id, preFilteredRows]) - - // Render a multi-select box - return ( - - ) -} diff --git a/src/Managing.WebApp/src/components/mollecules/Table/Table.tsx b/src/Managing.WebApp/src/components/mollecules/Table/Table.tsx deleted file mode 100644 index c562ad9..0000000 --- a/src/Managing.WebApp/src/components/mollecules/Table/Table.tsx +++ /dev/null @@ -1,231 +0,0 @@ -import { ArrowDownIcon, ArrowUpIcon } from '@heroicons/react/solid' -import React from 'react' -import { - useTable, - usePagination, - useSortBy, - useFilters, - useExpanded, -} from 'react-table' - -import type { TableInstanceWithHooks } from '../../../global/type' - -// Define a default UI for filtering -function DefaultColumnFilter({ - column: { filterValue, preFilteredRows, setFilter }, -}: any) { - const count = preFilteredRows.length - - return ( - { - setFilter(e.target.value || undefined) // Set undefined to remove the filter entirely - }} - placeholder={`Search ${count} records...`} - /> - ) -} - -export default function Table({ - columns, - data, - renderRowSubCompontent, - showPagination, - hiddenColumns, - showTotal = false, -}: any) { - const defaultColumn = React.useMemo( - () => ({ - // Let's set up our default Filter UI - Filter: DefaultColumnFilter, - }), - [] - ) - // Use the state and functions returned from useTable to build your UI - const { - getTableProps, - getTableBodyProps, - headerGroups, - prepareRow, - visibleColumns, - page, // Instead of using 'rows', we'll use page, - // which has only the rows for the active page - - // The rest of these things are super handy, too ;) - canPreviousPage, - canNextPage, - pageOptions, - pageCount, - gotoPage, - nextPage, - previousPage, - setPageSize, - state: { pageIndex, pageSize }, - } = useTable( - { - columns, - data, - defaultColumn, // Be sure to pass the defaultColumn option - initialState: { - hiddenColumns: hiddenColumns ? hiddenColumns : [], - }, - }, - useFilters, - useSortBy, - useExpanded, - usePagination - ) as TableInstanceWithHooks - - // Calculez le total des valeurs dans la colonne USD - const total = data - ? data - .reduce((sum: number, row: any) => { - return sum + (row.value || 0) // Si la valeur est undefined = 0 - }, 0) - .toFixed(2) + ' $' - : '0.00 $' - - // Render the UI for your table - return ( - <> -
    - - - {headerGroups.map((headerGroup: any) => ( - - {headerGroup.headers.map((column: any) => ( - - ))} - - ))} - - - {page.map((row: any) => { - prepareRow(row) - return ( - <> - - {row.cells.map((cell: any) => { - return ( - - ) - })} - - {row.isExpanded ? ( - - - - ) : null} - - ) - })} - - {/* Afficher le total ici */} - {showTotal ? ( - - - - ) : null} -
    -

    - {column.render('Header')} -

    - - {column.isSorted ? ( - column.isSortedDesc ? ( - - ) : ( - - ) - ) : ( - '' - )} - -
    - {column.canFilter ? column.render('Filter') : null} -
    -
    {cell.render('Cell')}
    - {/* - Inside it, call our renderRowSubComponent function. In reality, - you could pass whatever you want as props to - a component like this, including the entire - table instance. But for this example, we'll just - pass the row - */} - {renderRowSubCompontent({ row })} -
    - Total: {total} -
    -
    - {/* - Pagination can be built however you'd like. - This is just a very basic UI implementation: - */} -
    - {showPagination ? ( -
    - {' '} - {' '} - {' '} - {' '} - - Page{' '} - - {pageIndex + 1} of {pageOptions.length} - {' '} - - {/* - | Go to page:{' '} - { - const page = e.target.value ? Number(e.target.value) - 1 : 0 - gotoPage(page) - }} - style={{ width: '100px' }} - /> - {' '} */} - -
    - ) : null} - - ) -} diff --git a/src/Managing.WebApp/src/components/mollecules/Tabs/Tabs.tsx b/src/Managing.WebApp/src/components/mollecules/Tabs/Tabs.tsx deleted file mode 100644 index 1f06e88..0000000 --- a/src/Managing.WebApp/src/components/mollecules/Tabs/Tabs.tsx +++ /dev/null @@ -1,77 +0,0 @@ -import type { FC } from 'react' - -import type { ITabsProps } from '../../../global/type' - -/** - * Avalible Props - * @param className string - * @param tab Array of object - * @param selectedTab number - * @param onClick Function to set the active tab - * @param orientation Tab orientation Vertical | Horizontal - */ -const Tabs: FC = ({ - className = 'tabs-component', - tabs = [], - selectedTab = 0, - onClick, - orientation = 'horizontal', - addButton = false, - onAddButton, -}) => { - const Panel = tabs && tabs.find((tab) => tab.index === selectedTab) - - return ( -
    -
    - {tabs.map((tab) => ( - - ))} - {addButton && ( - - )} -
    -
    - {Panel && ( - - )} -
    -
    - ) -} -export default Tabs diff --git a/src/Managing.WebApp/src/components/mollecules/ThemeSelector/ThemeSelector.tsx b/src/Managing.WebApp/src/components/mollecules/ThemeSelector/ThemeSelector.tsx deleted file mode 100644 index a526acf..0000000 --- a/src/Managing.WebApp/src/components/mollecules/ThemeSelector/ThemeSelector.tsx +++ /dev/null @@ -1,21 +0,0 @@ -import useTheme from '../../../hooks/useTheme' -const themes = ['black', 'coffee', 'cyberpunk', 'lofi', 'retro'] - -const ThemeSelector = (): JSX.Element => { - const { setTheme } = useTheme() - - return ( - - ) -} - -export default ThemeSelector diff --git a/src/Managing.WebApp/src/components/mollecules/Toast/Toast.tsx b/src/Managing.WebApp/src/components/mollecules/Toast/Toast.tsx deleted file mode 100644 index 2a6c044..0000000 --- a/src/Managing.WebApp/src/components/mollecules/Toast/Toast.tsx +++ /dev/null @@ -1,31 +0,0 @@ -import type { Id, TypeOptions, UpdateOptions } from 'react-toastify' -import { toast } from 'react-toastify' - -const baseOptions: UpdateOptions = { - autoClose: 5000, - closeOnClick: true, - draggable: true, - hideProgressBar: false, - isLoading: false, - position: 'top-right', - progress: undefined, - theme: 'dark', -} - -class Toast { - private id: Id - - constructor(content: string) { - this.id = toast.loading(content) - } - - update(type: TypeOptions, content: string, opts?: any) { - const options = { ...baseOptions, ...opts } - options.type = type - options.render = content - options.isLoading = false - - toast.update(this.id, options) - } -} -export default Toast diff --git a/src/Managing.WebApp/src/components/mollecules/index.tsx b/src/Managing.WebApp/src/components/mollecules/index.tsx deleted file mode 100644 index 15228eb..0000000 --- a/src/Managing.WebApp/src/components/mollecules/index.tsx +++ /dev/null @@ -1,14 +0,0 @@ -export { CardText, CardPosition, CardSignal } from './CardText/CardText' -export { default as NavItem } from './NavItem/NavItem' -export { default as Tabs } from './Tabs/Tabs' -export { default as Modal } from './Modal/Modal' -export { default as Toast } from './Toast/Toast' -export { default as ThemeSelector } from './ThemeSelector/ThemeSelector' -export { default as Table } from './Table/Table' -export { default as NavBar } from './NavBar/NavBar' -export { default as PieChart } from './PieChart/PieChart' -export { default as FormInput } from './FormInput/FormInput' -export { default as LogIn } from './LogIn/LogIn' -export { default as GridTile } from './GridTile/GridTile' -export { default as SelectColumnFilter } from './Table/SelectColumnFilter' -export { default as Card } from './Card/Card' diff --git a/src/Managing.WebApp/src/components/organism/Account/Account.tsx b/src/Managing.WebApp/src/components/organism/Account/Account.tsx deleted file mode 100644 index 0fe15c4..0000000 --- a/src/Managing.WebApp/src/components/organism/Account/Account.tsx +++ /dev/null @@ -1,13 +0,0 @@ -import { useAccount, useEnsName } from 'wagmi' - -export function Account() { - const { address } = useAccount() - const { data: ensName } = useEnsName({ address }) - - return ( -
    - {ensName ?? address?.slice(0, -35)} - {ensName ? ` (${address})` : null} -
    - ) -} diff --git a/src/Managing.WebApp/src/components/organism/ActiveBots/ActiveBots.tsx b/src/Managing.WebApp/src/components/organism/ActiveBots/ActiveBots.tsx deleted file mode 100644 index 4814f8a..0000000 --- a/src/Managing.WebApp/src/components/organism/ActiveBots/ActiveBots.tsx +++ /dev/null @@ -1,211 +0,0 @@ -import { - ArrowDownIcon, - ArrowUpIcon, - ChevronDownIcon, - ChevronRightIcon, - PlayIcon, -} from '@heroicons/react/solid' -import React, { useEffect, useState } from 'react' - -import { Hub } from '../../../app/providers/Hubs' -import useApiUrlStore from '../../../app/store/apiStore' -import type { Account, TradingBot } from '../../../generated/ManagingApi' -import { - AccountClient, - BotClient, - TradeDirection, - TradeStatus, -} from '../../../generated/ManagingApi' -import { SelectColumnFilter, Table } from '../../mollecules' -import BacktestRowDetails from '../Backtest/backtestRowDetails' -import StatusBadge from '../StatusBadge/StatusBadge' -import Summary from '../Trading/Summary' - -export default function ActiveBots() { - const [bots, setBots] = useState([]) - const [accounts, setAccounts] = useState([]) - const { apiUrl } = useApiUrlStore() - - const columns = React.useMemo( - () => [ - { - Cell: ({ row }: any) => ( - // Use Cell to render an expander for each row. - // We can use the getToggleRowExpandedProps prop-getter - // to build the expander. - - {row.isExpanded ? ( - - ) : ( - - )} - - ), - - // Make sure it has an ID - Header: ({ getToggleAllRowsExpandedProps, isAllRowsExpanded }: any) => ( - - {isAllRowsExpanded ? 'v' : '>'} - - ), - // Build our expander column - id: 'expander', - }, - { - Cell: ({ cell }: any) => ( - <> - - - ), - Header: 'Status', - accessor: 'status', - disableFilters: true, - sortType: 'basic', - }, - { - accessor: 'isForWatchingOnly', - }, - { - Filter: SelectColumnFilter, - Header: 'Ticker', - accessor: 'ticker', - disableSortBy: true, - }, - { - Header: 'Account', - accessor: 'accountName', - }, - { - Filter: SelectColumnFilter, - Header: 'Timeframe', - accessor: 'timeframe', - disableSortBy: true, - }, - { - Filter: SelectColumnFilter, - Header: 'Scenario', - accessor: 'scenario', - disableSortBy: true, - }, - { - Cell: ({ cell }: any) => ( - <> - { - <> - { - cell.row.values.positions.filter( - (p: any) => p.originDirection == TradeDirection.Long - ).length - }{' '} - - {' | '} - { - cell.row.values.positions.filter( - (p: any) => p.originDirection == TradeDirection.Short - ).length - }{' '} - {' '} - { - cell.row.values.positions.filter( - (p: any) => p.status == TradeStatus.Filled - ).length - }{' '} - {' '} - - } - - ), - Header: 'Positions', - accessor: 'positions', - disableFilters: true, - }, - { - Cell: ({ cell }) => <>{cell.row.values.winRate} %, - Header: 'Winrate', - accessor: 'winRate', - disableFilters: true, - }, - { - Cell: ({ cell }) => <>{cell.row.values.profitAndLoss} $, - Header: 'PNL', - accessor: 'profitAndLoss', - disableFilters: true, - }, - ], - [] - ) - - useEffect(() => { - setupHubConnection().then(() => { - if (bots.length == 0) { - const client = new BotClient({}, apiUrl) - client.bot_GetActiveBots().then((data) => { - setBots(data) - }) - } - }) - const client = new AccountClient({}, apiUrl) - client.account_GetAccounts().then((data) => { - setAccounts(data) - }) - }, []) - - const setupHubConnection = async () => { - const hub = new Hub('bothub', apiUrl).hub - - hub.on('BotsSubscription', (data: TradingBot[]) => { - // eslint-disable-next-line no-console - console.log( - 'bot List', - bots.map((bot) => { - return bot.name - }) - ) - setBots(data) - }) - - return hub - } - - const renderRowSubComponent = React.useCallback( - ({ row }: any) => ( - <> - - - ), - [] - ) - - return ( - <> -
    - -
    -
    - - - - ) -} diff --git a/src/Managing.WebApp/src/components/organism/Backtest/backtestCards.tsx b/src/Managing.WebApp/src/components/organism/Backtest/backtestCards.tsx deleted file mode 100644 index 4bb97e0..0000000 --- a/src/Managing.WebApp/src/components/organism/Backtest/backtestCards.tsx +++ /dev/null @@ -1,309 +0,0 @@ -import { DotsVerticalIcon, TrashIcon } from '@heroicons/react/solid' -import moment from 'moment' -import React from 'react' - -import useApiUrlStore from '../../../app/store/apiStore' -import type { - Backtest, - MoneyManagement, - StartBotRequest, - Ticker, -} from '../../../generated/ManagingApi' -import { - BacktestClient, - BotClient, - BotType, -} from '../../../generated/ManagingApi' -import type { IBacktestCards } from '../../../global/type' -import MoneyManagementModal from '../../../pages/settingsPage/moneymanagement/moneyManagementModal' -import { CardPosition, CardText, Toast } from '../../mollecules' -import CardPositionItem from '../Trading/CardPositionItem' -import TradeChart from '../Trading/TradeChart/TradeChart' - -function baseBadgeClass(isOutlined = false) { - let classes = 'text-xs badge ' - - if (isOutlined) { - classes += 'badge-outline ' - } - return classes -} - -function botStatusResult( - growthPercentage: number | undefined, - hodlPercentage: number | undefined -) { - if (growthPercentage != undefined && hodlPercentage != undefined) { - const isWinning = growthPercentage > hodlPercentage - const classes = - baseBadgeClass() + (isWinning ? 'badge-success' : 'badge-content') - return
    {isWinning ? 'Winning' : 'Losing'}
    - } -} - -// function that return the number of day between a date and today -function daysBetween(date: Date) { - const oneDay = 24 * 60 * 60 * 1000 // hours*minutes*seconds*milliseconds - const firstDate = new Date(date) - const secondDate = new Date() - const diffDays = Math.round( - Math.abs((firstDate.getTime() - secondDate.getTime()) / oneDay) - ) - return diffDays -} - -const BacktestCards: React.FC = ({ list, setBacktests }) => { - const { apiUrl } = useApiUrlStore() - const [showMoneyManagementModal, setShowMoneyManagementModal] = - React.useState(false) - const [selectedMoneyManagement, setSelectedMoneyManagement] = - React.useState() - - async function runBot(backtest: Backtest, isForWatchOnly: boolean) { - const t = new Toast('Bot is starting') - const client = new BotClient({}, apiUrl) - - const request: StartBotRequest = { - accountName: backtest.accountName, - botName: backtest.ticker + '-' + backtest.timeframe.toString(), - botType: BotType.ScalpingBot, - isForWatchOnly: isForWatchOnly, - moneyManagementName: backtest.moneyManagement?.name, - scenario: backtest.scenario, - ticker: backtest.ticker as Ticker, - timeframe: backtest.timeframe, - } - - await client - .bot_Start(request) - .then((botStatus: string) => { - t.update('info', 'Bot status :' + botStatus) - }) - .catch((err) => { - t.update('error', 'Error :' + err) - }) - } - - async function runOptimizedBacktest(backtest: Backtest) { - const t = new Toast('Optimized backtest is running') - const client = new BacktestClient({}, apiUrl) - - await client - .backtest_Run( - backtest.accountName, - backtest.botType, - backtest.ticker as Ticker, - backtest.scenario, - backtest.timeframe, - false, - daysBetween(backtest.candles[0].date), - backtest.walletBalances[0].value, - '', - false, - backtest.optimizedMoneyManagement - ) - .then((backtest: Backtest) => { - t.update('success', `${backtest.ticker} Backtest Succeeded`) - setBacktests((arr) => [...arr, backtest]) - }) - .catch((err) => { - t.update('error', 'Error :' + err) - }) - } - - function saveMoneyManagement(moneyManagement: MoneyManagement) { - setSelectedMoneyManagement(moneyManagement) - setShowMoneyManagementModal(true) - } - - return ( -
    - {list.map((backtest: Backtest, index) => ( -
    -
    -
    - -
    - -
    -
    - { - - } -
    - -
    -

    -
    - -
      -
    • - -
    • -
    • - -
    • -
    • - -
    • -
    • - -
    • -
    -
    - {backtest.ticker} - {botStatusResult( - backtest.growthPercentage, - backtest.hodlPercentage - )} -

    -
    -
    - - - - -
    -
    -
    - - {/* */} - { - const realized = p.profitAndLoss?.realized ?? 0 - return realized > 0 ? p : null - })} - > - { - const realized = p.profitAndLoss?.realized ?? 0 - return realized <= 0 ? p : null - })} - > - -
    - -
    -
    - - - - -
    -
    -
    -
    - WR {backtest.winRate?.toFixed(2).toString()} % -
    -
    - PNL {backtest.growthPercentage?.toFixed(2).toString()} % -
    -
    -
    -
    -
    -
    - ))} - - setShowMoneyManagementModal(false)} - /> -
    - ) -} - -export default BacktestCards diff --git a/src/Managing.WebApp/src/components/organism/Backtest/backtestModal.tsx b/src/Managing.WebApp/src/components/organism/Backtest/backtestModal.tsx deleted file mode 100644 index f8c2f75..0000000 --- a/src/Managing.WebApp/src/components/organism/Backtest/backtestModal.tsx +++ /dev/null @@ -1,361 +0,0 @@ -import { useQuery } from '@tanstack/react-query' -import React, { useEffect, useState } from 'react' -import { useForm, type SubmitHandler } from 'react-hook-form' - -import useApiUrlStore from '../../../app/store/apiStore' -import type { - Backtest, - MoneyManagement, - Ticker, -} from '../../../generated/ManagingApi' -import { - AccountClient, - BacktestClient, - BotType, - DataClient, - MoneyManagementClient, - ScenarioClient, - Timeframe, -} from '../../../generated/ManagingApi' -import type { - BacktestModalProps, - IBacktestsFormInput, -} from '../../../global/type' -import { Loader, Slider } from '../../atoms' -import { Modal, Toast } from '../../mollecules' -import FormInput from '../../mollecules/FormInput/FormInput' -import CustomMoneyManagement from '../CustomMoneyManagement/CustomMoneyManagement' - -const BacktestModal: React.FC = ({ - showModal, - closeModal, - setBacktests, - showLoopSlider = false, -}) => { - const [selectedAccount, setSelectedAccount] = React.useState() - const [selectedTimeframe, setSelectedTimeframe] = React.useState() - const [selectedLoopQuantity, setLoopQuantity] = React.useState( - showLoopSlider ? 3 : 1 - ) - const [balance, setBalance] = React.useState(10000) - const [days, setDays] = React.useState(-10) - - const [customMoneyManagement, setCustomMoneyManagement] = - React.useState() - const [selectedMoneyManagement, setSelectedMoneyManagement] = - useState() - const [showCustomMoneyManagement, setShowCustomMoneyManagement] = - useState(false) - - const { apiUrl } = useApiUrlStore() - - const scenarioClient = new ScenarioClient({}, apiUrl) - const accountClient = new AccountClient({}, apiUrl) - const dataClient = new DataClient({}, apiUrl) - const moneyManagementClient = new MoneyManagementClient({}, apiUrl) - const backtestClient = new BacktestClient({}, apiUrl) - - const { register, handleSubmit } = useForm() - const onSubmit: SubmitHandler = async (form) => { - const { scenarioName, tickers } = form - closeModal() - for (let sIndex = 0; sIndex < scenarioName.length; sIndex++) { - for (let tIndex = 0; tIndex < tickers.length; tIndex++) { - await runBacktest( - form, - form.tickers[tIndex], - form.scenarioName[sIndex], - customMoneyManagement, - 1 - ) - } - } - } - - async function runBacktest( - form: IBacktestsFormInput, - ticker: string, - scenarioName: string, - moneyManagement: MoneyManagement | undefined, - loopCount: number - ) { - const t = new Toast(ticker + ' is running') - await backtestClient - .backtest_Run( - form.accountName, - form.botType, - ticker as Ticker, - scenarioName, - form.timeframe, - false, - days, - balance, - selectedMoneyManagement, - form.save, - selectedMoneyManagement ? undefined : moneyManagement - ) - .then((backtest: Backtest) => { - t.update('success', `${backtest.ticker} Backtest Succeeded`) - setBacktests((arr) => [...arr, backtest]) - - if (showLoopSlider && selectedLoopQuantity > loopCount) { - const nextCount = loopCount + 1 - const mm: MoneyManagement = { - balanceAtRisk: backtest.optimizedMoneyManagement.balanceAtRisk, - leverage: backtest.optimizedMoneyManagement.leverage, - name: backtest.optimizedMoneyManagement.name + nextCount, - stopLoss: backtest.optimizedMoneyManagement.stopLoss, - takeProfit: backtest.optimizedMoneyManagement.takeProfit, - timeframe: backtest.optimizedMoneyManagement.timeframe, - } - runBacktest(form, ticker, scenarioName, mm, nextCount) - } - }) - .catch((err) => { - t.update('error', 'Error :' + err) - }) - } - - function setSelectedAccountEvent(e: React.ChangeEvent) { - setSelectedAccount(e.target.value) - } - - function setSelectedTimeframeEvent(e: any) { - setSelectedTimeframe(e.target.value) - } - - function onMoneyManagementChange(e: any) { - if (e.target.value == 'Custom') { - setShowCustomMoneyManagement(true) - setSelectedMoneyManagement(e.target.value) - } else { - setShowCustomMoneyManagement(false) - setCustomMoneyManagement(undefined) - setSelectedMoneyManagement(undefined) - } - } - - const { data: scenarios } = useQuery({ - queryFn: () => scenarioClient.scenario_GetScenarios(), - queryKey: ['scenarios'], - }) - - const { data: accounts } = useQuery({ - onSuccess: () => { - if (accounts) { - setSelectedAccount(accounts[0].name) - } - setSelectedTimeframe(Timeframe.FiveMinutes) - }, - queryFn: () => accountClient.account_GetAccounts(), - queryKey: ['accounts'], - }) - - const { data: tickers, refetch: refetchTickers } = useQuery({ - enabled: !!selectedAccount && !!selectedTimeframe, - queryFn: () => { - if (selectedAccount && selectedTimeframe) { - return dataClient.data_GetTickers(selectedAccount, selectedTimeframe) - } - }, - queryKey: ['tickers', selectedAccount, selectedTimeframe], - }) - - const { data: moneyManagements } = useQuery({ - enabled: !!selectedTimeframe, - onSuccess: (data) => { - if (data) { - setSelectedMoneyManagement(data[0].name) - } - }, - queryFn: async () => { - if (selectedTimeframe) { - const mm = - await moneyManagementClient.moneyManagement_GetMoneyManagements() - mm.push({ - balanceAtRisk: 1, - leverage: 1, - name: 'Custom', - stopLoss: 1, - takeProfit: 1, - timeframe: selectedTimeframe, - }) - return mm - } - }, - queryKey: ['moneyManagements', selectedTimeframe], - }) - - useEffect(() => { - if (selectedAccount && selectedTimeframe) { - refetchTickers() - } - }, [selectedAccount, selectedTimeframe]) - - if (!accounts || !scenarios || !moneyManagements) { - return - } - - return ( - - - - - - - - - - - - - - - - - - - - setDays(e.target.value)} - step="1" - min="-360" - max="-1" - > - - - - setBalance(e.target.value)} - step="1000" - min="1000" - max="100000" - > - - - - - - - - - - - {/* Loop Quantity */} - {showLoopSlider ? ( - - setLoopQuantity(e.target.value)} - step="1" - min="1" - max="20" - > - - ) : null} - -
    - -
    - -
    - -
    -
    - ) -} -export default BacktestModal diff --git a/src/Managing.WebApp/src/components/organism/Backtest/backtestRowDetails.tsx b/src/Managing.WebApp/src/components/organism/Backtest/backtestRowDetails.tsx deleted file mode 100644 index 1ce93dd..0000000 --- a/src/Managing.WebApp/src/components/organism/Backtest/backtestRowDetails.tsx +++ /dev/null @@ -1,47 +0,0 @@ -import { TradeChart, CardPositionItem } from '..' -import type { IBotRowDetails } from '../../../global/interface' -import { CardPosition } from '../../mollecules' - -const BacktestRowDetails: React.FC = ({ - candles, - positions, - walletBalances, -}) => { - return ( - <> -
    -
    - { - const realized = p.profitAndLoss?.realized ?? 0 - return realized > 0 ? p : null - })} - > - { - const realized = p.profitAndLoss?.realized ?? 0 - return realized <= 0 ? p : null - })} - > - -
    -
    -
    - -
    -
    -
    - - ) -} - -export default BacktestRowDetails diff --git a/src/Managing.WebApp/src/components/organism/Backtest/backtestTable.tsx b/src/Managing.WebApp/src/components/organism/Backtest/backtestTable.tsx deleted file mode 100644 index 4289a33..0000000 --- a/src/Managing.WebApp/src/components/organism/Backtest/backtestTable.tsx +++ /dev/null @@ -1,275 +0,0 @@ -import { - ChevronDownIcon, - ChevronRightIcon, - PlayIcon, - TrashIcon, -} from '@heroicons/react/solid' -import React, { useEffect, useState } from 'react' - -import useApiUrlStore from '../../../app/store/apiStore' -import type { - Backtest, - StartBotRequest, - Ticker, -} from '../../../generated/ManagingApi' -import { BacktestClient, BotClient } from '../../../generated/ManagingApi' -import type { IBacktestCards } from '../../../global/type' -import { Toast, SelectColumnFilter, Table } from '../../mollecules' - -import BacktestRowDetails from './backtestRowDetails' - -const BacktestTable: React.FC = ({ list, isFetching }) => { - const [rows, setRows] = useState([]) - const { apiUrl } = useApiUrlStore() - - async function runBot(backtest: Backtest, isForWatchOnly: boolean) { - const t = new Toast('Bot is starting') - const client = new BotClient({}, apiUrl) - - const request: StartBotRequest = { - accountName: backtest.accountName, - botName: backtest.ticker + '-' + backtest.timeframe.toString(), - botType: backtest.botType, - isForWatchOnly: isForWatchOnly, - moneyManagementName: '', - scenario: backtest.scenario, - ticker: backtest.ticker as Ticker, - timeframe: backtest.timeframe, - } - - await client - .bot_Start(request) - .then((botStatus: string) => { - t.update('info', 'Bot status :' + botStatus) - }) - .catch((err) => { - t.update('error', 'Error :' + err) - }) - } - - async function deleteBacktest(id: string) { - const t = new Toast('Deleting backtest') - const client = new BacktestClient({}, apiUrl) - - await client - .backtest_DeleteBacktest(id) - .then(() => { - t.update('success', 'Backtest deleted') - }) - .catch((err) => { - t.update('error', err) - }) - } - - const columns = React.useMemo( - () => [ - { - Header: 'Informations', - columns: [ - { - Cell: ({ row }: any) => ( - // Use Cell to render an expander for each row. - // We can use the getToggleRowExpandedProps prop-getter - // to build the expander. - - {row.isExpanded ? ( - - ) : ( - - )} - - ), - - // Make sure it has an ID - Header: ({ - getToggleAllRowsExpandedProps, - isAllRowsExpanded, - }: any) => ( - - {isAllRowsExpanded ? 'v' : '>'} - - ), - // Build our expander column - id: 'expander', - }, - { - Filter: SelectColumnFilter, - Header: 'Ticker', - accessor: 'ticker', - disableSortBy: true, - }, - { - Filter: SelectColumnFilter, - Header: 'Timeframe', - accessor: 'timeframe', - disableSortBy: true, - }, - { - Filter: SelectColumnFilter, - Header: 'Scenario', - accessor: 'scenario', - disableSortBy: true, - }, - { - Filter: SelectColumnFilter, - Header: 'BotType', - accessor: 'botType', - disableSortBy: true, - }, - { - Filter: SelectColumnFilter, - Header: 'Account', - accessor: 'accountName', - disableSortBy: true, - }, - ], - }, - { - Header: 'Results', - columns: [ - { - Cell: ({ cell }: any) => ( - <>{cell.row.values.finalPnl.toFixed(2)} $ - ), - Header: 'Pnl $', - accessor: 'finalPnl', - disableFilters: true, - sortType: 'basic', - }, - { - Cell: ({ cell }: any) => ( - <>{cell.row.values.hodlPercentage.toFixed(2)} % - ), - Header: 'Hodl %', - accessor: 'hodlPercentage', - disableFilters: true, - sortType: 'basic', - }, - { - Cell: ({ cell }: any) => <>{cell.row.values.winRate} %, - Header: 'Winrate', - accessor: 'winRate', - disableFilters: true, - }, - { - Cell: ({ cell }: any) => ( - <>{cell.row.values.growthPercentage.toFixed(2)} % - ), - Header: 'Pnl %', - accessor: 'growthPercentage', - disableFilters: true, - sortType: 'basic', - }, - { - Cell: ({ cell }: any) => ( - <> - {( - cell.row.values.growthPercentage - - cell.row.values.hodlPercentage - ).toFixed(2)} - - ), - Header: 'H/P', - accessor: 'diff', - disableFilters: true, - sortType: 'basic', - }, - ], - }, - { - Header: 'Action', - columns: [ - { - Cell: ({ cell }: any) => ( - <> -
    - -
    - - ), - Header: '', - accessor: 'id', - disableFilters: true, - }, - // { - // Cell: ({ cell }) => ( - // <> - //
    - // - //
    - // - // ), - // Header: '', - // accessor: 'watcher', - // disableFilters: true, - // }, - { - Cell: ({ cell }: any) => ( - <> -
    - -
    - - ), - Header: '', - accessor: 'bot', - disableFilters: true, - }, - ], - }, - ], - [] - ) - - useEffect(() => { - setRows(list) - }, [list]) - - const renderRowSubComponent = React.useCallback( - ({ row }: any) => ( - <> - - - ), - [] - ) - - return ( -
    - {isFetching ? ( - - ) : ( -
    - )} - - ) -} - -export default BacktestTable diff --git a/src/Managing.WebApp/src/components/organism/CustomMoneyManagement/CustomMoneyManagement.tsx b/src/Managing.WebApp/src/components/organism/CustomMoneyManagement/CustomMoneyManagement.tsx deleted file mode 100644 index 78f3573..0000000 --- a/src/Managing.WebApp/src/components/organism/CustomMoneyManagement/CustomMoneyManagement.tsx +++ /dev/null @@ -1,104 +0,0 @@ -import React, { useEffect, useState } from 'react' - -import type { MoneyManagement, Timeframe } from '../../../generated/ManagingApi' -import { Slider } from '../../atoms' -import FormInput from '../../mollecules/FormInput/FormInput' - -type ICustomMoneyManagement = { - onCreateMoneyManagement: (moneyManagement: MoneyManagement) => void - timeframe: Timeframe - showCustomMoneyManagement: boolean -} - -const CustomMoneyManagement: React.FC = ({ - onCreateMoneyManagement, - timeframe, - showCustomMoneyManagement, -}) => { - const [balanceAtRisk, setBalanceAtRisk] = useState(1) - const [leverage, setLeverage] = useState(1) - const [takeProfit, setTakeProfit] = useState(1) - const [stopLoss, setStopLoss] = useState(1) - - const handleCreateMoneyManagement = () => { - const moneyManagement: MoneyManagement = { - balanceAtRisk, - leverage, - name: 'custom', - stopLoss, - takeProfit, - timeframe, - } - onCreateMoneyManagement(moneyManagement) - } - - useEffect(() => { - handleCreateMoneyManagement() - }, [balanceAtRisk, leverage, takeProfit, stopLoss]) - - return ( - <> - {showCustomMoneyManagement ? ( -
    - -
    - Custom MoneyManagement -
    -
    - - setBalanceAtRisk(parseInt(e.target.value))} - min="1" - max="100" - step="1" - suffixValue=" %" - > - - - - setLeverage(e.target.value)} - prefixValue="x " - > - - - - setTakeProfit(e.target.value)} - step="0.01" - max="20" - suffixValue=" %" - > - - - - setStopLoss(e.target.value)} - step="0.01" - max="20" - suffixValue=" %" - > - -
    -
    - ) : null} - - ) -} - -export default CustomMoneyManagement diff --git a/src/Managing.WebApp/src/components/organism/Positions/PositionList.tsx b/src/Managing.WebApp/src/components/organism/Positions/PositionList.tsx deleted file mode 100644 index af7231d..0000000 --- a/src/Managing.WebApp/src/components/organism/Positions/PositionList.tsx +++ /dev/null @@ -1,128 +0,0 @@ -import { StopIcon } from '@heroicons/react/solid' -import moment from 'moment' -import React from 'react' - -import useApiUrlStore from '../../../app/store/apiStore' -import { - TradeDirection, - type Position, - Ticker, - PositionStatus, - TradingClient, -} from '../../../generated/ManagingApi' -import { Toast, Table } from '../../mollecules' - -import PositionStatusBadge from './PositionStatusBadge' - -type IPositionList = { - positions: Position[] - isFetching: boolean -} - -const PositionsList: React.FC = ({ positions, isFetching }) => { - const { apiUrl } = useApiUrlStore() - - async function closePosition(identifier: string) { - const t = new Toast('Closing position') - const client = new TradingClient({}, apiUrl) - await client - .trading_ClosePosition(identifier) - .then(() => { - t.update('success', 'Position closed') - }) - .catch((err) => { - t.update('error', 'Error :' + err) - }) - } - - const columns = React.useMemo( - () => [ - { - Cell: ({ cell }: any) => ( - <> -
    - -
    - - ), - Header: 'Status', - accessor: 'status', - disableFilters: true, - sortType: 'basic', - }, - - { - Cell: ({ cell }: any) =>
    {Object.values(Ticker)[cell.value]}
    , - Header: 'Ticker', - accessor: 'ticker', - disableFilters: true, - disableSortBy: true, - }, - { - Cell: ({ cell }: any) => ( -
    {Object.values(TradeDirection)[cell.value]}
    - ), - Header: 'Direction', - accessor: 'originDirection', - disableFilters: true, - disableSortBy: true, - }, - { - Header: 'Account', - accessor: 'accountName', - disableFilters: true, - disableSortBy: true, - }, - { - Cell: ({ cell }: any) => moment(cell.value).fromNow(), - Header: 'Date', - accessor: 'date', - disableFilters: true, - }, - { - Cell: ({ cell }: any) => ( -
    {(cell.value.realized as number).toFixed(4)} $
    - ), - Header: 'uPNL', - accessor: 'profitAndLoss', - disableFilters: true, - }, - { - Cell: ({ cell }) => ( - <> -
    - -
    - - ), - Header: 'Actions', - accessor: 'identifier', - disableFilters: true, - }, - ], - [] - ) - return ( -
    - {isFetching ? ( -
    - -
    - ) : ( -
    - )} - - ) -} - -export default PositionsList diff --git a/src/Managing.WebApp/src/components/organism/Positions/PositionStatusBadge.tsx b/src/Managing.WebApp/src/components/organism/Positions/PositionStatusBadge.tsx deleted file mode 100644 index 2ac4f64..0000000 --- a/src/Managing.WebApp/src/components/organism/Positions/PositionStatusBadge.tsx +++ /dev/null @@ -1,30 +0,0 @@ -import { PositionStatus } from '../../../generated/ManagingApi' - -type IPositionStatusBadge = { - status: PositionStatus -} -function statusClasses(status: PositionStatus) { - let commonClasses = 'badge badge-xs ' - switch (status) { - case PositionStatus.Canceled: - case PositionStatus.Rejected: - commonClasses += 'bg-red-100' - break - case PositionStatus.New: - commonClasses += 'bg-blue-100' - break - case PositionStatus.PartiallyFilled: - commonClasses += 'bg-orange-100' - break - case PositionStatus.Filled: - commonClasses += 'bg-green-100' - break - default: - break - } - return commonClasses -} - -export default function PositionStatusBadge({ status }: IPositionStatusBadge) { - return -} diff --git a/src/Managing.WebApp/src/components/organism/SpotLightBadge/SpotLightBadge.tsx b/src/Managing.WebApp/src/components/organism/SpotLightBadge/SpotLightBadge.tsx deleted file mode 100644 index c1e260a..0000000 --- a/src/Managing.WebApp/src/components/organism/SpotLightBadge/SpotLightBadge.tsx +++ /dev/null @@ -1,31 +0,0 @@ -import moment from 'moment' - -import { TradeDirection } from '../../../generated/ManagingApi' -import type { ISpotlightBadge } from '../../../global/type' - -function GetBadgeColor(direction: TradeDirection | undefined) { - switch (direction) { - case TradeDirection.Long: - return 'badge bg-success' - case TradeDirection.Short: - return 'badge bg-error' - case TradeDirection.None: - return 'badge bg-warning' - default: - return 'badge' - } -} - -export default function SpotLightBadge({ - direction, - date, - price, -}: ISpotlightBadge) { - const tooltipText = - date == undefined ? 'No signal' : moment(date).fromNow() + ' @ ' + price - return ( -
    -
    -
    - ) -} diff --git a/src/Managing.WebApp/src/components/organism/StatusBadge/StatusBadge.tsx b/src/Managing.WebApp/src/components/organism/StatusBadge/StatusBadge.tsx deleted file mode 100644 index 4c44184..0000000 --- a/src/Managing.WebApp/src/components/organism/StatusBadge/StatusBadge.tsx +++ /dev/null @@ -1,14 +0,0 @@ -import type { IStatusBadge } from '../../../global/type' - -function statusClasses(status: string, isForWatchOnly: boolean) { - const commonClasses = 'badge badge-xs' - if (isForWatchOnly) { - return `${commonClasses} 'bg-blue-500'` - } - - return `${commonClasses} ${status == 'Up' ? 'bg-green-500' : 'bg-red-500'}` -} - -export default function StatusBadge({ status, isForWatchOnly }: IStatusBadge) { - return -} diff --git a/src/Managing.WebApp/src/components/organism/Trading/CardPositionItem.tsx b/src/Managing.WebApp/src/components/organism/Trading/CardPositionItem.tsx deleted file mode 100644 index 8a8e192..0000000 --- a/src/Managing.WebApp/src/components/organism/Trading/CardPositionItem.tsx +++ /dev/null @@ -1,34 +0,0 @@ -import { PositionStatus } from '../../../generated/ManagingApi' -import type { ICardPositionFlipped } from '../../../global/type' -import { CardText } from '../../mollecules' - -const CardPositionFlipped: React.FC = ({ positions }) => { - return ( - <> - { - const realized = p.profitAndLoss?.realized ?? 0 - return realized > 0 && p.status == PositionStatus.Flipped - ? p - : null - }) - .length.toString() + - ' | ' + - positions - .filter((p) => { - const realized = p.profitAndLoss?.realized ?? 0 - return realized <= 0 && p.status == PositionStatus.Flipped - ? p - : null - }) - .length.toString() - } - > - - ) -} - -export default CardPositionFlipped diff --git a/src/Managing.WebApp/src/components/organism/Trading/Summary.tsx b/src/Managing.WebApp/src/components/organism/Trading/Summary.tsx deleted file mode 100644 index 021531d..0000000 --- a/src/Managing.WebApp/src/components/organism/Trading/Summary.tsx +++ /dev/null @@ -1,133 +0,0 @@ -import { ArrowDownIcon, ArrowUpIcon } from '@heroicons/react/solid' -import React, { useEffect, useState } from 'react' - -import type { TradingBot } from '../../../generated/ManagingApi' -import { PositionStatus, TradeDirection } from '../../../generated/ManagingApi' -import type { IAccountBalanceProps } from '../../../global/type' - -function GetGlobalWinrate(bots: TradingBot[]) { - if (bots == null || bots == undefined || bots.length == 0) { - return 0 - } - - let totalPositions = 0 - let winningPosition = 0 - - bots.forEach((bot) => { - totalPositions += bot.positions.filter( - (p) => p.status != PositionStatus.New - ).length - winningPosition += bot.positions.filter((p) => { - const realized = p.profitAndLoss?.realized ?? 0 - return realized > 0 && - (p.status == PositionStatus.Finished || - p.status == PositionStatus.Flipped) - ? p - : null - }).length - }) - - if (totalPositions == 0) return 0 - - return (winningPosition * 100) / totalPositions -} - -function GetPositionCount( - bots: TradingBot[], - direction: TradeDirection, - status: PositionStatus -) { - let totalPositions = 0 - - if (bots == null || bots == undefined) { - return 0 - } - - bots.forEach((bot) => { - totalPositions += bot.positions.filter( - (p) => p.status == status && p.originDirection == direction - ).length - }) - - return totalPositions -} - -const Summary: React.FC = ({ bots }) => { - const [globalPnl, setGlobalPnl] = useState(0) - const [globalWinrate, setGlobalWinrate] = useState(0) - - const [openLong, setLong] = useState(0) - const [openShort, setShort] = useState(0) - - const [closedLong, setClosedLong] = useState(0) - const [closedShort, setClosedShort] = useState(0) - - useEffect(() => { - if (bots) { - const pnl = bots.reduce((acc, bot) => { - return acc + bot.profitAndLoss - }, 0) - setGlobalPnl(pnl) - setGlobalWinrate(GetGlobalWinrate(bots)) - setLong( - GetPositionCount(bots, TradeDirection.Long, PositionStatus.Filled) - ) - setShort( - GetPositionCount(bots, TradeDirection.Short, PositionStatus.Filled) - ) - setClosedLong( - GetPositionCount(bots, TradeDirection.Long, PositionStatus.Finished) + - GetPositionCount(bots, TradeDirection.Long, PositionStatus.Flipped) - ) - setClosedShort( - GetPositionCount(bots, TradeDirection.Short, PositionStatus.Finished) + - GetPositionCount(bots, TradeDirection.Short, PositionStatus.Flipped) - ) - } - }, [bots]) - - return ( -
    -
    - -
    -
    -
    Bots running
    -
    {bots.length}
    -
    - -
    -
    Total Profit
    -
    {globalPnl.toFixed(4)} $
    -
    - -
    -
    Global Winrate
    -
    - {globalWinrate ? globalWinrate.toFixed(2) : 0} % -
    -
    - -
    -
    Positions Openend
    -
    - {openLong} {' '} - {openShort}{' '} - {' '} -
    -
    -
    -
    Positions Closed
    -
    - {closedLong}{' '} - {' '} - {closedShort}{' '} - {' '} -
    -
    -
    -
    - ) -} - -export default Summary diff --git a/src/Managing.WebApp/src/components/organism/Trading/TradeChart/TradeChart.tsx b/src/Managing.WebApp/src/components/organism/Trading/TradeChart/TradeChart.tsx deleted file mode 100644 index 60d4daa..0000000 --- a/src/Managing.WebApp/src/components/organism/Trading/TradeChart/TradeChart.tsx +++ /dev/null @@ -1,302 +0,0 @@ -import type { - CandlestickData, - IChartApi, - ISeriesApi, - PriceLineOptions, - SeriesMarker, - SeriesMarkerShape, - Time, - UTCTimestamp, -} from 'lightweight-charts' -import { LineStyle, createChart, CrosshairMode } from 'lightweight-charts' -import moment from 'moment' -import * as React from 'react' -import { useEffect, useRef, useState } from 'react' - -import type { - Candle, - KeyValuePairOfDateTimeAndDecimal, - Position, - Signal, -} from '../../../../generated/ManagingApi' -import { - PositionStatus, - TradeDirection, -} from '../../../../generated/ManagingApi' -import useTheme from '../../../../hooks/useTheme' - -type ITradeChartProps = { - candles: Candle[] - positions: Position[] - signals: Signal[] - walletBalances?: KeyValuePairOfDateTimeAndDecimal[] | null - stream?: Candle | null - width: number - height: number -} - -const TradeChart = ({ - candles, - positions, - signals, - walletBalances, - stream, - width, - height, -}: ITradeChartProps) => { - const chartRef = React.useRef(null) - const chart = useRef() - const { themeProperty } = useTheme() - const theme = themeProperty() - const series1 = useRef>() - const [timeDiff, setTimeDiff] = useState(0) - const [candleCount, setCandleCount] = useState(candles.length) - - function buildLine( - color: string, - price: number, - title: string - ): PriceLineOptions { - return { - axisLabelVisible: true, - color: color, - lineStyle: LineStyle.Dotted, - lineVisible: true, - lineWidth: 1, - price: price, - title: title, - } - } - - function buildMarker( - shape: SeriesMarkerShape, - color: string, - direction: TradeDirection, - date: Date, - text?: string - ): SeriesMarker
    - - ) -} - -export default ScenarioTable diff --git a/src/Managing.WebApp/src/pages/scenarioPage/strategyList.tsx b/src/Managing.WebApp/src/pages/scenarioPage/strategyList.tsx deleted file mode 100644 index e75e916..0000000 --- a/src/Managing.WebApp/src/pages/scenarioPage/strategyList.tsx +++ /dev/null @@ -1,401 +0,0 @@ -import React, { useEffect, useState } from 'react' -import type { SubmitHandler } from 'react-hook-form' -import { useForm } from 'react-hook-form' -import 'react-toastify/dist/ReactToastify.css' - -import useApiUrlStore from '../../app/store/apiStore' -import { Toast } from '../../components/mollecules' -import type { Strategy } from '../../generated/ManagingApi' -import { - StrategyType, - ScenarioClient, - Timeframe, -} from '../../generated/ManagingApi' - -import StrategyTable from './strategyTable' - -interface IStrategyFormInput { - type: StrategyType - timeframe: Timeframe - name: string - period: number - fastPeriods: number - slowPeriods: number - signalPeriods: number - multiplier: number - stochPeriods: number - smoothPeriods: number - cyclePeriods: number -} - -const StrategyList: React.FC = () => { - const [strategyType, setStrategyType] = useState( - StrategyType.RsiDivergence - ) - const [strategies, setStrategies] = useState([]) - const [showModal, setShowModal] = useState(false) - const { register, handleSubmit } = useForm() - const { apiUrl } = useApiUrlStore() - const scenarioClient = new ScenarioClient({}, apiUrl) - - async function createStrategy(form: IStrategyFormInput) { - const t = new Toast('Creating strategy') - await scenarioClient - .scenario_CreateStrategy( - form.type, - form.timeframe, - form.name, - form.period, - form.fastPeriods, - form.slowPeriods, - form.signalPeriods, - form.multiplier, - form.stochPeriods, - form.smoothPeriods, - form.cyclePeriods - ) - .then((strategy: Strategy) => { - t.update('success', 'Strategy created') - setStrategies((arr) => [...arr, strategy]) - }) - .catch((err) => { - t.update('error', err) - }) - } - - function setStrategyTypeEvent(e: any) { - setStrategyType(e.target.value) - } - - const onSubmit: SubmitHandler = async (form) => { - closeModal() - await createStrategy(form) - } - - useEffect(() => { - scenarioClient.scenario_GetStrategies().then((data) => { - setStrategies(data) - }) - }, []) - - function openModal() { - setShowModal(true) - } - - function closeModal() { - setShowModal(false) - } - - return ( -
    -
    - - - {showModal ? ( - <> -
    -
    -
    -
    - -
    - Strategy builder -
    -
    -
    - - -
    -
    -
    -
    - - -
    -
    -
    -
    - - -
    -
    - - {strategyType == StrategyType.EmaTrend || - strategyType == StrategyType.RsiDivergence || - strategyType == StrategyType.StDev || - strategyType == StrategyType.EmaCross || - strategyType == StrategyType.RsiDivergenceConfirm ? ( - <> -
    -
    - - -
    -
    - - ) : null} - - {strategyType == StrategyType.MacdCross ? ( - <> -
    -
    - - -
    -
    -
    -
    - - -
    -
    -
    -
    - - -
    -
    - - ) : null} - - {strategyType == StrategyType.Stc ? ( - <> -
    -
    - - -
    -
    -
    -
    - - -
    -
    -
    -
    - - -
    -
    - - ) : null} - - {strategyType == StrategyType.SuperTrend || - strategyType == StrategyType.ChandelierExit ? ( - <> -
    -
    - - -
    -
    -
    -
    - - -
    -
    - - ) : null} - - {strategyType == StrategyType.StochRsiTrend ? ( - <> -
    -
    - - -
    -
    -
    -
    - - -
    -
    -
    -
    - - -
    -
    -
    -
    - - -
    -
    - - ) : null} -
    - -
    -
    -
    - -
    - - ) : null} -
    -
    - ) -} - -export default StrategyList diff --git a/src/Managing.WebApp/src/pages/scenarioPage/strategyTable.tsx b/src/Managing.WebApp/src/pages/scenarioPage/strategyTable.tsx deleted file mode 100644 index 6743d86..0000000 --- a/src/Managing.WebApp/src/pages/scenarioPage/strategyTable.tsx +++ /dev/null @@ -1,94 +0,0 @@ -import { TrashIcon } from '@heroicons/react/solid' -import React, { useEffect, useState } from 'react' - -import useApiUrlStore from '../../app/store/apiStore' -import { SelectColumnFilter, Table, Toast } from '../../components/mollecules' -import type { Strategy } from '../../generated/ManagingApi' -import { ScenarioClient } from '../../generated/ManagingApi' - -interface IStrategyList { - list: Strategy[] -} - -const StrategyTable: React.FC = ({ list }) => { - const [rows, setRows] = useState([]) - const { apiUrl } = useApiUrlStore() - - async function deleteBacktest(id: string) { - const t = new Toast('Deleting strategy') - const client = new ScenarioClient({}, apiUrl) - - await client - .scenario_DeleteStrategy(id) - .then(() => { - t.update('info', 'Strategy deleted') - }) - .catch((err) => { - t.update('error', err) - }) - } - - const columns = React.useMemo( - () => [ - { - Header: 'Name', - accessor: 'name', - disableFilters: true, - }, - { - Filter: SelectColumnFilter, - Header: 'Type', - accessor: 'type', - disableSortBy: true, - }, - { - Filter: SelectColumnFilter, - Header: 'Timeframe', - accessor: 'timeframe', - disableSortBy: true, - }, - { - Filter: SelectColumnFilter, - Header: 'Signal', - accessor: 'signalType', - disableSortBy: true, - }, - { - Filter: SelectColumnFilter, - Header: 'Period', - accessor: 'period', - disableSortBy: true, - }, - { - Cell: ({ cell }: any) => ( - <> -
    - -
    - - ), - Header: '', - accessor: 'id', - disableFilters: true, - }, - ], - [] - ) - - useEffect(() => { - setRows(list) - }, [list]) - - return ( -
    -
    - - ) -} - -export default StrategyTable diff --git a/src/Managing.WebApp/src/pages/settingsPage/account/accountModal.tsx b/src/Managing.WebApp/src/pages/settingsPage/account/accountModal.tsx deleted file mode 100644 index 84681f0..0000000 --- a/src/Managing.WebApp/src/pages/settingsPage/account/accountModal.tsx +++ /dev/null @@ -1,178 +0,0 @@ -import { useState } from 'react' -import type { SubmitHandler } from 'react-hook-form' -import { useForm } from 'react-hook-form' - -import useApiUrlStore from '../../../app/store/apiStore' -import { Modal, Toast } from '../../../components/mollecules' -import type { Account } from '../../../generated/ManagingApi' -import { - AccountType, - AccountClient, - TradingExchanges, -} from '../../../generated/ManagingApi' -import type { IAccountFormInput, IModalProps } from '../../../global/type' - -const AccountModal: React.FC = ({ showModal, toggleModal }) => { - const [selectedExchange, setSelectedExchange] = useState() - const [selectedType, setSelectedType] = useState() - const { register, handleSubmit } = useForm() - const { apiUrl } = useApiUrlStore() - - async function createMoneyManagement(form: IAccountFormInput) { - const t = new Toast('Creating account') - const client = new AccountClient({}, apiUrl) - const a: Account = { - exchange: form.exchange, - key: form.key, - name: form.name, - secret: form.secret, - type: form.type, - } - await client - .account_PostAccount(a) - .then(() => { - t.update('success', 'Account created') - }) - .catch((err) => { - t.update('error', 'Error :' + err) - }) - } - const onSubmit: SubmitHandler = async (form) => { - // @ts-ignore - toggleModal() - await createMoneyManagement(form) - } - - function setSelectedExchangeEvent(e: any) { - setSelectedExchange(e.target.value) - } - - function setSelectedTypeEvent(e: any) { - setSelectedType(e.target.value) - } - - return ( -
    - -
    -
    - - -
    -
    - -
    -
    - - -
    -
    - -
    -
    - - -
    -
    - - {selectedExchange != TradingExchanges.Evm && - selectedType != AccountType.Trader ? ( - <> -
    -
    - - -
    -
    - -
    -
    - - -
    -
    - - ) : null} - - {selectedExchange == TradingExchanges.Evm && - selectedType == AccountType.Watch ? ( - <> -
    -
    - - -
    -
    - - ) : null} - -
    - -
    -
    -
    - ) -} - -export default AccountModal diff --git a/src/Managing.WebApp/src/pages/settingsPage/account/accountRowDetails.tsx b/src/Managing.WebApp/src/pages/settingsPage/account/accountRowDetails.tsx deleted file mode 100644 index 47d8415..0000000 --- a/src/Managing.WebApp/src/pages/settingsPage/account/accountRowDetails.tsx +++ /dev/null @@ -1,63 +0,0 @@ -import React from 'react' - -import { SelectColumnFilter, Table } from '../../../components/mollecules' -import { IAccountRowDetail } from '../../../global/type' - -const columns = [ - { - Header: 'Chain', - accessor: 'chain.name', - disableFilters: true, - disableSortBy: true, - }, - { - Filter: SelectColumnFilter, - Header: 'Assets', - accessor: 'tokenName', - disableFilters: true, - disableSortBy: true, - }, - { - Cell: ({ cell }: any) => ( - <> -
    - {cell.row.values.amount.toFixed(4)} -
    - - ), - Header: 'Quantity', - accessor: 'amount', - disableFilters: true, - }, - { - Cell: ({ cell }: any) => <>{cell.row.values.value.toFixed(2)} $, - Header: 'USD', - accessor: 'value', - disableFilters: true, - disableSortBy: true, - }, - { - Cell: ({ cell }: any) => <> {cell.row.values.price} $, - Header: 'Price', - accessor: 'price', - disableFilters: true, - }, -] - -const AccountRowDetails: React.FC = ({ - balances, - showTotal, -}) => { - return ( - <> -
    - - ) -} - -export default AccountRowDetails diff --git a/src/Managing.WebApp/src/pages/settingsPage/account/accountSettings.tsx b/src/Managing.WebApp/src/pages/settingsPage/account/accountSettings.tsx deleted file mode 100644 index 708dde6..0000000 --- a/src/Managing.WebApp/src/pages/settingsPage/account/accountSettings.tsx +++ /dev/null @@ -1,47 +0,0 @@ -import React, { useEffect, useState } from 'react' - -import useApiUrlStore from '../../../app/store/apiStore' -import type { Account } from '../../../generated/ManagingApi' -import { AccountClient } from '../../../generated/ManagingApi' - -import AccountModal from './accountModal' -import AccountTable from './accountTable' - -const AccountSettings: React.FC = () => { - const [accounts, setAccounts] = useState([]) - const [showModal, setShowModal] = useState(false) - const { apiUrl } = useApiUrlStore() - const [isFetching, setIsFetching] = useState(false) - - useEffect(() => { - const client = new AccountClient({}, apiUrl) - setIsFetching(true) - client - .account_GetAccountsBalances() - .then((data) => { - setAccounts(data) - }) - .finally(() => setIsFetching(false)) - }, []) - - function toggleModal() { - setShowModal(!showModal) - } - - function openModal() { - setShowModal(true) - } - - return ( -
    -
    - - - -
    -
    - ) -} -export default AccountSettings diff --git a/src/Managing.WebApp/src/pages/settingsPage/account/accountTable.tsx b/src/Managing.WebApp/src/pages/settingsPage/account/accountTable.tsx deleted file mode 100644 index 5574e3c..0000000 --- a/src/Managing.WebApp/src/pages/settingsPage/account/accountTable.tsx +++ /dev/null @@ -1,163 +0,0 @@ -import { - ChevronDownIcon, - ChevronRightIcon, - ClipboardCopyIcon, - TrashIcon, -} from '@heroicons/react/solid' -import React, { useEffect, useState, useMemo } from 'react' - -import useApiUrlStore from '../../../app/store/apiStore' -import { - SelectColumnFilter, - Table, - Toast, -} from '../../../components/mollecules' -import type { Account } from '../../../generated/ManagingApi' -import { AccountClient } from '../../../generated/ManagingApi' - -import AccountRowDetails from './accountRowDetails' - -interface IAccountList { - list: Account[] - isFetching: boolean -} - -const AccountTable: React.FC = ({ list, isFetching }) => { - const [rows, setRows] = useState([]) - const { apiUrl } = useApiUrlStore() - - async function deleteAcount(accountName: string) { - const t = new Toast('Deleting money management') - const client = new AccountClient({}, apiUrl) - - await client - .account_DeleteAccount(accountName) - .then(() => { - t.update('success', 'Account deleted') - }) - .catch((err) => { - t.update('error', 'Error :' + err) - }) - } - - const columns = useMemo( - () => [ - { - Cell: ({ row }: any) => ( - - {row.isExpanded ? ( - - ) : ( - - )} - - ), - - // Make sure it has an ID - Header: ({ getToggleAllRowsExpandedProps, isAllRowsExpanded }: any) => ( - - {isAllRowsExpanded ? 'v' : '>'} - - ), - // Build our expander column - id: 'expander', - }, - { - Header: 'Name', - accessor: 'name', - disableFilters: true, - }, - { - Filter: SelectColumnFilter, - Header: 'Exchange', - accessor: 'exchange', - disableSortBy: true, - }, - { - Filter: SelectColumnFilter, - Header: 'Type', - accessor: 'type', - disableSortBy: true, - }, - { - Cell: ({ cell }: any) => ( - <> -
    - {cell.row.values.key.substring(0, 6)}... - {cell.row.values.key.slice(-4)} -
    - - ), - Header: 'Key', - accessor: 'key', - disableFilters: true, - }, - { - Cell: ({ cell }: any) => ( - <> -
    - -
    -
    - -
    - - ), - Header: 'Actions', - accessor: 'id', - disableFilters: true, - }, - ], - [] - ) - - useEffect(() => { - setRows(list) - }, [list]) - - const renderRowSubComponent = React.useCallback( - ({ row }: any) => ( - <> - {row.original.balances != undefined ? ( - - ) : ( -
    No balances
    - )} - - ), - [] - ) - - return ( - <> - {isFetching ? ( -
    - -
    - ) : ( -
    - )} - - ) -} - -export default AccountTable diff --git a/src/Managing.WebApp/src/pages/settingsPage/moneymanagement/moneyManagement.tsx b/src/Managing.WebApp/src/pages/settingsPage/moneymanagement/moneyManagement.tsx deleted file mode 100644 index 7e5bbda..0000000 --- a/src/Managing.WebApp/src/pages/settingsPage/moneymanagement/moneyManagement.tsx +++ /dev/null @@ -1,52 +0,0 @@ -import React, { useEffect, useState } from 'react' - -import useApiUrlStore from '../../../app/store/apiStore' -import type { MoneyManagement } from '../../../generated/ManagingApi' -import { MoneyManagementClient } from '../../../generated/ManagingApi' - -import MoneyManagementModal from './moneyManagementModal' -import MoneyManagementTable from './moneymanagementTable' - -const MoneyManagementSettings: React.FC = () => { - const [moneyManagements, setMoneyManagements] = useState( - [] - ) - const [showModal, setShowModal] = useState(false) - const { apiUrl } = useApiUrlStore() - - useEffect(() => { - const client = new MoneyManagementClient({}, apiUrl) - client.moneyManagement_GetMoneyManagements().then((data) => { - setMoneyManagements(data) - }) - }, []) - - function toggleModal() { - setShowModal(!showModal) - } - - function openModal() { - setShowModal(true) - } - - function closeModal() { - setShowModal(false) - } - - return ( -
    -
    - - - -
    -
    - ) -} -export default MoneyManagementSettings diff --git a/src/Managing.WebApp/src/pages/settingsPage/moneymanagement/moneyManagementModal.tsx b/src/Managing.WebApp/src/pages/settingsPage/moneymanagement/moneyManagementModal.tsx deleted file mode 100644 index 79043dd..0000000 --- a/src/Managing.WebApp/src/pages/settingsPage/moneymanagement/moneyManagementModal.tsx +++ /dev/null @@ -1,190 +0,0 @@ -import { useEffect, useState } from 'react' -import type { SubmitHandler } from 'react-hook-form' -import { useForm } from 'react-hook-form' - -import useApiUrlStore from '../../../app/store/apiStore' -import { Slider } from '../../../components/atoms' -import { FormInput, Modal, Toast } from '../../../components/mollecules' -import type { MoneyManagement } from '../../../generated/ManagingApi' -import { - MoneyManagementClient, - Timeframe, -} from '../../../generated/ManagingApi' -import type { - IMoneyManagementModalProps, - IMoneyManagementFormInput, -} from '../../../global/type' - -const MoneyManagementModal: React.FC = ({ - showModal, - onClose, - moneyManagement, - disableInputs = false, -}) => { - const [balanceAtRisk, setBalanceAtRisk] = useState(5) - const [takeProfit, setTakeProfit] = useState(20) - const [name, setName] = useState('') - const [stopLoss, setStopLoss] = useState(10) - const [leverage, setLeverage] = useState(1) - const [timeframe, setTimeframe] = useState( - Timeframe.FifteenMinutes - ) - const { reset, register, handleSubmit } = useForm() - const { apiUrl } = useApiUrlStore() - - async function createMoneyManagement(form: IMoneyManagementFormInput) { - const t = new Toast('Creating settings') - const client = new MoneyManagementClient({}, apiUrl) - const mm: MoneyManagement = { - balanceAtRisk: balanceAtRisk / 100, - leverage: leverage, - name: name, - stopLoss: stopLoss / 100, - takeProfit: takeProfit / 100, - timeframe: form.timeframe, - } - - await client - .moneyManagement_PostMoneyManagement(mm) - .then(() => { - t.update('success', 'Settings created') - }) - .catch((err) => { - t.update('error', 'Error :' + err) - }) - } - const onSubmit: SubmitHandler = async (form) => { - // @ts-ignore - await createMoneyManagement(form) - onClose() - } - - function onChangeName(e: any) { - setName(e.target.value) - } - - function onTakeProfitChange(e: any) { - setTakeProfit(e.target.value) - } - - function onStopLossChange(e: any) { - setStopLoss(e.target.value) - } - - function onLeverageChange(e: any) { - setLeverage(e.target.value) - } - - useEffect(() => { - if (moneyManagement) { - setBalanceAtRisk(moneyManagement.balanceAtRisk * 100) - setTakeProfit(moneyManagement.takeProfit * 100) - setStopLoss(moneyManagement.stopLoss * 100) - setLeverage(moneyManagement.leverage) - setTimeframe(moneyManagement.timeframe) - setName(moneyManagement.name) - - const defaultValues: MoneyManagement = { - balanceAtRisk: moneyManagement.balanceAtRisk, - leverage: moneyManagement.leverage, - name: moneyManagement.name || '', - stopLoss: moneyManagement.stopLoss, - takeProfit: moneyManagement.takeProfit, - timeframe: moneyManagement.timeframe, - } - reset({ ...defaultValues }) - } - }, [showModal, moneyManagement]) - - return ( - - - - - - - - - - - - - - - - - - - - - -
    -
    - -
    {(takeProfit / stopLoss).toFixed(2)}
    -
    -
    - {disableInputs ? null : ( -
    - -
    - )} -
    - ) -} - -export default MoneyManagementModal diff --git a/src/Managing.WebApp/src/pages/settingsPage/moneymanagement/moneymanagementTable.tsx b/src/Managing.WebApp/src/pages/settingsPage/moneymanagement/moneymanagementTable.tsx deleted file mode 100644 index 7cfea9e..0000000 --- a/src/Managing.WebApp/src/pages/settingsPage/moneymanagement/moneymanagementTable.tsx +++ /dev/null @@ -1,133 +0,0 @@ -import { PencilAltIcon, TrashIcon } from '@heroicons/react/solid' -import React, { useEffect, useState } from 'react' - -import useApiUrlStore from '../../../app/store/apiStore' -import { - Toast, - SelectColumnFilter, - Table, -} from '../../../components/mollecules' -import type { MoneyManagement } from '../../../generated/ManagingApi' -import { MoneyManagementClient } from '../../../generated/ManagingApi' -import type { IMoneyManagementList } from '../../../global/type' - -import MoneyManagementModal from './moneyManagementModal' - -const MoneyManagementTable: React.FC = ({ list }) => { - const [rows, setRows] = useState([]) - const [showModal, setShowModal] = useState(false) - const [selectedRow, setSelectedRow] = useState() - const { apiUrl } = useApiUrlStore() - - async function deleteMoneyManagement(name: string) { - const t = new Toast('Deleting money management') - const client = new MoneyManagementClient({}, apiUrl) - - await client - .moneyManagement_DeleteMoneyManagement(name) - .then(() => { - t.update('success', 'Configuration deleted') - }) - .catch((err) => { - t.update('error', 'Error :' + err) - }) - } - - function toggleModal() { - setShowModal(!showModal) - } - - function openModal(mm: MoneyManagement) { - setSelectedRow(mm) - setShowModal(true) - } - - const columns = React.useMemo( - () => [ - { - Filter: SelectColumnFilter, - Header: 'Timeframe', - accessor: 'timeframe', - disableSortBy: true, - }, - { - Cell: ({ cell }: any) => <>x{cell.row.values.leverage}, - Header: 'Leverage', - accessor: 'leverage', - disableFilters: true, - disableSortBy: true, - }, - { - Cell: ({ cell }: any) => <>{cell.row.values.balanceAtRisk * 100} %, - Header: 'Balance used', - accessor: 'balanceAtRisk', - disableFilters: true, - }, - { - Cell: ({ cell }) => <>{cell.row.values.stopLoss * 100} %, - Header: 'SL', - accessor: 'stopLoss', - disableFilters: true, - }, - { - Cell: ({ cell }) => <>{cell.row.values.takeProfit * 100} %, - Header: 'TP', - accessor: 'takeProfit', - disableFilters: true, - }, - { - Cell: ({ cell }) => ( - <> - {(cell.row.values.takeProfit / cell.row.values.stopLoss).toFixed(2)} - - ), - Header: 'R/R', - accessor: 'riskReward', - disableFilters: true, - }, - { - Cell: ({ cell }) => ( - <> -
    - -
    -
    - -
    - - ), - Header: 'Actions', - accessor: 'id', - disableFilters: true, - }, - ], - [] - ) - - useEffect(() => { - setRows(list) - }, [list]) - - return ( -
    -
    - - - ) -} - -export default MoneyManagementTable diff --git a/src/Managing.WebApp/src/pages/settingsPage/settings.tsx b/src/Managing.WebApp/src/pages/settingsPage/settings.tsx deleted file mode 100644 index 404d069..0000000 --- a/src/Managing.WebApp/src/pages/settingsPage/settings.tsx +++ /dev/null @@ -1,46 +0,0 @@ -import { useState } from 'react' - -import { Tabs } from '../../components/mollecules' - -import AccountSettings from './account/accountSettings' -import MoneyManagementSettings from './moneymanagement/moneyManagement' -import Theme from './theme' - -type TabsType = { - label: string - index: number - Component: React.FC<{}> -}[] - -// Tabs Array -const tabs: TabsType = [ - { - Component: MoneyManagementSettings, - index: 1, - label: 'Money Management', - }, - { - Component: AccountSettings, - index: 2, - label: 'Account Settings', - }, - { - Component: Theme, - index: 3, - label: 'Theme', - }, -] - -const Settings: React.FC = () => { - const [selectedTab, setSelectedTab] = useState(tabs[0].index) - - return ( -
    -
    - -
    -
    - ) -} - -export default Settings diff --git a/src/Managing.WebApp/src/pages/settingsPage/theme.tsx b/src/Managing.WebApp/src/pages/settingsPage/theme.tsx deleted file mode 100644 index a4b4515..0000000 --- a/src/Managing.WebApp/src/pages/settingsPage/theme.tsx +++ /dev/null @@ -1,14 +0,0 @@ -import { ThemeSelector } from '../../components/mollecules' - -const Theme: React.FC = () => { - return ( -
    -

    Settings

    -

    -

    Theme

    - -
    - ) -} - -export default Theme diff --git a/src/Managing.WebApp/src/pages/toolsPage/rektFees.tsx b/src/Managing.WebApp/src/pages/toolsPage/rektFees.tsx deleted file mode 100644 index 24f8f3c..0000000 --- a/src/Managing.WebApp/src/pages/toolsPage/rektFees.tsx +++ /dev/null @@ -1,158 +0,0 @@ -import { useEffect, useState } from 'react' -import type { SubmitHandler } from 'react-hook-form' -import { useForm } from 'react-hook-form' -import Plot from 'react-plotly.js' - -import useTheme from '../../hooks/useTheme' - -interface IRektToolInput { - fundingFeeRate: number - feePerTx: number - averageTradeTime: number -} - -const RektFees = () => { - const { register, handleSubmit } = useForm() - const [amountPerPosition, setAmountPerPosition] = useState([]) - const [z, setZ] = useState([]) - const [fundingFeesRate, setFundingFeesRate] = useState(0.0051) - const [feePerTx, setFeePerTx] = useState(0.13) - const [averageTradeTime, setAverageTradeTime] = useState(960) - const { themeProperty } = useTheme() - const theme = themeProperty() - const onSubmit: SubmitHandler = async (form) => { - setFundingFeesRate(form.fundingFeeRate) - setFeePerTx(form.feePerTx) - setAverageTradeTime(form.averageTradeTime) - } - - useEffect(() => { - updateAmountPerPosition() - updateAxis() - }, [fundingFeesRate, feePerTx, averageTradeTime]) - - function updateAmountPerPosition() { - const array: number[] = [] - for (let i = 300; i < 3500; i += 10) { - array.push(i) - } - setAmountPerPosition(array) - } - - function updateAxis() { - const tempZ: number[][] = [] - for (let i = 0; i < amountPerPosition.length; i++) { - const feePerPosition = getFeesForPosition(amountPerPosition[i]) - const zAxis: number[] = [] - - for (let t = 1; t < 1000; t++) { - const totalFees = feePerPosition * t - zAxis.push((totalFees * 100) / amountPerPosition[i]) - } - - tempZ.push(zAxis) - } - setZ(tempZ) - } - - function getFeesForPosition(amountPerPosition: number): number { - const tradeTime = averageTradeTime / 60 - const fundingFees = amountPerPosition * tradeTime * (fundingFeesRate / 100) - const positionFees = amountPerPosition * 0.0001 - const txFees = feePerTx * 2 // Open + SL or TP - - return fundingFees + txFees + positionFees - } - - return ( -
    -
    - - - - - - - -
    - ) -} -export default RektFees diff --git a/src/Managing.WebApp/src/pages/toolsPage/spotlight/spotlight.tsx b/src/Managing.WebApp/src/pages/toolsPage/spotlight/spotlight.tsx deleted file mode 100644 index 027928a..0000000 --- a/src/Managing.WebApp/src/pages/toolsPage/spotlight/spotlight.tsx +++ /dev/null @@ -1,76 +0,0 @@ -import moment from 'moment' -import { useEffect, useState } from 'react' - -import useApiUrlStore from '../../../app/store/apiStore' -import type { - Spotlight, - SpotlightOverview, -} from '../../../generated/ManagingApi' -import { DataClient } from '../../../generated/ManagingApi' - -import SpotlightSummary from './spotlightSummary' -import SpotlightTable from './spotlightTable' - -interface ISpotlightOverview { - overview: SpotlightOverview | undefined -} -const Overview: React.FC = ({ overview }) => { - return ( - <> -
    Last update : {moment(overview?.dateTime).fromNow()}
    -
    - -
    -
    - {overview?.spotlights.map((s) => ( - - ))} -
    - - ) -} - -interface ISpotlight { - spotlight: Spotlight -} -const SpotlightDetail: React.FC = ({ spotlight }) => { - return ( - <> -
    - -
    - - ) -} - -const SpotlightView = () => { - const [overview, setOverview] = useState() - const [isFetching, setIsFetching] = useState(true) - - const { apiUrl } = useApiUrlStore() - const dataClient = new DataClient({}, apiUrl) - - useEffect(() => { - dataClient - .data_GetSpotlight() - .then((data) => { - setOverview(data) - }) - .finally(() => { - setIsFetching(false) - }) - }, []) - - return ( - <> -
    - {isFetching ? ( - - ) : ( - - )} -
    - - ) -} -export default SpotlightView diff --git a/src/Managing.WebApp/src/pages/toolsPage/spotlight/spotlightSummary.tsx b/src/Managing.WebApp/src/pages/toolsPage/spotlight/spotlightSummary.tsx deleted file mode 100644 index 68557e9..0000000 --- a/src/Managing.WebApp/src/pages/toolsPage/spotlight/spotlightSummary.tsx +++ /dev/null @@ -1,210 +0,0 @@ -import React from 'react' - -import { PieChart } from '../../../components/mollecules' -import type { Signal, Spotlight } from '../../../generated/ManagingApi' -import { TradeDirection } from '../../../generated/ManagingApi' -import useTheme from '../../../hooks/useTheme' - -interface ISpotlightSummary { - spotlights: Spotlight[] | undefined -} - -interface ISummaryChart { - fiveMinutes: ISummaryChartItem - fifteenMinutes: ISummaryChartItem - oneHour: ISummaryChartItem - fourHours: ISummaryChartItem - oneDay: ISummaryChartItem -} - -interface ISummaryChartItem { - long: number - short: number - none: number -} - -const SpotlightSummary: React.FC = ({ spotlights }) => { - const { themeProperty } = useTheme() - const theme = themeProperty() - const labels = ['Long', 'Short', 'None'] - const colors = [theme.success, theme.error, 'grey'] - - function GetSummaryChartItem(signal: Signal) { - const summary: ISummaryChartItem = { - long: 0, - none: 0, - short: 0, - } - if (signal) { - switch (signal.direction) { - case TradeDirection.Long: - summary.long++ - break - case TradeDirection.Short: - summary.short++ - break - case TradeDirection.None: - summary.none++ - break - default: - summary.none++ - break - } - } else { - summary.none++ - } - return summary - } - - function GetSignalChart(): React.ReactNode { - const summary: ISummaryChart = { - fifteenMinutes: { - long: 0, - none: 0, - short: 0, - }, - fiveMinutes: { - long: 0, - none: 0, - short: 0, - }, - fourHours: { - long: 0, - none: 0, - short: 0, - }, - oneDay: { - long: 0, - none: 0, - short: 0, - }, - oneHour: { - long: 0, - none: 0, - short: 0, - }, - } - - if (spotlights) { - spotlights?.forEach((s) => { - s.tickerSignals.forEach((t) => { - const fiveMinutesSummary = GetSummaryChartItem( - t.fiveMinutes[t.fiveMinutes.length - 1] - ) - summary.fiveMinutes.long += fiveMinutesSummary.long - summary.fiveMinutes.short += fiveMinutesSummary.short - summary.fiveMinutes.none += fiveMinutesSummary.none - const fifteenMinutesSummary = GetSummaryChartItem( - t.fifteenMinutes[t.fifteenMinutes.length - 1] - ) - summary.fifteenMinutes.long += fifteenMinutesSummary.long - summary.fifteenMinutes.short += fifteenMinutesSummary.short - summary.fifteenMinutes.none += fifteenMinutesSummary.none - - const oneHour = GetSummaryChartItem(t.oneHour[t.oneHour.length - 1]) - summary.oneHour.long += oneHour.long - summary.oneHour.short += oneHour.short - summary.oneHour.none += oneHour.none - - const fourHours = GetSummaryChartItem( - t.fourHour[t.fourHour.length - 1] - ) - summary.fourHours.long += fourHours.long - summary.fourHours.short += fourHours.short - summary.fourHours.none += fourHours.none - - const oneDay = GetSummaryChartItem(t.oneDay[t.oneDay.length - 1]) - summary.oneDay.long += oneDay.long - summary.oneDay.short += oneDay.short - summary.oneDay.none += oneDay.none - }) - }) - } - - return ( - <> -
    -
    -
    5min
    -
    - -
    -
    -
    -
    15min
    -
    - -
    -
    -
    -
    1h
    -
    - -
    -
    -
    -
    4h
    -
    - -
    -
    -
    -
    1d
    -
    - -
    -
    -
    - - ) - } - - return ( -
    -
    - {GetSignalChart()} -
    - ) -} - -export default SpotlightSummary diff --git a/src/Managing.WebApp/src/pages/toolsPage/spotlight/spotlightTable.tsx b/src/Managing.WebApp/src/pages/toolsPage/spotlight/spotlightTable.tsx deleted file mode 100644 index cfd3f5c..0000000 --- a/src/Managing.WebApp/src/pages/toolsPage/spotlight/spotlightTable.tsx +++ /dev/null @@ -1,94 +0,0 @@ -import React from 'react' - -import { Table } from '../../../components/mollecules' -import { SpotLightBadge } from '../../../components/organism' -import type { Signal, Spotlight } from '../../../generated/ManagingApi' -import { TradeDirection } from '../../../generated/ManagingApi' - -interface ISpotlightTable { - spotlight: Spotlight -} -const GetBadgeForTimeframe = (signals: Signal[]) => { - const lastSignal = signals[signals.length > 1 ? signals.length - 1 : 0] - return lastSignal ? ( - - ) : ( - - ) -} - -const SpotlightTable: React.FC = ({ spotlight }) => { - const columns = React.useMemo( - () => [ - { - Header: spotlight.scenario.name, - columns: [ - { - Header: 'Ticker', - accessor: 'ticker', - disableFilters: true, - disableSortBy: true, - }, - { - Cell: ({ cell }: any) => ( - <>{GetBadgeForTimeframe(cell.row.values.fiveMinutes)} - ), - Header: '5m', - accessor: 'fiveMinutes', - disableFilters: true, - disableSortBy: true, - }, - { - Cell: ({ cell }: any) => ( - <>{GetBadgeForTimeframe(cell.row.values.fifteenMinutes)} - ), - Header: '15m', - accessor: 'fifteenMinutes', - disableFilters: true, - disableSortBy: true, - }, - { - Cell: ({ cell }: any) => ( - <>{GetBadgeForTimeframe(cell.row.values.oneHour)} - ), - Header: '1h', - accessor: 'oneHour', - disableFilters: true, - disableSortBy: true, - }, - { - Cell: ({ cell }: any) => ( - <>{GetBadgeForTimeframe(cell.row.values.fourHour)} - ), - Header: '4h', - accessor: 'fourHour', - disableFilters: true, - disableSortBy: true, - }, - { - Cell: ({ cell }: any) => ( - <>{GetBadgeForTimeframe(cell.row.values.oneDay)} - ), - Header: '1D', - accessor: 'oneDay', - disableFilters: true, - disableSortBy: true, - }, - ], - }, - ], - [] - ) - - return ( -
    -
    - - ) -} - -export default SpotlightTable diff --git a/src/Managing.WebApp/src/pages/toolsPage/tools.tsx b/src/Managing.WebApp/src/pages/toolsPage/tools.tsx deleted file mode 100644 index 94c9839..0000000 --- a/src/Managing.WebApp/src/pages/toolsPage/tools.tsx +++ /dev/null @@ -1,36 +0,0 @@ -import { useState, useEffect } from 'react' - -import { Tabs } from '../../components/mollecules' -import type { ITabsType } from '../../global/type' - -import RektFees from './rektFees' -import SpotlightView from './spotlight/spotlight' - -const tabs: ITabsType = [ - { - Component: SpotlightView, - index: 1, - label: 'Spotlight', - }, - { - Component: RektFees, - index: 2, - label: 'RektFees', - }, -] - -const Tools: React.FC = () => { - const [selectedTab, setSelectedTab] = useState(tabs[0].index) - - useEffect(() => {}, []) - - return ( -
    -
    - -
    -
    - ) -} - -export default Tools diff --git a/src/Managing.WebApp/src/pages/web3Page/web3.tsx b/src/Managing.WebApp/src/pages/web3Page/web3.tsx deleted file mode 100644 index fb60113..0000000 --- a/src/Managing.WebApp/src/pages/web3Page/web3.tsx +++ /dev/null @@ -1,104 +0,0 @@ -import type { Contract } from 'ethers' -import { ethers } from 'ethers' -import { useRef, useState } from 'react' - -const Web3 = () => { - const moodInputRef = useRef() - const [mood, setMood] = useState() - //@ts-ignore - const provider = new ethers.providers.Web3Provider(window.ethereum, 'ropsten') - const contractAddress = '0x0335e801159Af04b3067bED0aeeaCC86Ece51e19' - const moodAbi = [ - { - constant: true, - inputs: [], - name: 'getMood', - outputs: [ - { - internalType: 'string', - name: '', - type: 'string', - }, - ], - signature: '0x9d0c1397', - stateMutability: 'view', - type: 'function', - }, - { - inputs: [ - { - internalType: 'string', - name: '_mood', - type: 'string', - }, - ], - name: 'setMood', - outputs: [], - signature: '0x5f3cbff5', - stateMutability: 'nonpayable', - type: 'function', - }, - ] - - let contract: Contract - let signer - - provider.send('eth_requestAccounts', []).then(() => { - provider.listAccounts().then(function (accounts) { - signer = provider.getSigner(accounts[0]) - contract = new ethers.Contract(contractAddress, moodAbi, signer) - }) - }) - - async function getMoodAbi() { - const getMoodPromise = contract.getMood() - const currentMood = await getMoodPromise - setMood(currentMood) - } - - async function setMoodAbi() { - //@ts-ignore - const setMoodPromise = contract.setMood(moodInputRef.current.value) - await setMoodPromise - } - - return ( -
    -
    -

    Web3 Playground

    -
    -
    -

    dApp

    - - - - -
    -
    -

    {mood}

    -
    - -
    -
    -
    -
    -
    -

    NFT

    -
    -
    -
    -
    - ) -} - -export default Web3 diff --git a/src/Managing.WebApp/src/pages/workflow/workflows.tsx b/src/Managing.WebApp/src/pages/workflow/workflows.tsx deleted file mode 100644 index 205b728..0000000 --- a/src/Managing.WebApp/src/pages/workflow/workflows.tsx +++ /dev/null @@ -1,98 +0,0 @@ -import { useQuery } from '@tanstack/react-query' -import { useEffect, useState } from 'react' -import type { Edge, Node } from 'reactflow' - -import useApiUrlStore from '../../app/store/apiStore' -import { Loader } from '../../components/atoms' -import { Tabs } from '../../components/mollecules' -import { WorkflowCanvas } from '../../components/organism' -import type { - IFlow, - SyntheticFlow, - SyntheticWorkflow, -} from '../../generated/ManagingApi' -import { WorkflowClient } from '../../generated/ManagingApi' -import type { ITabsType, IWorkflow } from '../../global/type' - -const mapWorkflowToTabs = (workflows: SyntheticWorkflow[]): ITabsType => { - return workflows.map((workflow: SyntheticWorkflow, index: number) => { - return { - Component: WorkflowCanvas, - index: index, - label: workflow.name, - props: mapFlowsToNodes(workflow.flows, workflow.name), - } - }) -} - -const mapFlowsToNodes = (flows: SyntheticFlow[], name: string): IWorkflow => { - const nodes: Node[] = [] - const edges: Edge[] = [] - - flows.forEach((flow: SyntheticFlow) => { - nodes.push(mapFlowToNode(flow)) - }) - - for (const node of nodes) { - const childrenNodes = nodes.filter((n) => n.data.parentId == node.data.id) - - if (childrenNodes.length > 0) { - childrenNodes.forEach((childNode) => { - edges.push({ - id: `${node.id}-${childNode.id}`, - source: node.id, - target: childNode.id, - }) - }) - } - } - - return { edges, name: name, nodes } as IWorkflow -} - -const mapFlowToNode = (flow: SyntheticFlow): Node => { - return { - data: flow, - id: flow.id, - position: { x: 0, y: 0 }, - type: flow.type, - } -} - -const Workflows: React.FC = () => { - const [selectedTab, setSelectedTab] = useState(1) - const { apiUrl } = useApiUrlStore() - const client = new WorkflowClient({}, apiUrl) - - const { data, isLoading } = useQuery({ - onSuccess: () => { - setSelectedTab(0) - }, - queryFn: () => client.workflow_GetWorkflows(), - queryKey: ['workflows'], - }) - - useEffect(() => {}, [isLoading]) - - if (isLoading || data == null) { - return - } - - return ( -
    -
    - { - console.log('add button clicked') - }} - /> -
    -
    - ) -} - -export default Workflows diff --git a/src/Managing.WebApp/src/polyfills.ts b/src/Managing.WebApp/src/polyfills.ts deleted file mode 100644 index dc3665c..0000000 --- a/src/Managing.WebApp/src/polyfills.ts +++ /dev/null @@ -1,4 +0,0 @@ -window.global = window.global ?? window -window.process = window.process ?? { env: {} } - -export {} diff --git a/src/Managing.WebApp/src/smartcontracts/courses/mood.sol b/src/Managing.WebApp/src/smartcontracts/courses/mood.sol deleted file mode 100644 index 68f90b7..0000000 --- a/src/Managing.WebApp/src/smartcontracts/courses/mood.sol +++ /dev/null @@ -1,16 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.1; - -contract MoodDiary{ - string mood; - - //create a function that writes a mood to the smart contract - function setMood(string memory _mood) public{ - mood = _mood; - } - - //create a function the reads the mood from the smart contract - function getMood() public view returns(string memory){ - return mood; - } - } \ No newline at end of file diff --git a/src/Managing.WebApp/src/smartcontracts/courses/odaNFT.sol b/src/Managing.WebApp/src/smartcontracts/courses/odaNFT.sol deleted file mode 100644 index 378a154..0000000 --- a/src/Managing.WebApp/src/smartcontracts/courses/odaNFT.sol +++ /dev/null @@ -1,14 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.0; - -// Import the openzepplin contracts -import "@openzeppelin/contracts/token/ERC721/ERC721.sol"; - -// GameItem is ERC721 signifies that the contract we are creating imports ERC721 and follows ERC721 contract from openzeppelin -contract GameItem is ERC721 { - - constructor() ERC721("GameItem", "ITM") { - // mint an NFT to yourself - _mint(msg.sender, 1); - } -} \ No newline at end of file diff --git a/src/Managing.WebApp/src/smartcontracts/courses/odaToken.sol b/src/Managing.WebApp/src/smartcontracts/courses/odaToken.sol deleted file mode 100644 index 64743a8..0000000 --- a/src/Managing.WebApp/src/smartcontracts/courses/odaToken.sol +++ /dev/null @@ -1,10 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.0; - -import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/ERC20.sol"; - -contract OdaToken is ERC20 { - constructor(string memory _name, string memory _symbol) ERC20(_name, _symbol) { - _mint(msg.sender, 10 * 10 ** 18); - } -} \ No newline at end of file diff --git a/src/Managing.WebApp/src/stores/store.tsx b/src/Managing.WebApp/src/stores/store.tsx deleted file mode 100644 index 81c5d23..0000000 --- a/src/Managing.WebApp/src/stores/store.tsx +++ /dev/null @@ -1,3 +0,0 @@ -import { atomWithStorage } from 'jotai/utils' - -export const themeAtom = atomWithStorage('theme', 'black') diff --git a/src/Managing.WebApp/src/styles/app.css b/src/Managing.WebApp/src/styles/app.css deleted file mode 100644 index 89065d8..0000000 --- a/src/Managing.WebApp/src/styles/app.css +++ /dev/null @@ -1,46 +0,0 @@ -.App-logo { - width: 1.5em; -} - -@media (prefers-reduced-motion: no-preference) { - /* .App-logo { - animation: App-logo-spin infinite 20s linear; - } */ -} - -@keyframes App-logo-spin { - from { - transform: rotate(0deg); - } - to { - transform: rotate(360deg); - } -} - -button { - font-size: calc(10px + 2vmin); -} - -.notificationWrapper { - @apply w-96 hover:shadow-none transform-gpu hover:translate-y-1 rounded-xl relative flex flex-row items-center justify-between px-4 py-6 text-white transition-all duration-500 ease-in-out translate-y-0 bg-gray-900 shadow-2xl; -} - -.iconWrapper { - @apply text-xl; -} - -.contentWrapper { - @apply flex flex-col items-start justify-center ml-4 cursor-default; -} - -.contentWrapper h1 { - @apply text-base font-semibold leading-none tracking-wider text-gray-200; -} - -.contentWrapper p { - @apply mt-2 text-sm leading-relaxed tracking-wider text-gray-400; -} - -.closeIcon { - @apply top-2 right-2 absolute text-lg cursor-pointer; -} diff --git a/src/Managing.WebApp/src/styles/globals.css b/src/Managing.WebApp/src/styles/globals.css deleted file mode 100644 index da51981..0000000 --- a/src/Managing.WebApp/src/styles/globals.css +++ /dev/null @@ -1,23 +0,0 @@ -@tailwind base; -@tailwind components; -@tailwind utilities; - -@layer base { - body { - margin: 0; - - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; - } - - code { - font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', - monospace; - } -} - -@layer utilities { - .layout { - @apply sm:w-11/12 w-10/12 mx-auto; - } -} diff --git a/src/Managing.WebApp/src/vite-env.d.ts b/src/Managing.WebApp/src/vite-env.d.ts deleted file mode 100644 index 96204cc..0000000 --- a/src/Managing.WebApp/src/vite-env.d.ts +++ /dev/null @@ -1,3 +0,0 @@ -/// - -declare type AnyFunction = (...args: any[]) => any diff --git a/src/Managing.WebApp/tailwind.config.js b/src/Managing.WebApp/tailwind.config.js deleted file mode 100644 index c57aa29..0000000 --- a/src/Managing.WebApp/tailwind.config.js +++ /dev/null @@ -1,12 +0,0 @@ -module.exports = { - content: ['./index.html', './src/**/*.{js,ts,jsx,tsx}'], - daisyui: { - themes: true, - }, - plugins: [require('@tailwindcss/typography'), require('daisyui')], - theme: { - container: { - center: true, - }, - }, -} diff --git a/src/Managing.WebApp/tsconfig.json b/src/Managing.WebApp/tsconfig.json deleted file mode 100644 index 9f749f4..0000000 --- a/src/Managing.WebApp/tsconfig.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "compilerOptions": { - "target": "ESNext", - "useDefineForClassFields": true, - "lib": ["DOM", "DOM.Iterable", "ESNext"], - "allowJs": true, - "skipLibCheck": true, - "esModuleInterop": true, - "allowSyntheticDefaultImports": true, - "strict": true, - "forceConsistentCasingInFileNames": true, - "module": "ESNext", - "moduleResolution": "Node", - "resolveJsonModule": true, - "isolatedModules": true, - "noEmit": true, - "jsx": "react-jsx" - }, - "include": ["./src", "hardhat.config.js"], - "exclude": ["node_modules"] -} diff --git a/src/Managing.WebApp/vite.config.ts b/src/Managing.WebApp/vite.config.ts deleted file mode 100644 index 449a7b8..0000000 --- a/src/Managing.WebApp/vite.config.ts +++ /dev/null @@ -1,20 +0,0 @@ -import react from '@vitejs/plugin-react' -import { defineConfig } from 'vite' - -export default defineConfig({ - build: { - sourcemap: true, - target: 'es2020', - }, - optimizeDeps: { - esbuildOptions: { - target: 'es2020', - }, - }, - plugins: [react()], - publicDir: 'assets', - server: { - host: true, - open: true, - }, -})