├── .dockerignore ├── .gitignore ├── src ├── components │ ├── forms │ │ ├── keyValueInput │ │ │ ├── style.css │ │ │ └── index.js │ │ ├── asterisk │ │ │ └── index.js │ │ ├── textinput │ │ │ ├── style.css │ │ │ └── index.js │ │ ├── dropdown │ │ │ └── index.js │ │ ├── passwordinput │ │ │ └── index.js │ │ └── fileInput │ │ │ └── index.js │ ├── createAPIMonitor │ │ └── keyValueForm │ │ │ ├── style.css │ │ │ └── index.js │ ├── redirect │ │ └── index.js │ ├── loadingPage │ │ └── index.js │ ├── sideBar │ │ └── style.css │ ├── successPage │ │ └── index.js │ ├── APIMonitorEditor │ │ ├── style.css │ │ └── optionHelper.js │ ├── dashboardWrapper │ │ ├── style.css │ │ └── index.js │ ├── success_rate │ │ ├── style.css │ │ └── index.js │ ├── invalidPage │ │ └── index.js │ ├── chart │ │ ├── ChartConfig.js │ │ ├── response_time_chart │ │ │ └── index.js │ │ └── success_rate_percentage_chart │ │ │ └── index.js │ ├── teamEditor │ │ └── index.js │ ├── alertComponent │ │ └── index.js │ ├── teamMemberComponent │ │ └── index.js │ └── app.js ├── index.js ├── assets │ ├── favicon.ico │ ├── icons │ │ ├── favicon-16x16.png │ │ ├── favicon-32x32.png │ │ ├── mstile-150x150.png │ │ ├── apple-touch-icon.png │ │ ├── android-chrome-192x192.png │ │ └── logo-monapi.svg │ └── fonts │ │ └── Open_Sans │ │ ├── .DS_Store │ │ ├── static │ │ ├── .DS_Store │ │ ├── OpenSans │ │ │ ├── OpenSans-Bold.ttf │ │ │ ├── OpenSans-Italic.ttf │ │ │ ├── OpenSans-Light.ttf │ │ │ ├── OpenSans-Medium.ttf │ │ │ ├── OpenSans-Regular.ttf │ │ │ ├── OpenSans-ExtraBold.ttf │ │ │ ├── OpenSans-SemiBold.ttf │ │ │ ├── OpenSans-BoldItalic.ttf │ │ │ ├── OpenSans-LightItalic.ttf │ │ │ ├── OpenSans-MediumItalic.ttf │ │ │ ├── OpenSans-SemiBoldItalic.ttf │ │ │ └── OpenSans-ExtraBoldItalic.ttf │ │ ├── OpenSans_Condensed │ │ │ ├── OpenSans_Condensed-Bold.ttf │ │ │ ├── OpenSans_Condensed-Italic.ttf │ │ │ ├── OpenSans_Condensed-Light.ttf │ │ │ ├── OpenSans_Condensed-Medium.ttf │ │ │ ├── OpenSans_Condensed-Regular.ttf │ │ │ ├── OpenSans_Condensed-ExtraBold.ttf │ │ │ ├── OpenSans_Condensed-SemiBold.ttf │ │ │ ├── OpenSans_Condensed-BoldItalic.ttf │ │ │ ├── OpenSans_Condensed-LightItalic.ttf │ │ │ ├── OpenSans_Condensed-MediumItalic.ttf │ │ │ ├── OpenSans_Condensed-SemiBoldItalic.ttf │ │ │ └── OpenSans_Condensed-ExtraBoldItalic.ttf │ │ └── OpenSans_SemiCondensed │ │ │ ├── OpenSans_SemiCondensed-Bold.ttf │ │ │ ├── OpenSans_SemiCondensed-Italic.ttf │ │ │ ├── OpenSans_SemiCondensed-Light.ttf │ │ │ ├── OpenSans_SemiCondensed-Medium.ttf │ │ │ ├── OpenSans_SemiCondensed-ExtraBold.ttf │ │ │ ├── OpenSans_SemiCondensed-Regular.ttf │ │ │ ├── OpenSans_SemiCondensed-SemiBold.ttf │ │ │ ├── OpenSans_SemiCondensed-BoldItalic.ttf │ │ │ ├── OpenSans_SemiCondensed-LightItalic.ttf │ │ │ ├── OpenSans_SemiCondensed-MediumItalic.ttf │ │ │ ├── OpenSans_SemiCondensed-SemiBoldItalic.ttf │ │ │ └── OpenSans_SemiCondensed-ExtraBoldItalic.ttf │ │ ├── OpenSans-VariableFont_wdth,wght.ttf │ │ ├── OpenSans-Italic-VariableFont_wdth,wght.ttf │ │ ├── README.txt │ │ └── OFL.txt ├── routes │ ├── home │ │ ├── style.css │ │ └── index.js │ ├── verifyUser │ │ ├── style.css │ │ └── index.js │ ├── acceptInvite │ │ ├── style.css │ │ └── index.js │ ├── view_current_team │ │ └── style.css │ ├── editAPIMonitor │ │ └── index.js │ ├── createAPIMonitor │ │ └── index.js │ ├── a_test_api │ │ ├── optionHelper.js │ │ └── style.css │ ├── statusPageDashboard │ │ ├── style.css │ │ └── index.js │ ├── configuration │ │ ├── style.css │ │ └── optionHelper.js │ ├── view_api_monitor_detail │ │ ├── style.css │ │ └── index.js │ ├── error_logs │ │ └── style.css │ ├── view_list_monitor │ │ ├── style.css │ │ └── index.js │ ├── forgetPasswordToken │ │ └── index.js │ ├── editTeam │ │ └── index.js │ ├── createTeam │ │ └── index.js │ ├── forgetPassword │ │ └── index.js │ └── register │ │ └── index.js ├── config │ ├── api │ │ ├── constant.js │ │ ├── auth.js │ │ └── route.js │ ├── context │ │ └── index.js │ ├── middleware │ │ ├── axios.js │ │ └── middleware.js │ └── theme │ │ └── index.js ├── sw.js ├── manifest.json ├── template.html └── style │ └── index.css ├── .env.example ├── tests ├── __mocks__ │ ├── fileMocks.js │ ├── setupTests.js │ └── browserMocks.js ├── transform │ └── svgTransform.js ├── utils │ └── setup.js ├── routes │ ├── home │ │ └── home.test.js │ ├── verifyUser │ │ └── verifyUser.test.js │ ├── acceptInvite │ │ └── acceptInvite.test.js │ ├── createTeam │ │ └── createTeam.test.js │ ├── forgetPasswordToken │ │ └── forgetPasswordToken.test.js │ ├── forget_password │ │ └── forget_password.test.js │ ├── editTeam │ │ └── editTeam.test.js │ ├── configuration │ │ └── configuration.test.js │ └── login │ │ └── login.test.js ├── config │ ├── theme │ │ └── theme.test.js │ ├── middleware │ │ └── middleware.test.js │ └── api │ │ └── auth.test.js ├── components │ ├── forms │ │ ├── asterisk │ │ │ └── asterisk.test.js │ │ └── passwordinput │ │ │ └── passwordinput.test.js │ ├── app.test.js │ ├── redirect │ │ └── index.test.js │ ├── dashboardWrapper │ │ └── dashboardWrapper.test.js │ ├── chart │ │ ├── response_time_chart │ │ │ └── response_time_chart.test.js │ │ └── success_rate_percentage_chart │ │ │ └── success_rate_percentage_chart.test.js │ ├── teamMemberComponent │ │ └── teamMemberComponent.test.js │ ├── success_rate │ │ └── success_rate.test.js │ └── alertComponent │ │ └── alertComponent.test.js └── middleware │ └── axios.test.js ├── docs ├── development.md ├── docker.md └── release_notes.md ├── nginx └── default.conf ├── .github └── workflows │ ├── test.yml │ ├── staging.yml │ ├── production.yml │ └── build.yml ├── pull_request_template.md ├── sonar-project.properties ├── jest.config.js ├── target └── npmlist.json ├── Dockerfile ├── package.json └── README.md /.dockerignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | /build 3 | /*.log 4 | /coverage 5 | .env -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | /build 3 | /*.log 4 | /coverage 5 | .env 6 | -------------------------------------------------------------------------------- /src/components/forms/keyValueInput/style.css: -------------------------------------------------------------------------------- 1 | .key-value-input{ 2 | margin: 10px 0px; 3 | } -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import './style'; 2 | import App from './components/app'; 3 | 4 | export default App; 5 | -------------------------------------------------------------------------------- /src/assets/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MonAPI-xyz/MonAPI-Frontend/HEAD/src/assets/favicon.ico -------------------------------------------------------------------------------- /src/routes/home/style.css: -------------------------------------------------------------------------------- 1 | .home { 2 | padding: 56px 20px; 3 | min-height: 100%; 4 | width: 100%; 5 | } 6 | -------------------------------------------------------------------------------- /src/config/api/constant.js: -------------------------------------------------------------------------------- 1 | 2 | const BASE_URL = process.env.PREACT_APP_BACKEND_URL; 3 | 4 | export default BASE_URL; -------------------------------------------------------------------------------- /.env.example: -------------------------------------------------------------------------------- 1 | PREACT_APP_BACKEND_URL=http://localhost:8000 2 | PREACT_APP_SENTRY_DSN= 3 | PREACT_APP_GOOGLE_ANALYTICS_TAG=G-WLQ8M6KE7D -------------------------------------------------------------------------------- /src/assets/icons/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MonAPI-xyz/MonAPI-Frontend/HEAD/src/assets/icons/favicon-16x16.png -------------------------------------------------------------------------------- /src/assets/icons/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MonAPI-xyz/MonAPI-Frontend/HEAD/src/assets/icons/favicon-32x32.png -------------------------------------------------------------------------------- /src/assets/icons/mstile-150x150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MonAPI-xyz/MonAPI-Frontend/HEAD/src/assets/icons/mstile-150x150.png -------------------------------------------------------------------------------- /src/assets/fonts/Open_Sans/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MonAPI-xyz/MonAPI-Frontend/HEAD/src/assets/fonts/Open_Sans/.DS_Store -------------------------------------------------------------------------------- /src/assets/icons/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MonAPI-xyz/MonAPI-Frontend/HEAD/src/assets/icons/apple-touch-icon.png -------------------------------------------------------------------------------- /src/assets/fonts/Open_Sans/static/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MonAPI-xyz/MonAPI-Frontend/HEAD/src/assets/fonts/Open_Sans/static/.DS_Store -------------------------------------------------------------------------------- /src/assets/icons/android-chrome-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MonAPI-xyz/MonAPI-Frontend/HEAD/src/assets/icons/android-chrome-192x192.png -------------------------------------------------------------------------------- /src/config/context/index.js: -------------------------------------------------------------------------------- 1 | import { createContext } from "preact"; 2 | 3 | const UserContext = createContext(null) 4 | 5 | export {UserContext} -------------------------------------------------------------------------------- /src/sw.js: -------------------------------------------------------------------------------- 1 | import { getFiles, setupPrecaching, setupRouting } from 'preact-cli/sw/'; 2 | 3 | setupRouting(); 4 | setupPrecaching(getFiles()); 5 | -------------------------------------------------------------------------------- /src/routes/verifyUser/style.css: -------------------------------------------------------------------------------- 1 | .login-button{ 2 | width: max-content; 3 | display: block; 4 | margin-left: auto; 5 | margin-right: auto; 6 | } -------------------------------------------------------------------------------- /src/routes/acceptInvite/style.css: -------------------------------------------------------------------------------- 1 | .login-button{ 2 | width: max-content; 3 | display: block; 4 | margin-left: auto; 5 | margin-right: auto; 6 | } -------------------------------------------------------------------------------- /src/assets/fonts/Open_Sans/static/OpenSans/OpenSans-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MonAPI-xyz/MonAPI-Frontend/HEAD/src/assets/fonts/Open_Sans/static/OpenSans/OpenSans-Bold.ttf -------------------------------------------------------------------------------- /src/assets/fonts/Open_Sans/OpenSans-VariableFont_wdth,wght.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MonAPI-xyz/MonAPI-Frontend/HEAD/src/assets/fonts/Open_Sans/OpenSans-VariableFont_wdth,wght.ttf -------------------------------------------------------------------------------- /src/assets/fonts/Open_Sans/static/OpenSans/OpenSans-Italic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MonAPI-xyz/MonAPI-Frontend/HEAD/src/assets/fonts/Open_Sans/static/OpenSans/OpenSans-Italic.ttf -------------------------------------------------------------------------------- /src/assets/fonts/Open_Sans/static/OpenSans/OpenSans-Light.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MonAPI-xyz/MonAPI-Frontend/HEAD/src/assets/fonts/Open_Sans/static/OpenSans/OpenSans-Light.ttf -------------------------------------------------------------------------------- /src/assets/fonts/Open_Sans/static/OpenSans/OpenSans-Medium.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MonAPI-xyz/MonAPI-Frontend/HEAD/src/assets/fonts/Open_Sans/static/OpenSans/OpenSans-Medium.ttf -------------------------------------------------------------------------------- /src/assets/fonts/Open_Sans/static/OpenSans/OpenSans-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MonAPI-xyz/MonAPI-Frontend/HEAD/src/assets/fonts/Open_Sans/static/OpenSans/OpenSans-Regular.ttf -------------------------------------------------------------------------------- /src/assets/fonts/Open_Sans/static/OpenSans/OpenSans-ExtraBold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MonAPI-xyz/MonAPI-Frontend/HEAD/src/assets/fonts/Open_Sans/static/OpenSans/OpenSans-ExtraBold.ttf -------------------------------------------------------------------------------- /src/assets/fonts/Open_Sans/static/OpenSans/OpenSans-SemiBold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MonAPI-xyz/MonAPI-Frontend/HEAD/src/assets/fonts/Open_Sans/static/OpenSans/OpenSans-SemiBold.ttf -------------------------------------------------------------------------------- /src/assets/fonts/Open_Sans/static/OpenSans/OpenSans-BoldItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MonAPI-xyz/MonAPI-Frontend/HEAD/src/assets/fonts/Open_Sans/static/OpenSans/OpenSans-BoldItalic.ttf -------------------------------------------------------------------------------- /src/assets/fonts/Open_Sans/static/OpenSans/OpenSans-LightItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MonAPI-xyz/MonAPI-Frontend/HEAD/src/assets/fonts/Open_Sans/static/OpenSans/OpenSans-LightItalic.ttf -------------------------------------------------------------------------------- /src/assets/fonts/Open_Sans/static/OpenSans/OpenSans-MediumItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MonAPI-xyz/MonAPI-Frontend/HEAD/src/assets/fonts/Open_Sans/static/OpenSans/OpenSans-MediumItalic.ttf -------------------------------------------------------------------------------- /src/assets/fonts/Open_Sans/OpenSans-Italic-VariableFont_wdth,wght.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MonAPI-xyz/MonAPI-Frontend/HEAD/src/assets/fonts/Open_Sans/OpenSans-Italic-VariableFont_wdth,wght.ttf -------------------------------------------------------------------------------- /src/assets/fonts/Open_Sans/static/OpenSans/OpenSans-SemiBoldItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MonAPI-xyz/MonAPI-Frontend/HEAD/src/assets/fonts/Open_Sans/static/OpenSans/OpenSans-SemiBoldItalic.ttf -------------------------------------------------------------------------------- /src/assets/fonts/Open_Sans/static/OpenSans/OpenSans-ExtraBoldItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MonAPI-xyz/MonAPI-Frontend/HEAD/src/assets/fonts/Open_Sans/static/OpenSans/OpenSans-ExtraBoldItalic.ttf -------------------------------------------------------------------------------- /src/assets/fonts/Open_Sans/static/OpenSans_Condensed/OpenSans_Condensed-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MonAPI-xyz/MonAPI-Frontend/HEAD/src/assets/fonts/Open_Sans/static/OpenSans_Condensed/OpenSans_Condensed-Bold.ttf -------------------------------------------------------------------------------- /src/assets/fonts/Open_Sans/static/OpenSans_Condensed/OpenSans_Condensed-Italic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MonAPI-xyz/MonAPI-Frontend/HEAD/src/assets/fonts/Open_Sans/static/OpenSans_Condensed/OpenSans_Condensed-Italic.ttf -------------------------------------------------------------------------------- /src/assets/fonts/Open_Sans/static/OpenSans_Condensed/OpenSans_Condensed-Light.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MonAPI-xyz/MonAPI-Frontend/HEAD/src/assets/fonts/Open_Sans/static/OpenSans_Condensed/OpenSans_Condensed-Light.ttf -------------------------------------------------------------------------------- /src/assets/fonts/Open_Sans/static/OpenSans_Condensed/OpenSans_Condensed-Medium.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MonAPI-xyz/MonAPI-Frontend/HEAD/src/assets/fonts/Open_Sans/static/OpenSans_Condensed/OpenSans_Condensed-Medium.ttf -------------------------------------------------------------------------------- /src/assets/fonts/Open_Sans/static/OpenSans_Condensed/OpenSans_Condensed-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MonAPI-xyz/MonAPI-Frontend/HEAD/src/assets/fonts/Open_Sans/static/OpenSans_Condensed/OpenSans_Condensed-Regular.ttf -------------------------------------------------------------------------------- /src/assets/fonts/Open_Sans/static/OpenSans_Condensed/OpenSans_Condensed-ExtraBold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MonAPI-xyz/MonAPI-Frontend/HEAD/src/assets/fonts/Open_Sans/static/OpenSans_Condensed/OpenSans_Condensed-ExtraBold.ttf -------------------------------------------------------------------------------- /src/assets/fonts/Open_Sans/static/OpenSans_Condensed/OpenSans_Condensed-SemiBold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MonAPI-xyz/MonAPI-Frontend/HEAD/src/assets/fonts/Open_Sans/static/OpenSans_Condensed/OpenSans_Condensed-SemiBold.ttf -------------------------------------------------------------------------------- /tests/__mocks__/fileMocks.js: -------------------------------------------------------------------------------- 1 | // This fixed an error related to the CSS and loading gif breaking my Jest test 2 | // See https://facebook.github.io/jest/docs/en/webpack.html#handling-static-assets 3 | module.exports = 'test-file-stub'; -------------------------------------------------------------------------------- /src/assets/fonts/Open_Sans/static/OpenSans_Condensed/OpenSans_Condensed-BoldItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MonAPI-xyz/MonAPI-Frontend/HEAD/src/assets/fonts/Open_Sans/static/OpenSans_Condensed/OpenSans_Condensed-BoldItalic.ttf -------------------------------------------------------------------------------- /src/assets/fonts/Open_Sans/static/OpenSans_Condensed/OpenSans_Condensed-LightItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MonAPI-xyz/MonAPI-Frontend/HEAD/src/assets/fonts/Open_Sans/static/OpenSans_Condensed/OpenSans_Condensed-LightItalic.ttf -------------------------------------------------------------------------------- /src/assets/fonts/Open_Sans/static/OpenSans_Condensed/OpenSans_Condensed-MediumItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MonAPI-xyz/MonAPI-Frontend/HEAD/src/assets/fonts/Open_Sans/static/OpenSans_Condensed/OpenSans_Condensed-MediumItalic.ttf -------------------------------------------------------------------------------- /src/assets/fonts/Open_Sans/static/OpenSans_SemiCondensed/OpenSans_SemiCondensed-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MonAPI-xyz/MonAPI-Frontend/HEAD/src/assets/fonts/Open_Sans/static/OpenSans_SemiCondensed/OpenSans_SemiCondensed-Bold.ttf -------------------------------------------------------------------------------- /src/routes/home/index.js: -------------------------------------------------------------------------------- 1 | import { h } from 'preact'; 2 | import style from './style.css'; 3 | 4 | const Home = () => ( 5 |
6 |

MonAPI

7 |
8 | ); 9 | 10 | export default Home; 11 | -------------------------------------------------------------------------------- /src/assets/fonts/Open_Sans/static/OpenSans_Condensed/OpenSans_Condensed-SemiBoldItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MonAPI-xyz/MonAPI-Frontend/HEAD/src/assets/fonts/Open_Sans/static/OpenSans_Condensed/OpenSans_Condensed-SemiBoldItalic.ttf -------------------------------------------------------------------------------- /src/assets/fonts/Open_Sans/static/OpenSans_SemiCondensed/OpenSans_SemiCondensed-Italic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MonAPI-xyz/MonAPI-Frontend/HEAD/src/assets/fonts/Open_Sans/static/OpenSans_SemiCondensed/OpenSans_SemiCondensed-Italic.ttf -------------------------------------------------------------------------------- /src/assets/fonts/Open_Sans/static/OpenSans_SemiCondensed/OpenSans_SemiCondensed-Light.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MonAPI-xyz/MonAPI-Frontend/HEAD/src/assets/fonts/Open_Sans/static/OpenSans_SemiCondensed/OpenSans_SemiCondensed-Light.ttf -------------------------------------------------------------------------------- /src/assets/fonts/Open_Sans/static/OpenSans_SemiCondensed/OpenSans_SemiCondensed-Medium.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MonAPI-xyz/MonAPI-Frontend/HEAD/src/assets/fonts/Open_Sans/static/OpenSans_SemiCondensed/OpenSans_SemiCondensed-Medium.ttf -------------------------------------------------------------------------------- /docs/development.md: -------------------------------------------------------------------------------- 1 | ## How to start development server 2 | 1. Clone this project to your local machine 3 | 2. Copy `.env.example` file into `.env` 4 | 2. Run `npm install` to download all depedencies 5 | 3. Run `npm run dev` to start local server -------------------------------------------------------------------------------- /src/assets/fonts/Open_Sans/static/OpenSans_Condensed/OpenSans_Condensed-ExtraBoldItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MonAPI-xyz/MonAPI-Frontend/HEAD/src/assets/fonts/Open_Sans/static/OpenSans_Condensed/OpenSans_Condensed-ExtraBoldItalic.ttf -------------------------------------------------------------------------------- /src/assets/fonts/Open_Sans/static/OpenSans_SemiCondensed/OpenSans_SemiCondensed-ExtraBold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MonAPI-xyz/MonAPI-Frontend/HEAD/src/assets/fonts/Open_Sans/static/OpenSans_SemiCondensed/OpenSans_SemiCondensed-ExtraBold.ttf -------------------------------------------------------------------------------- /src/assets/fonts/Open_Sans/static/OpenSans_SemiCondensed/OpenSans_SemiCondensed-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MonAPI-xyz/MonAPI-Frontend/HEAD/src/assets/fonts/Open_Sans/static/OpenSans_SemiCondensed/OpenSans_SemiCondensed-Regular.ttf -------------------------------------------------------------------------------- /src/assets/fonts/Open_Sans/static/OpenSans_SemiCondensed/OpenSans_SemiCondensed-SemiBold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MonAPI-xyz/MonAPI-Frontend/HEAD/src/assets/fonts/Open_Sans/static/OpenSans_SemiCondensed/OpenSans_SemiCondensed-SemiBold.ttf -------------------------------------------------------------------------------- /src/assets/fonts/Open_Sans/static/OpenSans_SemiCondensed/OpenSans_SemiCondensed-BoldItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MonAPI-xyz/MonAPI-Frontend/HEAD/src/assets/fonts/Open_Sans/static/OpenSans_SemiCondensed/OpenSans_SemiCondensed-BoldItalic.ttf -------------------------------------------------------------------------------- /src/assets/fonts/Open_Sans/static/OpenSans_SemiCondensed/OpenSans_SemiCondensed-LightItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MonAPI-xyz/MonAPI-Frontend/HEAD/src/assets/fonts/Open_Sans/static/OpenSans_SemiCondensed/OpenSans_SemiCondensed-LightItalic.ttf -------------------------------------------------------------------------------- /tests/transform/svgTransform.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | process() { 3 | return { code: 'module.exports = {};' }; 4 | }, 5 | getCacheKey() { 6 | // The output is always the same. 7 | return 'svgTransform'; 8 | }, 9 | }; -------------------------------------------------------------------------------- /src/assets/fonts/Open_Sans/static/OpenSans_SemiCondensed/OpenSans_SemiCondensed-MediumItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MonAPI-xyz/MonAPI-Frontend/HEAD/src/assets/fonts/Open_Sans/static/OpenSans_SemiCondensed/OpenSans_SemiCondensed-MediumItalic.ttf -------------------------------------------------------------------------------- /src/assets/fonts/Open_Sans/static/OpenSans_SemiCondensed/OpenSans_SemiCondensed-SemiBoldItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MonAPI-xyz/MonAPI-Frontend/HEAD/src/assets/fonts/Open_Sans/static/OpenSans_SemiCondensed/OpenSans_SemiCondensed-SemiBoldItalic.ttf -------------------------------------------------------------------------------- /src/assets/fonts/Open_Sans/static/OpenSans_SemiCondensed/OpenSans_SemiCondensed-ExtraBoldItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MonAPI-xyz/MonAPI-Frontend/HEAD/src/assets/fonts/Open_Sans/static/OpenSans_SemiCondensed/OpenSans_SemiCondensed-ExtraBoldItalic.ttf -------------------------------------------------------------------------------- /src/components/createAPIMonitor/keyValueForm/style.css: -------------------------------------------------------------------------------- 1 | .key-value-wrapper{ 2 | display: flex; 3 | flex-direction: row; 4 | align-items: center; 5 | } 6 | 7 | .key-value-close-icon { 8 | margin: 0px 10px; 9 | cursor: pointer; 10 | } -------------------------------------------------------------------------------- /src/routes/view_current_team/style.css: -------------------------------------------------------------------------------- 1 | .team-details { 2 | display: flex; 3 | flex-direction: row; 4 | } 5 | 6 | .team-management-header { 7 | display: flex; 8 | flex-direction: row; 9 | justify-content: space-between; 10 | } 11 | -------------------------------------------------------------------------------- /tests/utils/setup.js: -------------------------------------------------------------------------------- 1 | import { render } from '@testing-library/preact'; 2 | import userEvent from '@testing-library/user-event' 3 | // setup function 4 | export const setup = (jsx) => { 5 | return { 6 | user: userEvent.setup(), 7 | ...render(jsx), 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/components/redirect/index.js: -------------------------------------------------------------------------------- 1 | import { route } from 'preact-router'; 2 | import { useEffect } from 'preact/hooks'; 3 | 4 | const Redirect = ({to}) => { 5 | useEffect(()=> { 6 | route(to, true); 7 | }) 8 | return null; 9 | } 10 | 11 | export default Redirect; 12 | -------------------------------------------------------------------------------- /tests/routes/home/home.test.js: -------------------------------------------------------------------------------- 1 | import { h } from 'preact'; 2 | import { render, screen} from '@testing-library/preact'; 3 | import Home from '../../../src/routes/home/index.js'; 4 | 5 | describe('Test Home', () => { 6 | 7 | test('setup home', async () => { 8 | render(); 9 | expect(screen.getByText('MonAPI')); 10 | }); 11 | 12 | }); -------------------------------------------------------------------------------- /src/components/forms/asterisk/index.js: -------------------------------------------------------------------------------- 1 | import { h, Fragment } from 'preact'; 2 | import { Box } from "@chakra-ui/react"; 3 | 4 | const Asterisk = ({isRequired}) => { 5 | return isRequired ? ( 6 | 7 | * 8 | 9 | ) : ( 10 | 11 | ); 12 | } 13 | export default Asterisk; -------------------------------------------------------------------------------- /nginx/default.conf: -------------------------------------------------------------------------------- 1 | server { 2 | listen 80; 3 | listen [::]:80; 4 | 5 | root /var/www/monapife; 6 | index index.html index.htm index.nginx-debian.html; 7 | 8 | # Use global server name to make sure we are able to serve 9 | # any domain from docker container 10 | server_name _; 11 | 12 | location / { 13 | try_files $uri /index.html; 14 | } 15 | } -------------------------------------------------------------------------------- /src/routes/editAPIMonitor/index.js: -------------------------------------------------------------------------------- 1 | import { h } from 'preact'; 2 | import APIMonitorEditor from '../../components/APIMonitorEditor/index.js'; 3 | 4 | const EditAPIMonitor = ({id}) => { 5 | return 11 | } 12 | 13 | export default EditAPIMonitor; -------------------------------------------------------------------------------- /tests/config/theme/theme.test.js: -------------------------------------------------------------------------------- 1 | import { h } from 'preact'; 2 | import { ChakraProvider } from '@chakra-ui/react'; 3 | import theme from '../../../src/config/theme'; 4 | import { render } from '@testing-library/preact'; 5 | 6 | describe('Test Theme', () => { 7 | 8 | test('setup theme', async () => { 9 | render(); 10 | }); 11 | 12 | }); -------------------------------------------------------------------------------- /src/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "MonAPI", 3 | "short_name": "monapi", 4 | "start_url": "/", 5 | "display": "standalone", 6 | "orientation": "portrait", 7 | "background_color": "#fff", 8 | "theme_color": "#673ab8", 9 | "icons": [ 10 | { 11 | "src": "/assets/icons/android-chrome-192x192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | } 15 | ] 16 | } -------------------------------------------------------------------------------- /src/routes/createAPIMonitor/index.js: -------------------------------------------------------------------------------- 1 | import { h } from 'preact'; 2 | import APIMonitorEditor from '../../components/APIMonitorEditor/index.js'; 3 | 4 | const CreateAPIMonitor = () => { 5 | return 11 | } 12 | 13 | 14 | export default CreateAPIMonitor; -------------------------------------------------------------------------------- /docs/docker.md: -------------------------------------------------------------------------------- 1 | ## How to run this repository as container (Docker) 2 | 1. Build docker image by running `docker build -t /monapife --build-arg BACKEND_URL= .` 3 | 2. To start docker image, run `docker run -d -p 80:80 --name monapife /monapife` 4 | 5 | Notes: 6 | * You are required to build your own docker image due to environment variables that need injected on build process 7 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Test 2 | on: 3 | push: 4 | branches: 5 | - '**' 6 | pull_request: 7 | types: [opened, synchronize, reopened] 8 | jobs: 9 | test: 10 | name: Test 11 | runs-on: ubuntu-20.04 12 | steps: 13 | - uses: actions/checkout@v3 14 | - uses: actions/setup-node@v3 15 | with: 16 | node-version: '14' 17 | - run: | 18 | npm install 19 | npm run test -------------------------------------------------------------------------------- /src/components/loadingPage/index.js: -------------------------------------------------------------------------------- 1 | import { h } from 'preact'; 2 | import { Box, Text, Spinner } from '@chakra-ui/react'; 3 | 4 | function LoadingPage(){ 5 | return ( 6 | 7 | Loading 8 | 9 | We are Accessing Your Request 10 | 11 | ) 12 | } 13 | 14 | export default LoadingPage; -------------------------------------------------------------------------------- /pull_request_template.md: -------------------------------------------------------------------------------- 1 | ### Describe your changes 2 | Describe what changes in this PR 3 | 4 | ### Backlog name 5 | Give name of backlog / task this PR relates to 6 | 7 | ### Checklist 8 | - [ ] I have performed a self-review of my code 9 | - [ ] Code successfully run on local 10 | - [ ] Feature / changes tested on local 11 | - [ ] No failing unit test 12 | - [ ] Passed UAT Test 13 | - [ ] Sonarqube tested 14 | - [ ] Required any changes to environment variable 15 | -------------------------------------------------------------------------------- /src/routes/a_test_api/optionHelper.js: -------------------------------------------------------------------------------- 1 | const methodOption = [ 2 | { 3 | key: "GET", 4 | value: "GET", 5 | }, 6 | { 7 | key: "POST", 8 | value: "POST", 9 | }, 10 | { 11 | key: "PUT", 12 | value: "PUT", 13 | }, 14 | { 15 | key: "PATCH", 16 | value: "PATCH", 17 | }, 18 | { 19 | key: "DELETE", 20 | value: "DELETE", 21 | }, 22 | ] 23 | 24 | export {methodOption}; -------------------------------------------------------------------------------- /tests/__mocks__/setupTests.js: -------------------------------------------------------------------------------- 1 | import { configure } from 'enzyme'; 2 | import Adapter from 'enzyme-adapter-preact-pure'; 3 | import 'jest-canvas-mock'; 4 | 5 | configure({ 6 | adapter: new Adapter() 7 | }); 8 | 9 | const noop = () => {}; 10 | 11 | Object.defineProperty(window, 'scrollTo', { value: noop, writable: true }); 12 | 13 | global.ResizeObserver = jest.fn().mockImplementation(() => ({ 14 | observe: jest.fn(), 15 | unobserve: jest.fn(), 16 | disconnect: jest.fn(), 17 | })) -------------------------------------------------------------------------------- /src/config/middleware/axios.js: -------------------------------------------------------------------------------- 1 | import axios from "axios"; 2 | import { route } from "preact-router"; 3 | import { deleteUserToken, getUserToken } from "../api/auth"; 4 | 5 | // Axios Intercept 401 / 403 Unauthorized 6 | axios.interceptors.response.use((response) => { 7 | return response; 8 | }, (error) => { 9 | if ((401 === error.response.status || 403 == error.response.status) && getUserToken() !== null) { 10 | deleteUserToken(); 11 | route('/login'); 12 | } 13 | return Promise.reject(error); 14 | }); 15 | -------------------------------------------------------------------------------- /src/routes/statusPageDashboard/style.css: -------------------------------------------------------------------------------- 1 | .status-page-category-item { 2 | display: flex; 3 | flex-direction: column; 4 | align-items: flex-start; 5 | border: 1px solid #efefef; 6 | padding: 15px 15px; 7 | } 8 | 9 | .status-page-category-item:first-child{ 10 | border-top-left-radius: 10px; 11 | border-top-right-radius: 10px; 12 | } 13 | 14 | .status-page-category-item:last-child{ 15 | border-bottom-left-radius: 10px; 16 | border-bottom-right-radius: 10px; 17 | } 18 | 19 | .spinner-container{ 20 | 21 | } -------------------------------------------------------------------------------- /tests/components/forms/asterisk/asterisk.test.js: -------------------------------------------------------------------------------- 1 | import { h } from 'preact'; 2 | import { render, waitFor} from '@testing-library/preact'; 3 | import Asterisk from '../../../../src/components/forms/asterisk/index.js'; 4 | 5 | describe('Test Asterisk', () => { 6 | 7 | test('not required asterisk', async () => { 8 | const { container } = render( 9 | ); 12 | 13 | await waitFor( ()=> { 14 | expect(container.innerHTML).toEqual('') 15 | }) 16 | }); 17 | }) -------------------------------------------------------------------------------- /src/components/forms/textinput/style.css: -------------------------------------------------------------------------------- 1 | .input { 2 | display: flex; 3 | flex-direction: column; 4 | } 5 | 6 | .input-title { 7 | margin: 10px 0px; 8 | font-size: 14px; 9 | } 10 | 11 | .input-box { 12 | background-color: #f1f1f1; 13 | border: 0px; 14 | padding: 8px 10px; 15 | border-radius: 10px; 16 | font-size: 14px; 17 | display: flex; 18 | flex-direction: row; 19 | align-items: center; 20 | width: 100%; 21 | } 22 | 23 | .input-box.invalid { 24 | border: 1px solid #E11900 ; 25 | background-color: #FFEFED; 26 | } -------------------------------------------------------------------------------- /src/config/api/auth.js: -------------------------------------------------------------------------------- 1 | const getUserToken = () => { 2 | if (typeof window !== "undefined") { 3 | return localStorage.getItem('MONAPI_TOKEN'); 4 | } 5 | return null; 6 | } 7 | 8 | const setUserToken = (token) => { 9 | if (typeof window !== "undefined") { 10 | localStorage.setItem('MONAPI_TOKEN', token); 11 | } 12 | } 13 | 14 | const deleteUserToken = () => { 15 | if (typeof window !== "undefined") { 16 | localStorage.removeItem('MONAPI_TOKEN'); 17 | } 18 | } 19 | 20 | export { getUserToken, setUserToken, deleteUserToken } -------------------------------------------------------------------------------- /src/routes/a_test_api/style.css: -------------------------------------------------------------------------------- 1 | .noselect { 2 | -webkit-touch-callout: none; /* iOS Safari */ 3 | -webkit-user-select: none; /* Safari */ 4 | -khtml-user-select: none; /* Konqueror HTML */ 5 | -moz-user-select: none; /* Old versions of Firefox */ 6 | -ms-user-select: none; /* Internet Explorer/Edge */ 7 | user-select: none; /* Non-prefixed version, currently 8 | supported by Chrome, Edge, Opera and Firefox */ 9 | cursor: pointer; 10 | } 11 | 12 | .test-api { 13 | padding: 40px 20px; 14 | } -------------------------------------------------------------------------------- /tests/__mocks__/browserMocks.js: -------------------------------------------------------------------------------- 1 | // Mock Browser API's which are not supported by JSDOM, e.g. ServiceWorker, LocalStorage 2 | /** 3 | * An example how to mock localStorage is given below 👇 4 | */ 5 | 6 | /* 7 | // Mocks localStorage 8 | const localStorageMock = (function() { 9 | let store = {}; 10 | 11 | return { 12 | getItem: (key) => store[key] || null, 13 | setItem: (key, value) => store[key] = value.toString(), 14 | clear: () => store = {} 15 | }; 16 | 17 | })(); 18 | 19 | Object.defineProperty(window, 'localStorage', { 20 | value: localStorageMock 21 | }); */ 22 | -------------------------------------------------------------------------------- /tests/components/app.test.js: -------------------------------------------------------------------------------- 1 | import { h } from 'preact'; 2 | import { render } from '@testing-library/preact'; 3 | import App from '../../src/components/app.js'; 4 | 5 | describe('Test App', () => { 6 | beforeEach(() => { 7 | jest.resetModules() 8 | }); 9 | 10 | test('setup app', async () => { 11 | render(); 12 | }); 13 | 14 | test('setup app without sentry', async () => { 15 | const prevEnv = process.env.PREACT_APP_SENTRY_DSN; 16 | process.env.PREACT_APP_SENTRY_DSN = ""; 17 | 18 | render() 19 | 20 | process.env.PREACT_APP_SENTRY_DSN = prevEnv; 21 | 22 | }) 23 | }); -------------------------------------------------------------------------------- /tests/config/middleware/middleware.test.js: -------------------------------------------------------------------------------- 1 | import { h } from 'preact'; 2 | import { waitFor, render } from '@testing-library/preact'; 3 | import { getCurrentUrl, route } from 'preact-router'; 4 | import App from '../../../src/components/app.js'; 5 | import { deleteUserToken } from '../../../src/config/api/auth.js'; 6 | 7 | 8 | describe('Test Routes Register', () => { 9 | test('alow to route to register', async () => { 10 | deleteUserToken() 11 | render(); 12 | route('/register') 13 | 14 | await waitFor(() => { 15 | expect(getCurrentUrl()).toBe('/register'); 16 | }) 17 | }) 18 | }); -------------------------------------------------------------------------------- /src/config/theme/index.js: -------------------------------------------------------------------------------- 1 | import { extendTheme, theme as base } from '@chakra-ui/react'; 2 | 3 | // 2. Call `extendTheme` and pass your custom values 4 | const theme = extendTheme({ 5 | fonts: { 6 | heading: `Open Sans, ${base.fonts?.heading}`, 7 | body: `Open Sans, ${base.fonts?.body}`, 8 | }, 9 | breakpoints: { 10 | xs: '10em', 11 | sm: '30em', 12 | md: '48em', 13 | lg: '62em', 14 | xl: '80em', 15 | '2xl': '96em', 16 | '4xl': '118em', 17 | }, 18 | colors: { 19 | blueChill: { 20 | 500: '#4B8F8C', 21 | } 22 | } 23 | }); 24 | 25 | export default theme; 26 | -------------------------------------------------------------------------------- /sonar-project.properties: -------------------------------------------------------------------------------- 1 | sonar.projectKey=MonAPI-xyz_MonAPI-Frontend 2 | sonar.organization=monapi-xyz 3 | 4 | # This is the name and version displayed in the SonarCloud UI. 5 | #sonar.projectName=MonAPI-Frontend 6 | #sonar.projectVersion=1.0 7 | 8 | # Path is relative to the sonar-project.properties file. Replace "\" by "/" on Windows. 9 | #sonar.sources=. 10 | 11 | # Encoding of the source code. Default is default system encoding 12 | #sonar.sourceEncoding=UTF-8 13 | sonar.coverage.exclusions=**/tests/**, src/sw.js, jest.config.js 14 | sonar.exclusions=tests/**/*.test.js 15 | 16 | sonar.javascript.lcov.reportPaths=coverage/lcov.info -------------------------------------------------------------------------------- /src/components/sideBar/style.css: -------------------------------------------------------------------------------- 1 | .menu-button { 2 | color: black!important; 3 | } 4 | 5 | .menu-button.active{ 6 | color: #226666!important; 7 | font-weight: 700; 8 | } 9 | 10 | .team-list-accordion{ 11 | max-height: 120px; 12 | overflow-y: scroll; 13 | z-index: 99; 14 | width: 100%; 15 | margin-inline-start: 0px!important; 16 | margin-inline-end: 0px!important; 17 | border-bottom-left-radius: 10px; 18 | border-bottom-right-radius: 10px; 19 | } 20 | 21 | .menu-item-active{ 22 | color: #226666!important; 23 | font-weight: 700; 24 | } 25 | 26 | .team-accordion{ 27 | position: relative; 28 | } -------------------------------------------------------------------------------- /src/routes/configuration/style.css: -------------------------------------------------------------------------------- 1 | .home { 2 | padding: 56px 20px; 3 | min-height: 100%; 4 | width: 100%; 5 | } 6 | 7 | .noselect { 8 | -webkit-touch-callout: none; /* iOS Safari */ 9 | -webkit-user-select: none; /* Safari */ 10 | -khtml-user-select: none; /* Konqueror HTML */ 11 | -moz-user-select: none; /* Old versions of Firefox */ 12 | -ms-user-select: none; /* Internet Explorer/Edge */ 13 | user-select: none; /* Non-prefixed version, currently 14 | supported by Chrome, Edge, Opera and Firefox */ 15 | cursor: pointer; 16 | } 17 | 18 | .alert-configuration { 19 | padding: 40px 20px; 20 | } -------------------------------------------------------------------------------- /src/components/successPage/index.js: -------------------------------------------------------------------------------- 1 | import { h } from 'preact'; 2 | import { Box, Heading, Text } from '@chakra-ui/react'; 3 | import { CheckCircleIcon } from '@chakra-ui/icons'; 4 | 5 | function SuccessPage({headMessage, bodyMessage1, bodyMessage2}){ 6 | return ( 7 | 8 | 9 | 10 | {headMessage} 11 | 12 | 13 | {bodyMessage1}
14 | {bodyMessage2} 15 |
16 |
17 | ) 18 | } 19 | 20 | export default SuccessPage; -------------------------------------------------------------------------------- /src/components/APIMonitorEditor/style.css: -------------------------------------------------------------------------------- 1 | .noselect { 2 | -webkit-touch-callout: none; /* iOS Safari */ 3 | -webkit-user-select: none; /* Safari */ 4 | -khtml-user-select: none; /* Konqueror HTML */ 5 | -moz-user-select: none; /* Old versions of Firefox */ 6 | -ms-user-select: none; /* Internet Explorer/Edge */ 7 | user-select: none; /* Non-prefixed version, currently 8 | supported by Chrome, Edge, Opera and Firefox */ 9 | cursor: pointer; 10 | } 11 | 12 | .new-api-monitor { 13 | padding: 40px 20px; 14 | } 15 | 16 | .add-new-category-button{ 17 | color: teal; 18 | cursor: pointer; 19 | margin: 5px 0px; 20 | } -------------------------------------------------------------------------------- /src/components/dashboardWrapper/style.css: -------------------------------------------------------------------------------- 1 | .dashboard { 2 | width: 100%; 3 | display: flex; 4 | flex-direction: row; 5 | background-color: white; 6 | } 7 | 8 | .dashboard-content { 9 | flex: 1; 10 | width: calc(100% - 270px); 11 | transition: width 0.3s; 12 | min-height: 100vh; 13 | } 14 | 15 | .dashboard-content-sidebar-collapse { 16 | width: calc(100% - 80px); 17 | } 18 | 19 | .dashboard-sidebar{ 20 | width: 270px; 21 | flex: 0 0 auto; 22 | transition: width 0.3s; 23 | } 24 | 25 | .dashboard-sidebar-collapse { 26 | width: 80px; 27 | } 28 | 29 | .dashboard-inner-sidebar{ 30 | position:fixed; 31 | background-color: white; 32 | z-index: 99; 33 | } -------------------------------------------------------------------------------- /src/config/middleware/middleware.js: -------------------------------------------------------------------------------- 1 | import { h } from "preact" 2 | import { route } from "preact-router" 3 | import { useEffect } from "react" 4 | import { getUserToken } from "../api/auth" 5 | import ROUTE from "../api/route" 6 | 7 | const AuthenticationChecker = ({children}) => { 8 | const isAuthenticated = isAuthenticate(); 9 | 10 | useEffect(()=>{ 11 | if (!isAuthenticated) { 12 | route(ROUTE.LOGIN, true) 13 | } 14 | }, [isAuthenticated]) 15 | 16 | return (
17 | {isAuthenticated && children} 18 |
) 19 | } 20 | 21 | function isAuthenticate() { 22 | return (getUserToken() != null) 23 | } 24 | 25 | export {AuthenticationChecker, isAuthenticate} -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | const jestConfig = { 2 | preset: "jest-preset-preact", 3 | setupFiles: [ 4 | "/tests/__mocks__/browserMocks.js", 5 | "/tests/__mocks__/setupTests.js" 6 | ], 7 | verbose: true, 8 | testMatch: [ 9 | '/src/**/__tests__/**/*.{mjs,js,jsx,ts,tsx}', 10 | '/{src,test,tests}/**/*.{spec,test}.{mjs,js,jsx,ts,tsx}', 11 | '/tests/**.test.js', 12 | '/tests/*/**.test.js', 13 | '/tests/*/*/**.test.js', 14 | '/tests/*/*/*/**.test.js' 15 | ], 16 | transform: { 17 | "^.+\\.svg$": "/tests/transform/svgTransform.js" 18 | } 19 | } 20 | 21 | module.exports = jestConfig -------------------------------------------------------------------------------- /src/config/api/route.js: -------------------------------------------------------------------------------- 1 | const ROUTE = { 2 | DASHBOARD: '/', 3 | LOGIN: '/login', 4 | REGISTER: '/register', 5 | LOGOUT: '/logout', 6 | DETAIL: '/:id/detail', 7 | ERROR_LOGS: '/error-logs', 8 | CREATE_API_MONITOR: '/create', 9 | EDIT: '/:id/edit', 10 | CONFIGURATION: '/configuration', 11 | FORGET_PASSWORD: '/forget', 12 | FORGET_PASSWORD_TOKEN: '/forget_password', 13 | TEST_API: '/test-api', 14 | TEAM_MANAGEMENT: '/team-management', 15 | VIEW_CURRENT_TEAM: '/team-management/current', 16 | TEAM_MANAGEMENT_EDIT: '/:id/team-management', 17 | ACCEPT_INVITE: '/invite-member', 18 | STATUS_PAGE_DASHBOARD: 'status/:path', 19 | STATUS_PAGE: '/status-page', 20 | VERIFY: '/verify' 21 | }; 22 | 23 | export default ROUTE; 24 | -------------------------------------------------------------------------------- /src/routes/view_api_monitor_detail/style.css: -------------------------------------------------------------------------------- 1 | .home { 2 | padding: 56px 20px; 3 | min-height: 100%; 4 | width: 100%; 5 | } 6 | 7 | .container-flex-column { 8 | display: flex; 9 | flex-direction: column; 10 | margin-top: 20px; 11 | margin-bottom: 20px; 12 | } 13 | 14 | .requestItem { 15 | padding: 10px; 16 | } 17 | 18 | .dropdownOption { 19 | width: 150px; 20 | } 21 | 22 | .container-dropdown-time { 23 | width: 200px; 24 | margin-left: auto; 25 | margin-bottom: 30px; 26 | } 27 | 28 | .chart-container { 29 | display: flex; 30 | flex-direction: row; 31 | } 32 | 33 | .chart { 34 | width: 50%; 35 | padding: 10px; 36 | position: relative; 37 | } 38 | 39 | .spinner-container { 40 | padding: 50px; 41 | display: flex; 42 | flex-direction: row; 43 | justify-content: center; 44 | } 45 | -------------------------------------------------------------------------------- /tests/components/redirect/index.test.js: -------------------------------------------------------------------------------- 1 | import { h } from 'preact'; 2 | import { render, waitFor } from '@testing-library/preact'; 3 | import { getCurrentUrl, route } from 'preact-router'; 4 | import * as axios from 'axios'; 5 | 6 | import App from "../../../src/components/app"; 7 | import { setUserToken } from "../../../src/config/api/auth"; 8 | 9 | jest.mock('axios'); 10 | 11 | describe('Test default redirect', () => { 12 | test('try to access invalid routes then redirect to login', async () => { 13 | const response = [] 14 | axios.get.mockImplementation(() => Promise.resolve({data: response})) 15 | 16 | setUserToken('sometoken') 17 | 18 | render(); 19 | route('/some_invalid_routes') 20 | 21 | await waitFor(() => { 22 | expect(getCurrentUrl()).toBe("/") 23 | }) 24 | }) 25 | }); -------------------------------------------------------------------------------- /src/template.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | <% preact.title %> 6 | 7 | 8 | 9 | 10 | 11 | 12 | <% preact.headEnd %> 13 | 14 | 15 | <% preact.bodyEnd %> 16 | 17 | 18 | -------------------------------------------------------------------------------- /target/npmlist.json: -------------------------------------------------------------------------------- 1 | {"version":"0.0.0","name":"monapi","dependencies":{"@babel/preset-env":{"version":"7.19.1"},"@babel/preset-react":{"version":"7.18.6"},"@chakra-ui/icons":{"version":"2.0.11"},"@chakra-ui/react":{"version":"2.3.4"},"@emotion/react":{"version":"11.10.4"},"@emotion/styled":{"version":"11.10.4"},"@sentry/react":{"version":"7.17.3"},"@sentry/tracing":{"version":"7.17.3"},"@testing-library/jest-dom":{"version":"5.16.5"},"@testing-library/user-event":{"version":"13.5.0"},"axios":{"version":"0.21.2"},"chart.js":{"version":"3.9.1"},"framer-motion":{"version":"7.4.0"},"moment":{"version":"2.29.4"},"preact-global-state":{"version":"1.0.5"},"preact-render-to-string":{"version":"5.2.4"},"preact-router":{"version":"3.2.1"},"preact":{"version":"10.11.0"},"react-hook-form":{"version":"7.36.0"},"react-icons":{"version":"4.4.0"},"react-pro-sidebar":{"version":"0.7.1"},"react":{"version":"18.2.0"}}} -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # Pull official base image 2 | FROM node:14.20-alpine3.15 3 | 4 | # Store arguments for build environment 5 | ARG BACKEND_URL 6 | 7 | # Install nginx 8 | RUN apk add --update nginx gcc musl-dev 9 | RUN rm /etc/nginx/http.d/default.conf 10 | COPY nginx/default.conf /etc/nginx/http.d/monapife.conf 11 | 12 | # Create directory for the app 13 | RUN mkdir -p /home/monapife 14 | WORKDIR /home/monapife 15 | 16 | # Copy and install depedency 17 | COPY package.json /home/monapife 18 | RUN npm install 19 | 20 | # Copy file and build production 21 | COPY . /home/monapife 22 | RUN PREACT_APP_BACKEND_URL=${BACKEND_URL} \ 23 | npm run build 24 | 25 | # Copy build file to www folder 26 | RUN mkdir -p /var/www/monapife 27 | RUN cp -r build/. /var/www/monapife 28 | 29 | # Remove repository files 30 | RUN rm -rf /home/monapife 31 | 32 | # Expose nginx port and run service 33 | EXPOSE 80 34 | CMD ["nginx", "-g", "daemon off;"] -------------------------------------------------------------------------------- /src/components/dashboardWrapper/index.js: -------------------------------------------------------------------------------- 1 | import { h } from 'preact'; 2 | import SideBar from '../sideBar'; 3 | import style from './style.css'; 4 | import { useState } from 'preact/hooks'; 5 | 6 | const DashboardWrapper = ({children}) => { 7 | const [menuCollapse, setMenuCollapse] = useState(false) 8 | 9 | return ( 10 |
11 |
12 |
13 | 14 |
15 |
16 |
17 | {children} 18 |
19 |
20 | ) 21 | } 22 | 23 | export default DashboardWrapper; -------------------------------------------------------------------------------- /.github/workflows/staging.yml: -------------------------------------------------------------------------------- 1 | name: Staging Deployment 2 | on: 3 | push: 4 | branches: 5 | - staging 6 | jobs: 7 | test: 8 | runs-on: ubuntu-20.04 9 | steps: 10 | - uses: actions/checkout@v3 11 | - uses: actions/setup-node@v3 12 | with: 13 | node-version: '14' 14 | - run: | 15 | npm install 16 | npm run test 17 | deployment: 18 | needs: test 19 | runs-on: ubuntu-20.04 20 | environment: staging 21 | steps: 22 | - run: | 23 | command -v ssh-agent >/dev/null 24 | apt-get update -y && apt-get install openssh-client -y 25 | eval `ssh-agent -s` && echo "${{ secrets.SSH_PRIVATE_KEY }}" | tr -d '\r' | ssh-add - 26 | mkdir -p ~/.ssh 27 | chmod 700 ~/.ssh 28 | ssh-keyscan -H ${{ secrets.SERVER_IP }} >> ~/.ssh/known_hosts 29 | ssh root@${{ secrets.SERVER_IP }} "set -x && ./deploy_frontend_staging.sh && exit" 30 | -------------------------------------------------------------------------------- /.github/workflows/production.yml: -------------------------------------------------------------------------------- 1 | name: Production Deployment 2 | on: 3 | push: 4 | branches: 5 | - main 6 | jobs: 7 | test: 8 | runs-on: ubuntu-20.04 9 | steps: 10 | - uses: actions/checkout@v3 11 | - uses: actions/setup-node@v3 12 | with: 13 | node-version: '14' 14 | - run: | 15 | npm install 16 | npm run test 17 | deployment: 18 | needs: test 19 | runs-on: ubuntu-20.04 20 | environment: production 21 | steps: 22 | - run: | 23 | command -v ssh-agent >/dev/null 24 | apt-get update -y && apt-get install openssh-client -y 25 | eval `ssh-agent -s` && echo "${{ secrets.SSH_PRIVATE_KEY }}" | tr -d '\r' | ssh-add - 26 | mkdir -p ~/.ssh 27 | chmod 700 ~/.ssh 28 | ssh-keyscan -H ${{ secrets.SERVER_IP }} >> ~/.ssh/known_hosts 29 | ssh root@${{ secrets.SERVER_IP }} "set -x && ./deploy_frontend_production.sh && exit" 30 | -------------------------------------------------------------------------------- /src/components/success_rate/style.css: -------------------------------------------------------------------------------- 1 | .home { 2 | padding: 56px 20px; 3 | min-height: 100%; 4 | width: 100%; 5 | } 6 | 7 | .green-bar { 8 | margin-top: 1px; 9 | margin-bottom: 2px; 10 | margin-left: 3px; 11 | margin-right: 4px; 12 | height:25px; 13 | width:7px; 14 | background-color:green; 15 | border-radius: 2px; 16 | } 17 | 18 | .red-bar { 19 | margin-top: 1px; 20 | margin-bottom: 2px; 21 | margin-left: 3px; 22 | margin-right: 4px; 23 | height:25px; 24 | width:7px; 25 | background-color:red; 26 | border-radius: 2px; 27 | border-color: black; 28 | } 29 | 30 | .grey-bar { 31 | margin-top: 1px; 32 | margin-bottom: 2px; 33 | margin-left: 3px; 34 | margin-right: 4px; 35 | height:25px; 36 | width:7px; 37 | background-color:grey; 38 | border-radius: 2px; 39 | } 40 | 41 | .yellow-bar { 42 | margin-top: 1px; 43 | margin-bottom: 2px; 44 | margin-left: 3px; 45 | margin-right: 4px; 46 | height:25px; 47 | width:7px; 48 | background-color:yellow; 49 | border-radius: 2px; 50 | } -------------------------------------------------------------------------------- /src/components/success_rate/index.js: -------------------------------------------------------------------------------- 1 | import { h, Fragment } from 'preact'; 2 | import style from './style.css'; 3 | 4 | const SuccessRate = ({success,failed, width = '7px', height = '25px' })=> { 5 | return ( 6 | <> 7 | {(success == 0 && failed == 0)? 8 |
9 | : 10 | <> 11 | {(failed == 0)? 12 |
13 | : 14 | <> 15 | {(success == 0)? 16 |
17 | : 18 |
19 | } 20 | 21 | } 22 | 23 | } 24 | 25 | ) 26 | } 27 | 28 | export default SuccessRate; 29 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build 2 | on: 3 | push: 4 | branches: 5 | - main 6 | - staging 7 | pull_request: 8 | types: [opened, synchronize, reopened] 9 | jobs: 10 | sonarcloud: 11 | name: SonarCloud 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@v2 15 | with: 16 | fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis 17 | - uses: actions/setup-node@v3 18 | with: 19 | node-version: '14' 20 | - run: | 21 | npm install 22 | npm run test 23 | - name: Override Coverage Source Path for Sonar 24 | run: sed -i "s/SF:\/home\/runner\/work\/MonAPI-Frontend\/MonAPI-Frontend/SF:\/github\/workspace/g" /home/runner/work/MonAPI-Frontend/MonAPI-Frontend/coverage/lcov.info 25 | - name: SonarCloud Scan 26 | uses: SonarSource/sonarcloud-github-action@master 27 | env: 28 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Needed to get PR information, if any 29 | SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} 30 | -------------------------------------------------------------------------------- /src/components/forms/textinput/index.js: -------------------------------------------------------------------------------- 1 | import { h } from 'preact'; 2 | import { FormControl, FormLabel, FormErrorMessage, Box } from '@chakra-ui/react'; 3 | import { Input } from '@chakra-ui/input'; 4 | import Asterisk from '../asterisk'; 5 | 6 | const TextInput = (props) => { 7 | const { id, errors, rules, register, title, placeholder, defaultValue, hasLabel = true, mb, w, testId, ...rest } = props; 8 | 9 | return ( 10 | 11 | 12 | {hasLabel && 13 | {title} 14 | } 15 | 24 | {errors[id] && errors[id].message} 25 | 26 | 27 | ); 28 | 29 | } 30 | 31 | export default TextInput; -------------------------------------------------------------------------------- /src/components/invalidPage/index.js: -------------------------------------------------------------------------------- 1 | import { h } from 'preact'; 2 | import { Box, Heading, Text, Flex } from '@chakra-ui/react'; 3 | import { CloseIcon } from '@chakra-ui/icons'; 4 | 5 | function InvalidPage({headMessage, bodyMessage1, bodyMessage2}){ 6 | return ( 7 | 8 | 9 | 18 | 19 | 20 | 21 | 22 | {headMessage} 23 | 24 | 25 | {bodyMessage1}

26 | {bodyMessage2} 27 |
28 |
29 | ) 30 | } 31 | 32 | export default InvalidPage; -------------------------------------------------------------------------------- /tests/components/dashboardWrapper/dashboardWrapper.test.js: -------------------------------------------------------------------------------- 1 | import { h } from 'preact'; 2 | import { render, waitFor, screen } from '@testing-library/preact' 3 | import App from '../../../src/components/app'; 4 | import { setUserToken } from '../../../src/config/api/auth'; 5 | import * as axios from 'axios'; 6 | 7 | jest.mock('axios'); 8 | describe('Test Dashboard Wrapper', () => { 9 | 10 | test('Dashboard wrapper show sidebar and children', async () => { 11 | setUserToken('d16c4059484867e8d12ff535072509e3f29719e7'); 12 | const response = [ 13 | { 14 | "id": 1, 15 | "name": "test teqam" 16 | }, 17 | { 18 | "id": 2, 19 | "name": "test teqam2" 20 | }, 21 | { 22 | "id": 3, 23 | "name": "test teqam3" 24 | } 25 | ] 26 | axios.get.mockImplementation(() => Promise.resolve({data: response})) 27 | render(); 28 | 29 | await waitFor(() => { 30 | expect(screen.getByText("Error Logs")).toBeDefined(); 31 | expect(screen.getByText("Dashboard")).toBeDefined(); 32 | }) 33 | }); 34 | 35 | }); -------------------------------------------------------------------------------- /src/components/APIMonitorEditor/optionHelper.js: -------------------------------------------------------------------------------- 1 | const methodOption = [ 2 | { 3 | key: "GET", 4 | value: "GET", 5 | }, 6 | { 7 | key: "POST", 8 | value: "POST", 9 | }, 10 | { 11 | key: "PUT", 12 | value: "PUT", 13 | }, 14 | { 15 | key: "PATCH", 16 | value: "PATCH", 17 | }, 18 | { 19 | key: "DELETE", 20 | value: "DELETE", 21 | }, 22 | ] 23 | 24 | const intervalOption = [ 25 | { 26 | key: "1MIN", 27 | value: "1 Minute", 28 | }, 29 | { 30 | key: "2MIN", 31 | value: "2 Minutes", 32 | }, 33 | { 34 | key: "3MIN", 35 | value: "3 Minutes", 36 | }, 37 | { 38 | key: "5MIN", 39 | value: "5 Minutes", 40 | }, 41 | { 42 | key: "10MIN", 43 | value: "10 Minutes", 44 | }, 45 | { 46 | key: "15MIN", 47 | value: "15 Minutes", 48 | }, 49 | { 50 | key: "30MIN", 51 | value: "30 Minutes", 52 | }, 53 | { 54 | key: "60MIN", 55 | value: "60 Minutes", 56 | }, 57 | ] 58 | 59 | export {methodOption, intervalOption}; -------------------------------------------------------------------------------- /src/components/chart/ChartConfig.js: -------------------------------------------------------------------------------- 1 | import { h } from 'preact'; 2 | 3 | function ChartConfig(title, labels, data, backgroundColor, xScaleConfig, y_scale_config, tooltipConfig) { 4 | return { 5 | type: "bar", 6 | data: { 7 | labels: labels, 8 | datasets: [{ 9 | label: title, 10 | data: data, 11 | backgroundColor: backgroundColor, 12 | borderRadius: Number.MAX_VALUE, 13 | borderSkipped: false, 14 | minBarLength: 5, 15 | }] 16 | }, 17 | options: { 18 | responsive: true, 19 | plugins: { 20 | title: { 21 | display: true, 22 | color: 'black', 23 | text: title, 24 | align: 'start', 25 | font: { 26 | weight: 'bold', 27 | size: 25, 28 | family: 'Open Sans', 29 | }, 30 | padding: { 31 | top: 10, 32 | bottom: 30 33 | } 34 | }, 35 | legend: { 36 | display: false 37 | }, 38 | tooltip: tooltipConfig, 39 | }, 40 | scales: { 41 | y: y_scale_config, 42 | x: xScaleConfig, 43 | } 44 | }, 45 | }; 46 | } 47 | 48 | export default ChartConfig; -------------------------------------------------------------------------------- /tests/config/api/auth.test.js: -------------------------------------------------------------------------------- 1 | import { h } from 'preact'; 2 | import { deleteUserToken, getUserToken, setUserToken } from '../../../src/config/api/auth.js'; 3 | 4 | describe('Test Auth Function Without Window', () => { 5 | const { window } = global; 6 | 7 | beforeAll(() => { 8 | delete global.window; 9 | }); 10 | 11 | afterAll(() => { 12 | global.window = window; 13 | }); 14 | 15 | test('get user token', async () => { 16 | expect(getUserToken()).toEqual(null); 17 | }); 18 | 19 | test('set user token', async () => { 20 | setUserToken('unsupported'); 21 | expect(getUserToken()).toEqual(null); 22 | }); 23 | 24 | test('delete user token', async () => { 25 | deleteUserToken(); 26 | expect(getUserToken()).toEqual(null); 27 | }); 28 | 29 | }); 30 | 31 | describe('Test Auth Function With Window', () => { 32 | test('get user token', async () => { 33 | localStorage.setItem("MONAPI_TOKEN", 'test_token'); 34 | expect(getUserToken()).toEqual('test_token'); 35 | }); 36 | 37 | test('set user token', async () => { 38 | setUserToken('test_token'); 39 | expect(getUserToken()).toEqual('test_token'); 40 | }); 41 | 42 | test('delete user token', async () => { 43 | deleteUserToken(); 44 | expect(getUserToken()).toEqual(null); 45 | }); 46 | 47 | }); -------------------------------------------------------------------------------- /src/components/forms/dropdown/index.js: -------------------------------------------------------------------------------- 1 | import { h } from 'preact'; 2 | import { FormControl, FormLabel } from '@chakra-ui/form-control'; 3 | import Asterisk from '../asterisk/index.js'; 4 | import { Select } from '@chakra-ui/react'; 5 | 6 | const Dropdown = (props) => { 7 | const { id, errors, rules, register, title, hasAsterisk = true, placeholder, isDisabled = false, options, dataTestId, isCategory = false, ...rest } = props; 8 | 9 | return ( 10 | 11 | 12 | {title} {hasAsterisk && } 13 | 14 | 15 | 32 | 33 | 34 | ); 35 | } 36 | 37 | export default Dropdown; -------------------------------------------------------------------------------- /tests/components/chart/response_time_chart/response_time_chart.test.js: -------------------------------------------------------------------------------- 1 | import { h } from 'preact'; 2 | import { render, waitFor, screen } from '@testing-library/preact'; 3 | import ResponseTimeChart from '../../../../src/components/chart/response_time_chart'; 4 | 5 | describe('Test response time chart', () => { 6 | test('When render with data, then return with chart', async () => { 7 | const myData = [ 8 | { 9 | "start_time": "2022-10-11T23:55:54.784248+07:00", 10 | "end_time": "2022-10-11T23:56:54.784248+07:00", 11 | "avg": 20 12 | }, 13 | { 14 | "start_time": "2022-10-11T23:55:54.784248+07:00", 15 | "end_time": "2022-10-11T23:56:54.784248+07:00", 16 | "avg": 0 17 | }, 18 | ]; 19 | 20 | render(); 21 | 22 | await waitFor(() => { 23 | expect(screen.getByRole("responseTimeChart").style["_values"].display).toBe('block') 24 | }) 25 | }); 26 | 27 | test('When render with empty data, then return empty chart', async () => { 28 | render(); 29 | 30 | await waitFor(() => { 31 | expect(screen.getByRole("responseTimeChart").style["_values"].display).toBe('block') 32 | }) 33 | }); 34 | 35 | test('When render without data, then return without chart', async () => { 36 | render(); 37 | 38 | await waitFor(() => { 39 | expect(screen.queryByRole("responseTimeChart")).toBeNull() 40 | }) 41 | }); 42 | }) -------------------------------------------------------------------------------- /docs/release_notes.md: -------------------------------------------------------------------------------- 1 | ## Release Notes 2 | 3 | ### Pre Release 4 | Version: Pre-release
5 | Date: 16th September 2022 6 | 1. Staging server is now officially online! checkout on https://staging.monapi.xyz 7 | 2. Our blog is now officially online! checkout on https://blog.monapi.xyz 8 | 9 | ### Release 1 10 | Version: v0.1.0
11 | Date: 25th September 2022 12 | 1. Add feature for login and register 13 | 2. Add feature for view list API Monitor 14 | 15 | ### Release 2 16 | Version: v0.2.0
17 | Date: 17th October 2022 18 | 1. Implement create new api monitor 19 | 2. Implement api monitor details 20 | 3. Run cron for api monitor 21 | 22 | ### Release 3 23 | Version: v0.3.0
24 | Date: 31th October 2022 25 | 1. Edit API Monitor 26 | 2. API Monitor Assertions 27 | 3. Multi step API monitor 28 | 4. Alerts configuration 29 | 30 | ### Release 4 31 | Version: v0.4.0
32 | Date: 13th November 2022 33 | 1. Alerts integration 34 | 2. Forget Password 35 | 3. Test API 36 | 37 | ### Release 5 38 | Version: v0.5.0
39 | Date: 28th November 2022 40 | 1. Team Management 41 | 2. Alerts User-Defined Timezone 42 | 43 | ### Release 6 44 | Version: v1.0.0
45 | Date: 12th December 2022 46 | 1. Status Page Integration 47 | 2. Email register verification 48 | 3. Create new category directly when create API Monitor 49 | 4. Partial Assertions Text 50 | 5. Release 1st version of MonAPI 51 | 52 | ### Release 7 53 | Version: v1.0.1
54 | Date: 28th December 2022 55 | 1. Fix bug password validation on forget password 56 | 2. Update pagerduty email configuration title -------------------------------------------------------------------------------- /src/components/teamEditor/index.js: -------------------------------------------------------------------------------- 1 | import { h, Fragment } from "preact"; 2 | import { Box } from "@chakra-ui/react"; 3 | import FileInput from "../forms/fileInput"; 4 | import TextInput from "../forms/textinput"; 5 | 6 | const TeamEditor = ({errors, register, control, description, logo}) => { 7 | return ( 8 | <> 9 | 10 | 18 | 19 | 20 | 21 |