├── .babelrc.js
├── .dockerignore
├── .gitignore
├── Dockerfile
├── LICENSE
├── README.md
├── config-overrides.js
├── jsconfig.json
├── nginx.conf
├── package.json
├── public
├── assets
│ └── images
│ │ ├── illustrations
│ │ └── dreamer.svg
│ │ ├── logo-circle.png
│ │ ├── logo-circle.svg
│ │ ├── logo.png
│ │ ├── logo.svg
│ │ └── sidebar
│ │ └── sidebar-bg-dark.jpg
├── favicon.ico
├── index.html
├── locales
│ ├── en
│ │ └── translation.json
│ └── pl
│ │ └── translation.json
└── manifest.json
├── src
├── _index.scss
├── app
│ ├── App.jsx
│ ├── MatxLayout
│ │ ├── Layout1
│ │ │ ├── Layout1.jsx
│ │ │ ├── Layout1Settings.js
│ │ │ ├── Layout1Sidenav.jsx
│ │ │ └── Layout1Topbar.jsx
│ │ ├── MatxLayout.jsx
│ │ ├── MatxLayoutSFC.jsx
│ │ ├── MatxTheme
│ │ │ ├── EchartTheme.jsx
│ │ │ ├── MatxCssVars.jsx
│ │ │ ├── MatxTheme.jsx
│ │ │ ├── SecondarySidenavTheme
│ │ │ │ └── SecondarySidenavTheme.jsx
│ │ │ ├── SidenavTheme.jsx
│ │ │ ├── SidenavTheme
│ │ │ │ ├── SidenavTheme.jsx
│ │ │ │ └── SidenavThemeStyles.jsx
│ │ │ ├── themeColors.js
│ │ │ └── themeOptions.js
│ │ ├── SharedCompoents
│ │ │ ├── Brand.jsx
│ │ │ ├── Footer.jsx
│ │ │ ├── MatxCustomizer
│ │ │ │ ├── BadgeSelected.jsx
│ │ │ │ ├── Layout1Customizer.jsx
│ │ │ │ ├── Layout2Customizer.jsx
│ │ │ │ ├── MatxCustomizer.jsx
│ │ │ │ └── customizerOptions.js
│ │ │ ├── SecondarySidebar
│ │ │ │ ├── SecondarySidebar.jsx
│ │ │ │ ├── SecondarySidebarContent.jsx
│ │ │ │ └── SecondarySidebarToggle.jsx
│ │ │ ├── Sidenav.jsx
│ │ │ └── TopbarMenu.jsx
│ │ ├── index.js
│ │ └── settings.js
│ ├── NotificationDisplay.jsx
│ ├── RootRoutes.jsx
│ ├── appContext.js
│ ├── auth
│ │ ├── Auth.jsx
│ │ ├── AuthGuard.jsx
│ │ └── authRoles.js
│ ├── navigations.js
│ ├── redux
│ │ ├── Store.js
│ │ ├── actions
│ │ │ ├── BotCreateActions.js
│ │ │ ├── BotRightsActions.js
│ │ │ ├── BotSettingsActions.js
│ │ │ ├── BotsActions.js
│ │ │ ├── LayoutActions.js
│ │ │ ├── LoginActions.js
│ │ │ ├── NavigationAction.js
│ │ │ ├── NavigationActions.js
│ │ │ ├── NotificationsActions.js
│ │ │ ├── UserActions.js
│ │ │ ├── UserCreateActions.js
│ │ │ ├── UserRemoveActions.js
│ │ │ └── UsersActions.js
│ │ └── reducers
│ │ │ ├── BotCreateReducer.js
│ │ │ ├── BotRightsReducer.js
│ │ │ ├── BotSettingsReducer.js
│ │ │ ├── BotsReducer.js
│ │ │ ├── LayoutReducer.js
│ │ │ ├── LoginReducer.js
│ │ │ ├── NavigationReducer.js
│ │ │ ├── NotificationReducer.js
│ │ │ ├── NotificationsReducer.js
│ │ │ ├── RootReducer.js
│ │ │ ├── ScrumBoardReducer.js
│ │ │ ├── UserCreateReducer.js
│ │ │ ├── UserReducer.js
│ │ │ └── UsersReducer.js
│ ├── services
│ │ └── jwtAuthService.js
│ ├── ui
│ │ ├── AdvanceTable.jsx
│ │ ├── BlockButton.jsx
│ │ └── Spinner.jsx
│ └── views
│ │ ├── bot
│ │ ├── AddRightModel.jsx
│ │ ├── Bot.jsx
│ │ ├── BotRoutes.js
│ │ ├── RightsTable.jsx
│ │ ├── SettingFormHeader.jsx
│ │ └── SettingsFormBody.jsx
│ │ ├── botcreate
│ │ ├── BotCreate.jsx
│ │ └── BotCreateRoutes.js
│ │ ├── dashboard
│ │ ├── Analytics.jsx
│ │ ├── DashboardRoutes.js
│ │ ├── OtherBotsTable.jsx
│ │ ├── RemoveBotModal.jsx
│ │ ├── StatsCard.jsx
│ │ ├── TransferBotModal.jsx
│ │ └── UserBotsTable.jsx
│ │ ├── sessions
│ │ ├── NotFound.jsx
│ │ ├── SessionRoutes.js
│ │ └── SignIn.jsx
│ │ └── users
│ │ ├── PasswordChangeModal.jsx
│ │ ├── RemoveUserModal.jsx
│ │ ├── Users.jsx
│ │ └── UsersRoutes.js
├── axios.js
├── history.js
├── i18n.js
├── index.jsx
├── matx
│ ├── components
│ │ ├── Breadcrumb.jsx
│ │ ├── ConfirmationDialog.jsx
│ │ ├── LoaderBounce.jsx
│ │ ├── MatxHorizontalNav
│ │ │ └── MatxHorizontalNav.jsx
│ │ ├── MatxListItem1.jsx
│ │ ├── MatxLoadable
│ │ │ ├── Loading.js
│ │ │ └── MatxLoadable.jsx
│ │ ├── MatxLoading
│ │ │ └── MatxLoading.jsx
│ │ ├── MatxMenu.jsx
│ │ ├── MatxProgressBar.jsx
│ │ ├── MatxSearchBox.jsx
│ │ ├── MatxSidenav
│ │ │ ├── MatxSidenav.jsx
│ │ │ ├── MatxSidenavContainer.jsx
│ │ │ └── MatxSidenavContent.jsx
│ │ ├── MatxSnackbar.jsx
│ │ ├── MatxSuspense
│ │ │ └── MatxSuspense.jsx
│ │ ├── MatxToolbarMenu.jsx
│ │ ├── MatxVerticalNav
│ │ │ ├── MatxVerticalNav.jsx
│ │ │ └── MatxVerticalNavExpansionPanel.jsx
│ │ ├── RectangleAvatar.jsx
│ │ ├── RichTextEditor.jsx
│ │ └── cards
│ │ │ ├── CardWidget1.jsx
│ │ │ └── SimpleCard.jsx
│ ├── index.js
│ └── theme
│ │ └── EchartTheme.jsx
├── serviceWorker.js
├── styles
│ ├── _app.scss
│ ├── _mixins.scss
│ ├── _reboot.scss
│ ├── _variables.scss
│ ├── components
│ │ ├── _avatar.scss
│ │ ├── _customizer.scss
│ │ ├── _index.scss
│ │ ├── _list.scss
│ │ ├── _loader.scss
│ │ ├── _matx-search-box.scss
│ │ ├── _matx-sidenav.scss
│ │ ├── _matx-tootbar-menu.scss
│ │ ├── _notification.scss
│ │ └── _shopping-cart.scss
│ ├── layouts
│ │ ├── _index.scss
│ │ ├── layout1
│ │ │ ├── _index.scss
│ │ │ └── _layout1.scss
│ │ ├── layout2
│ │ │ ├── _index.scss
│ │ │ ├── _layout2.scss
│ │ │ ├── _navbar.scss
│ │ │ ├── _topbar.scss
│ │ │ └── _variables.scss
│ │ └── shared
│ │ │ ├── _footer.scss
│ │ │ ├── _index.scss
│ │ │ ├── _layout.scss
│ │ │ └── _sidenav.scss
│ ├── utilities
│ │ ├── _animations.scss
│ │ ├── _border.scss
│ │ ├── _carousel.scss
│ │ ├── _color.scss
│ │ ├── _common.scss
│ │ ├── _functions.scss
│ │ ├── _misc.scss
│ │ ├── _positionings.scss
│ │ ├── _shadow.scss
│ │ ├── _spacing.scss
│ │ ├── _typography.scss
│ │ └── _utilities.scss
│ └── views
│ │ ├── _CRUDTable.scss
│ │ ├── _calendar.scss
│ │ ├── _carousel.scss
│ │ ├── _chat-box.scss
│ │ ├── _dashboard.scss
│ │ ├── _ecommerce.scss
│ │ ├── _form.scss
│ │ ├── _inbox.scss
│ │ ├── _index.scss
│ │ ├── _invoice.scss
│ │ ├── _landing.scss
│ │ ├── _list.scss
│ │ ├── _page-layouts.scss
│ │ ├── _pricing.scss
│ │ ├── _sales.scss
│ │ ├── _scrum-board.scss
│ │ ├── _sessions.scss
│ │ ├── _todo.scss
│ │ └── _topbar.scss
└── utils.js
└── yarn.lock
/.babelrc.js:
--------------------------------------------------------------------------------
1 | const plugins = [
2 | [
3 | "babel-plugin-import",
4 | {
5 | libraryName: "@material-ui/core",
6 | // Use "'libraryDirectory': ''," if your bundler does not support ES modules
7 | libraryDirectory: "esm",
8 | camel2DashComponentName: false
9 | },
10 | "core"
11 | ],
12 | [
13 | "babel-plugin-import",
14 | {
15 | libraryName: "@material-ui/icons",
16 | // Use "'libraryDirectory': ''," if your bundler does not support ES modules
17 | libraryDirectory: "esm",
18 | camel2DashComponentName: false
19 | },
20 | "icons"
21 | ]
22 | ];
23 |
24 | module.exports = { plugins };
25 |
--------------------------------------------------------------------------------
/.dockerignore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/elipeF/TS3AudioBot-Control-Panel/069dd3c1d72391db25847392eba49a08ea574f6d/.dockerignore
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /docs/node_modules
6 | /.pnp
7 | .pnp.js
8 |
9 | # testing
10 | /coverage
11 |
12 | # production
13 | /build
14 |
15 | # misc
16 | .DS_Store
17 | .env.local
18 | .env.development.local
19 | .env.test.local
20 | .env.production.local
21 |
22 | npm-debug.log*
23 | yarn-debug.log*
24 | yarn-error.log*
25 |
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM node:12.18-alpine
2 | WORKDIR /app
3 | COPY ./package.json ./
4 | RUN npm install
5 | COPY . .
6 | RUN npm run build
7 |
8 | FROM nginx:alpine
9 | COPY --from=0 /app/build /var/www
10 | COPY nginx.conf /etc/nginx/nginx.conf
11 | EXPOSE 80
12 | ENTRYPOINT ["nginx","-g","daemon off;"]
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright © Mateusz Budaj
4 | Copyright © 2020 UI LIB
5 |
6 | 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:
7 |
8 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
9 |
10 | 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.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # TS3AudioBot-Control-Panel
2 |
3 | # [Instalacja po polsku](https://egcforum.pl/topic/3027-ts3audiobot-control-panel/)
4 |
5 | TS3AudioBot Control Panel allows you to easily create and manipulate bots. Assign them between users and allow users to add play rights to own bots.
6 |
7 | ### Suported languages
8 |
9 | - English
10 | - Polish
11 |
12 | Feel free to pull request with translation :)
13 |
14 | ### Tech
15 |
16 | - reactjs.org
17 | - nodejs.org
18 | - nestjs.com
19 | - [Matx](https://github.com/uilibrary/matx-react) - styling for dashboard
20 |
21 | ### Installation v2
22 |
23 | TS3AudioBot Control Panel requires [Docker](https://docs.docker.com/engine/install/)
24 |
25 | ```sh
26 | $ mkdir abdash
27 | $ cd abdash
28 | $ wget https://github.com/elipeF/TS3AudioBot-Control-Panel/releases/download/2.0.0/kickstartv2.tar.gz
29 | $ tar -xvf kickstartv2.tar.gz
30 | $ chown -R 9999:9999 $(pwd)/ts3ab
31 | !IMPORTANT: Edit docker-compose and change JWT_SECRET
32 | $ docker-compose up -d
33 | ```
34 |
35 | Create admin user
36 |
37 | ```sh
38 | $ wget https://gist.githubusercontent.com/elipeF/192e10d114696c6771b29466169cefd5/raw/64b960776c78a11aa30304ad71aa554d73429790/addadmin.sh
39 | $ chmod +x addadmin.sh
40 | !IMPORTANT: Default port 80, if you have changed, also change below
41 | $ ./addadmin.sh 80 PASS_HERE
42 | ```
43 |
44 | ### Upgrade from v1
45 |
46 | Example docker-compose: https://gist.githubusercontent.com/elipeF/b54b70c36c023e76ccc14c060b0f680c/raw/4ca8561c3eca881397aed1e772fdb60f661e5f94/docker-compose.yml
47 |
48 | ```sh
49 | $ cd abdash
50 | $ docker-compose down
51 | $ rm docker-compose.yml
52 | $ wget https://gist.githubusercontent.com/elipeF/b54b70c36c023e76ccc14c060b0f680c/raw/4ca8561c3eca881397aed1e772fdb60f661e5f94/docker-compose.yml
53 | !IMPORTANT: Edit docker-compose and change JWT_SECRET
54 | $ docker-compose pull
55 | $ docker-compose up -d
56 | ```
57 |
58 |
59 | ### Screenshots
60 |
61 | 
62 | 
63 | 
64 | 
65 |
66 | ## License
67 |
68 | MIT
69 |
--------------------------------------------------------------------------------
/config-overrides.js:
--------------------------------------------------------------------------------
1 | const { useBabelRc, override } = require("customize-cra");
2 |
3 | module.exports = override(useBabelRc());
4 |
--------------------------------------------------------------------------------
/jsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "baseUrl": "src"
4 | }
5 | }
--------------------------------------------------------------------------------
/nginx.conf:
--------------------------------------------------------------------------------
1 | # auto detects a good number of processes to run
2 | worker_processes auto;
3 |
4 | #Provides the configuration file context in which the directives that affect connection processing are specified.
5 | events {
6 | # Sets the maximum number of simultaneous connections that can be opened by a worker process.
7 | worker_connections 8000;
8 | # Tells the worker to accept multiple connections at a time
9 | multi_accept on;
10 | }
11 |
12 |
13 | http {
14 | # what times to include
15 | include /etc/nginx/mime.types;
16 | # what is the default one
17 | default_type application/octet-stream;
18 |
19 | # Sets the path, format, and configuration for a buffered log write
20 | log_format compression '$remote_addr - $remote_user [$time_local] '
21 | '"$request" $status $upstream_addr '
22 | '"$http_referer" "$http_user_agent"';
23 |
24 | server {
25 | # listen on port 80
26 | listen 80;
27 | # save logs here
28 | access_log /var/log/nginx/access.log compression;
29 |
30 | # where the root here
31 | root /var/www;
32 | # what file to server as index
33 | index index.html index.htm;
34 |
35 | location / {
36 | # First attempt to serve request as file, then
37 | # as directory, then fall back to redirecting to index.html
38 | try_files $uri $uri/ /index.html;
39 | }
40 |
41 | location /api/ {
42 | proxy_pass http://api/;
43 | }
44 |
45 | # Media: images, icons, video, audio, HTC
46 | location ~* \.(?:jpg|jpeg|gif|png|ico|cur|gz|svg|svgz|mp4|ogg|ogv|webm|htc)$ {
47 | expires 1M;
48 | access_log off;
49 | add_header Cache-Control "public";
50 | }
51 |
52 | # Javascript and CSS files
53 | location ~* \.(?:css|js)$ {
54 | try_files $uri =404;
55 | expires 1y;
56 | access_log off;
57 | add_header Cache-Control "public";
58 | }
59 |
60 | # Any route containing a file extension (e.g. /devicesfile.js)
61 | location ~ ^.+\..+$ {
62 | try_files $uri =404;
63 | }
64 | }
65 | }
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "matx-react",
3 | "version": "1.0.0",
4 | "private": true,
5 | "dependencies": {
6 | "@date-io/core": "^1.3.13",
7 | "@date-io/date-fns": "^1.3.6",
8 | "@material-ui/codemod": "^4.5.0",
9 | "@material-ui/core": "^4.3.1",
10 | "@material-ui/icons": "^4.2.1",
11 | "@material-ui/lab": "^4.0.0-alpha.22",
12 | "@material-ui/pickers": "^3.2.2",
13 | "autosuggest-highlight": "^3.1.1",
14 | "axios": "^0.19.0",
15 | "babel-polyfill": "^6.26.0",
16 | "clsx": "^1.0.4",
17 | "cross-fetch": "^3.0.4",
18 | "css-vars-ponyfill": "^2.1.2",
19 | "date-fns": "^2.0.0-alpha.34",
20 | "downshift": "^3.2.10",
21 | "globalize": "^0.1.1",
22 | "history": "^4.10.0",
23 | "i18next": "^19.7.0",
24 | "i18next-browser-languagedetector": "^6.0.1",
25 | "i18next-http-backend": "^1.0.20",
26 | "lodash": "^4.17.15",
27 | "material-table": "^1.69.0",
28 | "node-sass": "^4.13.1",
29 | "notistack": "^0.8.8",
30 | "prop-types": "^15.7.2",
31 | "react": "^16.8.6",
32 | "react-autosuggest": "^9.4.3",
33 | "react-beautiful-dnd": "^11.0.4",
34 | "react-dom": "^16.8.6",
35 | "react-highlight": "^0.12.0",
36 | "react-html-parser": "^2.0.2",
37 | "react-i18next": "^11.7.2",
38 | "react-infinite-scroller": "^1.2.4",
39 | "react-loadable": "^5.5.0",
40 | "react-material-ui-form-validator": "^2.0.9",
41 | "react-perfect-scrollbar": "^1.5.2",
42 | "react-redux": "^7.0.3",
43 | "react-router-config": "^5.0.1",
44 | "react-router-dom": "^5.0.0",
45 | "react-scripts": "^3.3.1",
46 | "react-select": "^3.0.4",
47 | "react-vis": "^1.11.7",
48 | "redux": "^4.0.1",
49 | "redux-thunk": "^2.3.0"
50 | },
51 | "scripts": {
52 | "start": "react-app-rewired start",
53 | "build": "NODE_ENV=production GENERATE_SOURCEMAP=false react-scripts build",
54 | "test": "react-scripts test",
55 | "eject": "react-scripts eject",
56 | "ghp": "react-scripts build && gh-pages -d build"
57 | },
58 | "eslintConfig": {
59 | "extends": "react-app"
60 | },
61 | "browserslist": [
62 | ">0.2%",
63 | "not dead",
64 | "not ie <= 11",
65 | "not op_mini all"
66 | ],
67 | "devDependencies": {
68 | "@babel/runtime": "^7.5.5",
69 | "axios-mock-adapter": "^1.17.0",
70 | "babel-plugin-import": "^1.13.0",
71 | "cross-env": "^5.2.0",
72 | "customize-cra": "^0.9.1",
73 | "eslint-plugin-react-hooks": "0.0.0-8d7535e54",
74 | "gh-pages": "^2.1.1",
75 | "react-app-rewired": "^2.1.5"
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/public/assets/images/logo-circle.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/elipeF/TS3AudioBot-Control-Panel/069dd3c1d72391db25847392eba49a08ea574f6d/public/assets/images/logo-circle.png
--------------------------------------------------------------------------------
/public/assets/images/logo-circle.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/public/assets/images/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/elipeF/TS3AudioBot-Control-Panel/069dd3c1d72391db25847392eba49a08ea574f6d/public/assets/images/logo.png
--------------------------------------------------------------------------------
/public/assets/images/logo.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/public/assets/images/sidebar/sidebar-bg-dark.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/elipeF/TS3AudioBot-Control-Panel/069dd3c1d72391db25847392eba49a08ea574f6d/public/assets/images/sidebar/sidebar-bg-dark.jpg
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/elipeF/TS3AudioBot-Control-Panel/069dd3c1d72391db25847392eba49a08ea574f6d/public/favicon.ico
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | updateSidebarMode({ mode: "close" })}
32 | className="sidenav__overlay"
33 | />
34 | );
35 |
36 | return (
37 |
38 |
42 | {props.children}
43 |
44 |
45 | {renderOverlay()}
46 |
47 | );
48 | };
49 |
50 | Sidenav.propTypes = {
51 | setLayoutSettings: PropTypes.func.isRequired,
52 | settings: PropTypes.object.isRequired
53 | };
54 |
55 | const mapStateToProps = state => ({
56 | setLayoutSettings: PropTypes.func.isRequired,
57 | settings: state.layout.settings
58 | });
59 |
60 | export default withRouter(
61 | connect(mapStateToProps, {
62 | setLayoutSettings
63 | })(Sidenav)
64 | );
65 |
--------------------------------------------------------------------------------
/src/app/MatxLayout/SharedCompoents/TopbarMenu.jsx:
--------------------------------------------------------------------------------
1 | import React, { useState } from "react";
2 | import { Icon, IconButton, Hidden } from "@material-ui/core";
3 | import { classList } from "Utils";
4 |
5 | const TopbarMenu = props => {
6 | let { offsetTop } = props;
7 | const [open, setOpen] = useState(false);
8 |
9 | const handleToggle = () => {
10 | setOpen(!open);
11 | };
12 |
13 | return (
14 |
20 |
21 |
22 | {open ? "close" : "more_vert"}
23 |
24 |
25 |
26 |
30 | {props.children}
31 |
32 |
33 | );
34 | };
35 |
36 | export default TopbarMenu;
37 |
--------------------------------------------------------------------------------
/src/app/MatxLayout/index.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | export const MatxLayouts = {
4 | layout1: React.lazy(() => import("./Layout1/Layout1"))
5 | };
6 |
--------------------------------------------------------------------------------
/src/app/MatxLayout/settings.js:
--------------------------------------------------------------------------------
1 | import layout1Settings from "./Layout1/Layout1Settings";
2 | import { themeColors } from "./MatxTheme/themeColors";
3 | import { createMuiTheme } from "@material-ui/core/styles";
4 | import { forEach, merge } from "lodash";
5 | import themeOptions from "./MatxTheme/themeOptions";
6 |
7 | function createMatxThemes() {
8 | let themes = {};
9 |
10 | forEach(themeColors, (value, key) => {
11 | themes[key] = createMuiTheme(merge({}, themeOptions, value));
12 | });
13 | return themes;
14 | }
15 | const themes = createMatxThemes();
16 |
17 | export const MatxLayoutSettings = {
18 | activeLayout: "layout1", // layout1, layout2
19 | activeTheme: "purple1", // View all valid theme colors inside MatxTheme/themeColors.js
20 | perfectScrollbar: true,
21 |
22 | themes: themes,
23 | layout1Settings, // open Layout1/Layout1Settings.js
24 |
25 | secondarySidebar: {
26 | show: false,
27 | open: false,
28 | theme: "slateDark1", // View all valid theme colors inside MatxTheme/themeColors.js
29 | },
30 | // Footer options
31 | footer: {
32 | show: false,
33 | fixed: false,
34 | theme: "slateDark1", // View all valid theme colors inside MatxTheme/themeColors.js
35 | },
36 | };
37 |
--------------------------------------------------------------------------------
/src/app/NotificationDisplay.jsx:
--------------------------------------------------------------------------------
1 | import { withSnackbar } from "notistack";
2 | import { useEffect } from "react";
3 | import { useTranslation } from "react-i18next";
4 | import { connect } from "react-redux";
5 | import { removeNotification } from "./redux/actions/NotificationsActions";
6 |
7 | const NotificationDisplay = (props) => {
8 | const { notification, enqueueSnackbar, onRemoveNotification } = props;
9 | const { t } = useTranslation();
10 | useEffect(() => {
11 | if (notification.length > 0) {
12 | enqueueSnackbar(t("responses." + notification[0].message), {
13 | variant: notification[0].type,
14 | autoHideDuration: 2000,
15 | anchorOrigin: {
16 | vertical: "top",
17 | horizontal: "right",
18 | },
19 | });
20 | onRemoveNotification();
21 | }
22 | }, [notification, enqueueSnackbar, onRemoveNotification, t]);
23 | return null;
24 | };
25 |
26 | const mapStateToProps = (state) => {
27 | return {
28 | notification: state.notification.messages,
29 | };
30 | };
31 |
32 | const mapDispatchToProps = (dispatch) => {
33 | return {
34 | onRemoveNotification: () => dispatch(removeNotification()),
35 | };
36 | };
37 |
38 | export default connect(
39 | mapStateToProps,
40 | mapDispatchToProps
41 | )(withSnackbar(NotificationDisplay));
42 |
--------------------------------------------------------------------------------
/src/app/RootRoutes.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { Redirect } from "react-router-dom";
3 |
4 | import dashboardRoutes from "./views/dashboard/DashboardRoutes";
5 | import botRoutes from "./views/bot/BotRoutes";
6 | import botCreateRoutes from "./views/botcreate/BotCreateRoutes";
7 | import usersRoutes from "./views/users/UsersRoutes";
8 | import sessionRoutes from "./views/sessions/SessionRoutes";
9 |
10 | const redirectRoute = [
11 | {
12 | path: "/",
13 | exact: true,
14 | component: () =>
,
15 | },
16 | ];
17 |
18 | const errorRoute = [
19 | {
20 | component: () =>
,
21 | },
22 | ];
23 |
24 | const routes = [
25 | ...sessionRoutes,
26 | ...dashboardRoutes,
27 | ...botRoutes,
28 | ...botCreateRoutes,
29 | ...usersRoutes,
30 | ...redirectRoute,
31 | ...errorRoute,
32 | ];
33 |
34 | export default routes;
35 |
--------------------------------------------------------------------------------
/src/app/appContext.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | const AppContext = React.createContext({});
4 |
5 | export default AppContext;
6 |
--------------------------------------------------------------------------------
/src/app/auth/Auth.jsx:
--------------------------------------------------------------------------------
1 | /* eslint-disable react-hooks/exhaustive-deps */
2 | import React, { Fragment, useEffect } from "react";
3 | import { connect } from "react-redux";
4 | import PropTypes from "prop-types";
5 | import { setUserData } from "../redux/actions/UserActions";
6 | import { getNavigationByUser } from "../redux/actions/NavigationAction";
7 | import jwtAuthService from "../services/jwtAuthService";
8 | import history from "history.js";
9 |
10 | const checkJwtAuth = async (setUserData) => {
11 | // You need to send token to your server to check token is valid
12 | // modify loginWithToken method in jwtService
13 | let user;
14 | try {
15 | user = await jwtAuthService.loginWithToken();
16 | } catch (e) {}
17 | if (user) setUserData(user);
18 | else
19 | history.push({
20 | pathname: "/session/signin",
21 | });
22 | return user;
23 | };
24 |
25 | const Auth = ({ children, setUserData, getNavigationByUser, login }) => {
26 | setUserData(JSON.parse(window.localStorage.getItem("auth_user")));
27 | useEffect(() => {
28 | checkJwtAuth(setUserData);
29 | getNavigationByUser();
30 | }, [setUserData, getNavigationByUser, login]);
31 |
32 | return
{children};
33 | };
34 |
35 | const mapStateToProps = (state) => ({
36 | setUserData: PropTypes.func.isRequired,
37 | getNavigationByUser: PropTypes.func.isRequired,
38 | login: state.login,
39 | });
40 |
41 | export default connect(mapStateToProps, { setUserData, getNavigationByUser })(
42 | Auth
43 | );
44 |
--------------------------------------------------------------------------------
/src/app/auth/AuthGuard.jsx:
--------------------------------------------------------------------------------
1 | import React, { Fragment, useState, useEffect, useContext } from "react";
2 | import { withRouter } from "react-router-dom";
3 | import { connect } from "react-redux";
4 | import AppContext from "app/appContext";
5 |
6 | const redirectRoute = props => {
7 | const { location, history } = props;
8 | const { pathname } = location;
9 | history.push({
10 | pathname: "/session/signin",
11 | state: { redirectUrl: pathname }
12 | });
13 | };
14 |
15 | const getAuthStatus = (props, routes) => {
16 | const { location, user } = props;
17 | const { pathname } = location;
18 | const matched = routes.find(r => r.path === pathname);
19 | const authenticated =
20 | matched && matched.auth && matched.auth.length
21 | ? matched.auth.includes(user.role)
22 | : true;
23 |
24 | return authenticated;
25 | };
26 |
27 | const AuthGuard = ({ children, ...props }) => {
28 | const { routes } = useContext(AppContext);
29 |
30 | let [authenticated, setAuthenticated] = useState(
31 | getAuthStatus(props, routes)
32 | );
33 |
34 | useEffect(() => {
35 | if (!authenticated) {
36 | redirectRoute(props);
37 | }
38 | setAuthenticated(getAuthStatus(props, routes));
39 | }, [setAuthenticated, authenticated, routes, props]);
40 |
41 | return authenticated ?
{children} : null;
42 | };
43 |
44 | const mapStateToProps = state => ({
45 | user: state.user
46 | });
47 |
48 | export default withRouter(connect(mapStateToProps)(AuthGuard));
49 |
--------------------------------------------------------------------------------
/src/app/auth/authRoles.js:
--------------------------------------------------------------------------------
1 | export const authRoles = {
2 | sa: ['SA'], // Only Super Admin has access
3 | admin: ['SA', 'admin'], // Only SA & Admin has access
4 | editor: ['SA', 'admin', 'EDITOR'], // Only SA & Admin & Editor has access
5 | guest: ['SA', 'admin', 'EDITOR', 'GUEST'] // Everyone has access
6 | }
7 |
8 | // Check out app/views/dashboard/DashboardRoutes.js
9 | // Only SA & Admin has dashboard access
10 |
11 | // const dashboardRoutes = [
12 | // {
13 | // path: "/dashboard/analytics",
14 | // component: Analytics,
15 | // auth: authRoles.admin <----------------
16 | // }
17 | // ];
--------------------------------------------------------------------------------
/src/app/navigations.js:
--------------------------------------------------------------------------------
1 | export const navigations = [
2 | {
3 | name: "navigation.dashboard",
4 | path: "/dashboard",
5 | icon: "dashboard",
6 | },
7 | {
8 | name: "navigation.createbot",
9 | path: "/createbot",
10 | icon: "add_box",
11 | auth: "admin",
12 | },
13 | {
14 | name: "navigation.users",
15 | path: "/users",
16 | icon: "account_box",
17 | auth: "admin",
18 | },
19 | ];
20 |
--------------------------------------------------------------------------------
/src/app/redux/Store.js:
--------------------------------------------------------------------------------
1 | import thunk from "redux-thunk";
2 | import { createStore, applyMiddleware, compose } from "redux";
3 | import RootReducer from "./reducers/RootReducer";
4 |
5 | const initialState = {};
6 | const middlewares = [thunk];
7 | let devtools = x => x;
8 |
9 | if (
10 | process.env.NODE_ENV !== "production" &&
11 | process.browser &&
12 | window.__REDUX_DEVTOOLS_EXTENSION__
13 | ) {
14 | devtools = window.__REDUX_DEVTOOLS_EXTENSION__();
15 | }
16 |
17 | export const Store = createStore(
18 | RootReducer,
19 | initialState,
20 | compose(applyMiddleware(...middlewares), devtools)
21 | );
22 |
--------------------------------------------------------------------------------
/src/app/redux/actions/BotCreateActions.js:
--------------------------------------------------------------------------------
1 | import instance from "./../../../axios";
2 | import { newNotification } from "./NotificationsActions";
3 |
4 | export const CREATE_BOT_START = "CREATE_BOT_START";
5 | export const CREATE_BOT_SUCCESS = "CREATE_BOT_SUCCESS";
6 | export const CREATE_BOT_FAIL = "CREATE_BOT_FAIL";
7 |
8 | export const createBot = (data) => (dispatch) => {
9 | dispatch({
10 | type: CREATE_BOT_START,
11 | });
12 |
13 | instance
14 | .post(`/api/bots/`, { ...data })
15 | .then((res) => {
16 | dispatch({
17 | type: CREATE_BOT_SUCCESS,
18 | });
19 | dispatch(
20 | newNotification({
21 | type: "success",
22 | message: CREATE_BOT_SUCCESS,
23 | })
24 | );
25 | })
26 | .catch((e) => {
27 | dispatch(
28 | newNotification({
29 | type: "error",
30 | message: e.response?.data.message
31 | ? e.response?.data.message
32 | : e.message,
33 | })
34 | );
35 | dispatch({
36 | type: CREATE_BOT_FAIL,
37 | payload: e,
38 | });
39 | });
40 | };
41 |
--------------------------------------------------------------------------------
/src/app/redux/actions/BotsActions.js:
--------------------------------------------------------------------------------
1 | import instance from "./../../../axios";
2 | import { newNotification } from "./NotificationsActions";
3 |
4 | export const GET_BOTS_LIST_START = "GET_BOTS_LIST_START";
5 | export const GET_BOTS_LIST_SUCCESS = "GET_BOTS_LIST_SUCCESS";
6 | export const GET_BOTS_LIST_FAIL = "GET_BOTS_LIST_FAIL";
7 |
8 | export const getBotsList = () => (dispatch) => {
9 | dispatch({
10 | type: GET_BOTS_LIST_START,
11 | });
12 | instance
13 | .get(`/api/bots/`)
14 | .then((res) => {
15 | dispatch({
16 | type: GET_BOTS_LIST_SUCCESS,
17 | payload: res.data,
18 | });
19 | })
20 | .catch((e) => {
21 | console.log(e);
22 | dispatch(
23 | newNotification({
24 | type: "error",
25 | message: e.response?.data.message
26 | ? e.response.data.message
27 | : e.message,
28 | })
29 | );
30 | dispatch({
31 | type: GET_BOTS_LIST_FAIL,
32 | payload: e,
33 | });
34 | });
35 | };
36 |
--------------------------------------------------------------------------------
/src/app/redux/actions/LayoutActions.js:
--------------------------------------------------------------------------------
1 | export const SET_LAYOUT_SETTINGS = "LAYOUT_SET_SETTINGS";
2 | export const SET_DEFAULT_LAYOUT_SETTINGS = "LAYOUT_SET_DEFAULT_SETTINGS";
3 |
4 | export const setLayoutSettings = data => dispatch => {
5 | dispatch({
6 | type: SET_LAYOUT_SETTINGS,
7 | data: data
8 | });
9 | };
10 |
11 | export const setDefaultSettings = data => dispatch => {
12 | dispatch({
13 | type: SET_DEFAULT_LAYOUT_SETTINGS,
14 | data: data
15 | });
16 | };
17 |
--------------------------------------------------------------------------------
/src/app/redux/actions/LoginActions.js:
--------------------------------------------------------------------------------
1 | import jwtAuthService from "../../services/jwtAuthService";
2 | import { setUserData } from "./UserActions";
3 | import history from "history.js";
4 | import { newNotification } from "./NotificationsActions";
5 | import { INIT_USER_NAVIGATION } from "./NavigationAction";
6 |
7 | export const LOGIN_ERROR = "LOGIN_ERROR";
8 | export const LOGIN_SUCCESS = "LOGIN_SUCCESS";
9 | export const LOGIN_LOADING = "LOGIN_LOADING";
10 | export const RESET_PASSWORD = "RESET_PASSWORD";
11 |
12 | export function loginWithUsernameAndPassword({ username, password }) {
13 | return (dispatch) => {
14 | dispatch({
15 | type: LOGIN_LOADING,
16 | });
17 |
18 | jwtAuthService
19 | .loginWithUsernameAndPassword(username, password)
20 | .then((user) => {
21 | dispatch(setUserData(user));
22 | dispatch({
23 | type: INIT_USER_NAVIGATION,
24 | });
25 |
26 | history.push({
27 | pathname: "/",
28 | });
29 |
30 | return dispatch({
31 | type: LOGIN_SUCCESS,
32 | });
33 | })
34 | .catch((e) => {
35 | dispatch(
36 | newNotification({
37 | type: "error",
38 | message: e.response?.data.message
39 | ? e.response.data.message
40 | : e.message,
41 | })
42 | );
43 | return dispatch({
44 | type: LOGIN_ERROR,
45 | payload: e,
46 | });
47 | });
48 | };
49 | }
50 | export function resetPassword({ email }) {
51 | return (dispatch) => {
52 | dispatch({
53 | payload: email,
54 | type: RESET_PASSWORD,
55 | });
56 | };
57 | }
58 |
--------------------------------------------------------------------------------
/src/app/redux/actions/NavigationAction.js:
--------------------------------------------------------------------------------
1 | export const SET_USER_NAVIGATION = "SET_USER_NAVIGATION";
2 | export const INIT_USER_NAVIGATION = "INIT_USER_NAVIGATION";
3 |
4 | const getfilteredNavigations = (navList = [], role) => {
5 | return navList.reduce((array, nav) => {
6 | if (nav.auth) {
7 | if (nav.auth.includes(role)) {
8 | array.push(nav);
9 | }
10 | } else {
11 | if (nav.children) {
12 | nav.children = getfilteredNavigations(nav.children, role);
13 | array.push(nav);
14 | } else {
15 | array.push(nav);
16 | }
17 | }
18 | return array;
19 | }, []);
20 | };
21 |
22 | export function getNavigationByUser() {
23 | return (dispatch, getState) => {
24 | let { user, navigations = [] } = getState();
25 | let filteredNavigations = getfilteredNavigations(navigations, user.role);
26 |
27 | dispatch({
28 | type: SET_USER_NAVIGATION,
29 | payload: [...filteredNavigations],
30 | });
31 | };
32 | }
33 |
--------------------------------------------------------------------------------
/src/app/redux/actions/NavigationActions.js:
--------------------------------------------------------------------------------
1 | export const SET_NAVIGATION = "SET_NAVIGATION";
2 | export const GET_NAVIGATION = "GET_NAVIGATION";
3 | export const RESET_NAVIGATION = "RESET_NAVIGATION";
4 |
5 | export function getNavigation(user) {
6 | return {
7 | type: GET_NAVIGATION,
8 | };
9 | }
10 |
11 | export function logoutUser() {
12 | return (dispatch) => {
13 | jwtAuthService.logout();
14 |
15 | history.push({
16 | pathname: "/session/signin",
17 | });
18 |
19 | dispatch({
20 | type: USER_LOGGED_OUT,
21 | });
22 | };
23 | }
24 |
--------------------------------------------------------------------------------
/src/app/redux/actions/NotificationsActions.js:
--------------------------------------------------------------------------------
1 |
2 | export const ADD_NOTIFICATION = "ADD_NOTIFICATION";
3 | export const REMOVE_NOTIFICATION = "REMOVE_NOTIFICATION";
4 |
5 |
6 |
7 |
8 | export const newNotification = (data) => dispatch => {
9 | dispatch({
10 | type: ADD_NOTIFICATION,
11 | payload: data
12 | });
13 | };
14 |
15 | export const removeNotification = () => dispatch => {
16 | dispatch({
17 | type: REMOVE_NOTIFICATION,
18 | });
19 | };
--------------------------------------------------------------------------------
/src/app/redux/actions/UserActions.js:
--------------------------------------------------------------------------------
1 | import history from "history.js";
2 | import jwtAuthService from "../../services/jwtAuthService";
3 |
4 |
5 | export const SET_USER_DATA = "USER_SET_DATA";
6 | export const REMOVE_USER_DATA = "USER_REMOVE_DATA";
7 | export const USER_LOGGED_OUT = "USER_LOGGED_OUT";
8 |
9 | export function setUserData(user) {
10 | return (dispatch) => {
11 | dispatch({
12 | type: SET_USER_DATA,
13 | data: user,
14 | });
15 | };
16 | }
17 |
18 | export function logoutUser() {
19 | return (dispatch) => {
20 | jwtAuthService.logout();
21 |
22 | history.push({
23 | pathname: "/session/signin",
24 | });
25 |
26 | dispatch({
27 | type: USER_LOGGED_OUT,
28 | });
29 | };
30 | }
31 |
--------------------------------------------------------------------------------
/src/app/redux/actions/UserCreateActions.js:
--------------------------------------------------------------------------------
1 | import instance from "./../../../axios";
2 | import { newNotification } from "./NotificationsActions";
3 | import { getUsersList } from "./UsersActions";
4 |
5 | export const CREATE_USER_START = "CREATE_USER_START";
6 | export const CREATE_USER_SUCCESS = "CREATE_USER_SUCCESS";
7 | export const CREATE_USER_FAIL = "CREATE_USER_FAIL";
8 |
9 | export const createUser = (name, password) => (dispatch) => {
10 | dispatch({
11 | type: CREATE_USER_START,
12 | });
13 |
14 | instance
15 | .post(`/api/auth/register`, {
16 | name,
17 | password,
18 | })
19 | .then((res) => {
20 | dispatch({
21 | type: CREATE_USER_SUCCESS,
22 | });
23 | dispatch(getUsersList());
24 | dispatch(
25 | newNotification({
26 | type: "success",
27 | message: CREATE_USER_SUCCESS,
28 | })
29 | );
30 | })
31 | .catch((e) => {
32 | dispatch(
33 | newNotification({
34 | type: "error",
35 | message: e.response?.data.message
36 | ? e.response?.data.message
37 | : e.message,
38 | })
39 | );
40 | dispatch({
41 | type: CREATE_USER_FAIL,
42 | payload: e,
43 | });
44 | });
45 | };
46 |
--------------------------------------------------------------------------------
/src/app/redux/actions/UserRemoveActions.js:
--------------------------------------------------------------------------------
1 | import instance from "./../../../axios";
2 | import { newNotification } from "./NotificationsActions";
3 | import { getUsersList } from "./UsersActions";
4 |
5 | const REMOVE_USER_SUCCESS = "REMOVE_USER_SUCCESS";
6 |
7 | export const removeUser = (id) => (dispatch) => {
8 | instance
9 | .delete(`/api/users/` + id)
10 | .then((res) => {
11 | dispatch(getUsersList());
12 | dispatch(
13 | newNotification({
14 | type: "success",
15 | message: REMOVE_USER_SUCCESS,
16 | })
17 | );
18 | })
19 | .catch((e) => {
20 | dispatch(
21 | newNotification({
22 | type: "error",
23 | message: e.response?.data.message
24 | ? e.response?.data.message
25 | : e.message,
26 | })
27 | );
28 | });
29 | };
30 |
31 | export const removeUserWithBotMigrate = (id, target) => (dispatch) => {
32 | instance
33 | .patch(`/api/bots/` + id + "/migrate", {
34 | id: target,
35 | })
36 | .then((res) => {
37 | dispatch(removeUser(id));
38 | })
39 | .catch((e) => {
40 | dispatch(
41 | newNotification({
42 | type: "error",
43 | message: e.response?.data.message
44 | ? e.response?.data.message
45 | : e.message,
46 | })
47 | );
48 | });
49 | };
50 |
51 | export const removeUserWithBotDelete = (id, target) => (dispatch) => {
52 | instance
53 | .delete(`/api/bots/` + id + "/all", {
54 | id: target,
55 | })
56 | .then((res) => {
57 | dispatch(removeUser(id));
58 | })
59 | .catch((e) => {
60 | dispatch(
61 | newNotification({
62 | type: "error",
63 | message: e.response?.data.message
64 | ? e.response?.data.message
65 | : e.message,
66 | })
67 | );
68 | });
69 | };
70 |
--------------------------------------------------------------------------------
/src/app/redux/actions/UsersActions.js:
--------------------------------------------------------------------------------
1 | import instance from "./../../../axios";
2 | import { newNotification } from "./NotificationsActions";
3 |
4 | export const GET_USERS_START = "GET_USERS_START";
5 | export const GET_USERS_SUCCESS = "GET_USERS_SUCCESS";
6 | export const GET_USERS_FAIL = "GET_USERS_FAIL";
7 |
8 | export const getUsersList = () => (dispatch) => {
9 | dispatch({
10 | type: GET_USERS_START,
11 | });
12 | instance
13 | .get(`/api/users/`)
14 | .then((res) => {
15 | dispatch({
16 | type: GET_USERS_SUCCESS,
17 | payload: res.data,
18 | });
19 | })
20 | .catch((e) => {
21 | dispatch(
22 | newNotification({
23 | type: "error",
24 | message: e.response?.data.message
25 | ? e.response.data.message
26 | : e.message,
27 | })
28 | );
29 | dispatch({
30 | type: GET_USERS_FAIL,
31 | payload: e,
32 | });
33 | });
34 | };
35 |
--------------------------------------------------------------------------------
/src/app/redux/reducers/BotCreateReducer.js:
--------------------------------------------------------------------------------
1 | import {
2 | CREATE_BOT_FAIL,
3 | CREATE_BOT_START,
4 | CREATE_BOT_SUCCESS,
5 | } from "../actions/BotCreateActions";
6 |
7 | const initialState = {
8 | loading: false,
9 | };
10 |
11 | const BotCreateReducer = function (state = initialState, action) {
12 | switch (action.type) {
13 | case CREATE_BOT_START: {
14 | return {
15 | ...state,
16 | loading: true,
17 | };
18 | }
19 | case CREATE_BOT_SUCCESS: {
20 | return {
21 | ...state,
22 | loading: false,
23 | };
24 | }
25 | case CREATE_BOT_FAIL: {
26 | return {
27 | ...state,
28 | loading: false,
29 | };
30 | }
31 | default: {
32 | return {
33 | ...state,
34 | };
35 | }
36 | }
37 | };
38 |
39 | export default BotCreateReducer;
40 |
--------------------------------------------------------------------------------
/src/app/redux/reducers/BotRightsReducer.js:
--------------------------------------------------------------------------------
1 | import { GET_BOT_RIGHTS_START, GET_BOT_RIGHTS_SUCCESS, GET_BOT_RIGHTS_FAIL, ADD_BOT_RIGHTS_START, ADD_BOT_RIGHTS_FAIL, ADD_BOT_RIGHTS_SUCCESS, DEL_BOT_RIGHTS_START, DEL_BOT_RIGHTS_SUCCESS, DEL_BOT_RIGHTS_FAIL } from "../actions/BotRightsActions";
2 |
3 | const initialState = {
4 | useruid: [],
5 | groupid: [],
6 | loading: true,
7 | };
8 |
9 | const BotRightsReducer = function (state = initialState, action) {
10 | switch (action.type) {
11 | case GET_BOT_RIGHTS_START: {
12 | return {
13 | ...state,
14 | loading: true
15 | };
16 | }
17 | case GET_BOT_RIGHTS_SUCCESS: {
18 | return {
19 | ...state,
20 | loading: false,
21 | useruid: [...action.payload.useruid],
22 | groupid: [...action.payload.groupid],
23 | };
24 | }
25 | case GET_BOT_RIGHTS_FAIL: {
26 | return {
27 | ...state,
28 | loading: false,
29 | }
30 | }
31 | case ADD_BOT_RIGHTS_START: {
32 | return {
33 | ...state,
34 | loading: true
35 | };
36 | }
37 | case ADD_BOT_RIGHTS_SUCCESS: {
38 | return {
39 | ...state,
40 | loading: false,
41 | };
42 | }
43 | case ADD_BOT_RIGHTS_FAIL: {
44 | return {
45 | ...state,
46 | loading: false,
47 | }
48 | }
49 | case DEL_BOT_RIGHTS_START: {
50 | return {
51 | ...state,
52 | loading: true
53 | };
54 | }
55 | case DEL_BOT_RIGHTS_SUCCESS: {
56 | return {
57 | ...state,
58 | loading: false,
59 | };
60 | }
61 | case DEL_BOT_RIGHTS_FAIL: {
62 | return {
63 | ...state,
64 | loading: false,
65 | }
66 | }
67 | default: {
68 | return {
69 | ...state
70 | };
71 | }
72 | }
73 | };
74 |
75 | export default BotRightsReducer;
76 |
--------------------------------------------------------------------------------
/src/app/redux/reducers/BotSettingsReducer.js:
--------------------------------------------------------------------------------
1 | import { EDIT_BOT_FAIL, EDIT_BOT_START, EDIT_BOT_SUCCESS, GET_BOT_FAIL, GET_BOT_START, GET_BOT_SUCCESS, START_BOT_FAIL, START_BOT_START, START_BOT_SUCCESS, STOP_BOT_FAIL, STOP_BOT_START, STOP_BOT_SUCCESS } from "../actions/BotSettingsActions";
2 |
3 | const initialState = {
4 | bot: null,
5 | loading: true,
6 | };
7 |
8 | const BotSettingsReducer = function (state = initialState, action) {
9 | switch (action.type) {
10 | case GET_BOT_START: {
11 | return {
12 | ...state,
13 | loading: true
14 | };
15 | }
16 | case GET_BOT_SUCCESS: {
17 | return {
18 | ...state,
19 | loading: false,
20 | bot: { ...action.payload }
21 | };
22 | }
23 | case GET_BOT_FAIL: {
24 | return {
25 | ...state,
26 | loading: false,
27 | }
28 | }
29 | case START_BOT_START: {
30 | return {
31 | ...state,
32 | loading: true
33 | }
34 | }
35 | case START_BOT_SUCCESS: {
36 | return {
37 | ...state,
38 | loading: false
39 | }
40 | }
41 | case START_BOT_FAIL: {
42 | return {
43 | ...state,
44 | loading: false
45 | }
46 | }
47 | case STOP_BOT_START: {
48 | return {
49 | ...state,
50 | loading: true
51 | }
52 | }
53 | case STOP_BOT_SUCCESS: {
54 | return {
55 | ...state,
56 | loading: false
57 | }
58 | }
59 | case STOP_BOT_FAIL: {
60 | return {
61 | ...state,
62 | loading: false
63 | }
64 | }
65 | case EDIT_BOT_START: {
66 | return {
67 | ...state,
68 | loading: true
69 | }
70 | }
71 | case EDIT_BOT_SUCCESS: {
72 | return {
73 | ...state,
74 | loading: false
75 | }
76 | }
77 | case EDIT_BOT_FAIL: {
78 | return {
79 | ...state,
80 | loading: false
81 | }
82 | }
83 | default: {
84 | return {
85 | ...state
86 | };
87 | }
88 | }
89 | };
90 |
91 | export default BotSettingsReducer;
92 |
--------------------------------------------------------------------------------
/src/app/redux/reducers/BotsReducer.js:
--------------------------------------------------------------------------------
1 | import { GET_BOTS_LIST_FAIL, GET_BOTS_LIST_START, GET_BOTS_LIST_SUCCESS } from "../actions/BotsActions";
2 |
3 | const initialState = {
4 | bots: [],
5 | loading: true,
6 | };
7 |
8 | const BotsReducer = function (state = initialState, action) {
9 | switch (action.type) {
10 | case GET_BOTS_LIST_START: {
11 | return {
12 | ...state,
13 | loading: true
14 | };
15 | }
16 | case GET_BOTS_LIST_SUCCESS: {
17 | return {
18 | ...state,
19 | loading: false,
20 | bots: [...action.payload]
21 | };
22 | }
23 | case GET_BOTS_LIST_FAIL: {
24 | return {
25 | ...state,
26 | loading: false,
27 | }
28 | }
29 | default: {
30 | return {
31 | ...state
32 | };
33 | }
34 | }
35 | };
36 |
37 | export default BotsReducer;
38 |
--------------------------------------------------------------------------------
/src/app/redux/reducers/LayoutReducer.js:
--------------------------------------------------------------------------------
1 | import {
2 | SET_LAYOUT_SETTINGS,
3 | SET_DEFAULT_LAYOUT_SETTINGS
4 | } from "../actions/LayoutActions";
5 | import { MatxLayoutSettings } from "../../MatxLayout/settings";
6 |
7 | const initialState = {
8 | settings: {
9 | ...MatxLayoutSettings
10 | },
11 | defaultSettings: {
12 | ...MatxLayoutSettings
13 | }
14 | };
15 |
16 | const LayoutReducer = (state = initialState, action) => {
17 | switch (action.type) {
18 | case SET_LAYOUT_SETTINGS:
19 | return {
20 | ...state,
21 | settings: { ...action.data }
22 | };
23 | case SET_DEFAULT_LAYOUT_SETTINGS:
24 | return {
25 | ...state,
26 | defaultSettings: { ...action.data }
27 | };
28 | default:
29 | return { ...state };
30 | }
31 | };
32 |
33 | export default LayoutReducer;
34 |
--------------------------------------------------------------------------------
/src/app/redux/reducers/LoginReducer.js:
--------------------------------------------------------------------------------
1 | import {
2 | LOGIN_SUCCESS,
3 | LOGIN_ERROR,
4 | LOGIN_LOADING,
5 | RESET_PASSWORD
6 | } from "../actions/LoginActions";
7 |
8 | const initialState = {
9 | success: false,
10 | loading: false,
11 | error: {
12 | username: null,
13 | password: null
14 | }
15 | };
16 |
17 | const LoginReducer = function(state = initialState, action) {
18 | switch (action.type) {
19 | case LOGIN_LOADING: {
20 | return {
21 | ...state,
22 | loading: true
23 | };
24 | }
25 | case LOGIN_SUCCESS: {
26 | return {
27 | ...state,
28 | success: true,
29 | loading: false
30 | };
31 | }
32 | case RESET_PASSWORD: {
33 | return {
34 | ...state,
35 | success: true,
36 | loading: false
37 | };
38 | }
39 | case LOGIN_ERROR: {
40 | return {
41 | success: false,
42 | loading: false,
43 | error: action.data
44 | };
45 | }
46 | default: {
47 | return state;
48 | }
49 | }
50 | };
51 |
52 | export default LoginReducer;
53 |
--------------------------------------------------------------------------------
/src/app/redux/reducers/NavigationReducer.js:
--------------------------------------------------------------------------------
1 | import { navigations } from "app/navigations";
2 | import {
3 | INIT_USER_NAVIGATION,
4 | SET_USER_NAVIGATION,
5 | } from "../actions/NavigationAction";
6 |
7 | const initialState = [...navigations];
8 |
9 | const NavigationReducer = function (state = initialState, action) {
10 | switch (action.type) {
11 | case SET_USER_NAVIGATION: {
12 | return [...action.payload];
13 | }
14 | case INIT_USER_NAVIGATION: {
15 | return [...navigations];
16 | }
17 | default: {
18 | return [...state];
19 | }
20 | }
21 | };
22 |
23 | export default NavigationReducer;
24 |
--------------------------------------------------------------------------------
/src/app/redux/reducers/NotificationReducer.js:
--------------------------------------------------------------------------------
1 | import {
2 | GET_NOTIFICATION,
3 | CREATE_NOTIFICATION,
4 | DELETE_ALL_NOTIFICATION,
5 | DELETE_NOTIFICATION
6 | } from "../actions/NotificationActions";
7 |
8 | const initialState = [];
9 |
10 | const NotificationReducer = function(state = initialState, action) {
11 | switch (action.type) {
12 | case GET_NOTIFICATION: {
13 | return [...action.payload];
14 | }
15 | case CREATE_NOTIFICATION: {
16 | return [...action.payload];
17 | }
18 | case DELETE_NOTIFICATION: {
19 | return [...action.payload];
20 | }
21 | case DELETE_ALL_NOTIFICATION: {
22 | return [...action.payload];
23 | }
24 | default: {
25 | return [...state];
26 | }
27 | }
28 | };
29 |
30 | export default NotificationReducer;
31 |
--------------------------------------------------------------------------------
/src/app/redux/reducers/NotificationsReducer.js:
--------------------------------------------------------------------------------
1 | import { ADD_NOTIFICATION, REMOVE_NOTIFICATION } from "../actions/NotificationsActions";
2 |
3 | const initialState = {
4 | messages: [],
5 | };
6 |
7 | const NotificationsReducer = function (state = initialState, action) {
8 | switch (action.type) {
9 | case ADD_NOTIFICATION: {
10 | return {
11 | ...state,
12 | messages: state.messages.concat(action.payload)
13 | };
14 | }
15 | case REMOVE_NOTIFICATION: {
16 | return {
17 | ...state,
18 | messages: [...state.messages.slice(0, 0), ...state.messages.slice(1)]
19 | };
20 | }
21 | default: {
22 | return {
23 | ...state
24 | };
25 | }
26 | }
27 | };
28 |
29 | export default NotificationsReducer;
30 |
--------------------------------------------------------------------------------
/src/app/redux/reducers/RootReducer.js:
--------------------------------------------------------------------------------
1 | import { combineReducers } from "redux";
2 | import LoginReducer from "./LoginReducer";
3 | import UserReducer from "./UserReducer";
4 | import LayoutReducer from "./LayoutReducer";
5 | import NavigationReducer from "./NavigationReducer";
6 | import BotsReducer from "./BotsReducer";
7 | import BotSettingsReducer from "./BotSettingsReducer";
8 | import BotRightsReducer from "./BotRightsReducer";
9 | import NotificationsReducer from "./NotificationsReducer";
10 | import UsersReducer from "./UsersReducer";
11 | import BotCreateReducer from "./BotCreateReducer";
12 | import UserCreateReducer from "./UserCreateReducer";
13 |
14 | const RootReducer = combineReducers({
15 | bots: BotsReducer,
16 | botSettings: BotSettingsReducer,
17 | botCreate: BotCreateReducer,
18 | userCreate: UserCreateReducer,
19 | botRights: BotRightsReducer,
20 | notification: NotificationsReducer,
21 | login: LoginReducer,
22 | user: UserReducer,
23 | users: UsersReducer,
24 | layout: LayoutReducer,
25 | // scrumboard: ScrumBoardReducer,
26 | // ecommerce: EcommerceReducer,
27 | navigations: NavigationReducer,
28 | });
29 |
30 | export default RootReducer;
31 |
--------------------------------------------------------------------------------
/src/app/redux/reducers/ScrumBoardReducer.js:
--------------------------------------------------------------------------------
1 | import {
2 | GET_ALL_BOARD,
3 | ADD_BOARD,
4 | GET_BOARD_BY_ID,
5 | ADD_LIST,
6 | RENAME_LIST,
7 | DELETE_LIST,
8 | ADD_CARD,
9 | GET_ALL_MEMBERS,
10 | GET_ALL_LABELS,
11 | ADD_MEMBER_IN_BOARD,
12 | DELETE_MEMBER_FROM_BOARD,
13 | UPDATE_CARD,
14 | MOVE_CARD,
15 | REORDER_LIST,
16 | REORDER_CARD_LIST
17 | } from "../actions/ScrumBoardActions";
18 |
19 | const initialState = {};
20 |
21 | const ScrumBoardReducer = function(state = initialState, action) {
22 | switch (action.type) {
23 | case GET_ALL_MEMBERS: {
24 | return {
25 | ...state,
26 | memberList: [...action.payload]
27 | };
28 | }
29 | case GET_ALL_LABELS: {
30 | return {
31 | ...state,
32 | labelList: [...action.payload]
33 | };
34 | }
35 | case GET_ALL_BOARD: {
36 | return {
37 | ...state,
38 | boardList: [...action.payload]
39 | };
40 | }
41 | case ADD_BOARD: {
42 | return {
43 | ...state,
44 | boardList: [...action.payload]
45 | };
46 | }
47 | case GET_BOARD_BY_ID: {
48 | return {
49 | ...state,
50 | board: { ...action.payload }
51 | };
52 | }
53 | case ADD_MEMBER_IN_BOARD: {
54 | return {
55 | ...state,
56 | board: { ...action.payload }
57 | };
58 | }
59 | case DELETE_MEMBER_FROM_BOARD: {
60 | return {
61 | ...state,
62 | board: { ...action.payload }
63 | };
64 | }
65 | case ADD_LIST: {
66 | return {
67 | ...state,
68 | board: { ...action.payload }
69 | };
70 | }
71 | case RENAME_LIST: {
72 | return {
73 | ...state,
74 | board: { ...action.payload }
75 | };
76 | }
77 | case DELETE_LIST: {
78 | return {
79 | ...state,
80 | board: { ...action.payload }
81 | };
82 | }
83 | case REORDER_LIST: {
84 | return {
85 | ...state,
86 | board: { ...action.payload }
87 | };
88 | }
89 | case ADD_CARD: {
90 | return {
91 | ...state,
92 | board: { ...action.payload }
93 | };
94 | }
95 | case UPDATE_CARD: {
96 | return {
97 | ...state,
98 | board: { ...action.payload }
99 | };
100 | }
101 | case REORDER_CARD_LIST: {
102 | return {
103 | ...state,
104 | board: { ...action.payload }
105 | };
106 | }
107 | case MOVE_CARD: {
108 | return {
109 | ...state,
110 | board: { ...action.payload }
111 | };
112 | }
113 | default: {
114 | return state;
115 | }
116 | }
117 | };
118 |
119 | export default ScrumBoardReducer;
120 |
--------------------------------------------------------------------------------
/src/app/redux/reducers/UserCreateReducer.js:
--------------------------------------------------------------------------------
1 | import {
2 | CREATE_USER_FAIL,
3 | CREATE_USER_START,
4 | CREATE_USER_SUCCESS,
5 | } from "../actions/UserCreateActions";
6 |
7 | const initialState = {
8 | loading: false,
9 | };
10 |
11 | const UserCreateReducer = function (state = initialState, action) {
12 | switch (action.type) {
13 | case CREATE_USER_START: {
14 | return {
15 | ...state,
16 | loading: true,
17 | };
18 | }
19 | case CREATE_USER_SUCCESS: {
20 | return {
21 | ...state,
22 | loading: false,
23 | };
24 | }
25 | case CREATE_USER_FAIL: {
26 | return {
27 | ...state,
28 | loading: false,
29 | };
30 | }
31 | default: {
32 | return {
33 | ...state,
34 | };
35 | }
36 | }
37 | };
38 |
39 | export default UserCreateReducer;
40 |
--------------------------------------------------------------------------------
/src/app/redux/reducers/UserReducer.js:
--------------------------------------------------------------------------------
1 | import {
2 | SET_USER_DATA,
3 | REMOVE_USER_DATA,
4 | USER_LOGGED_OUT
5 | } from "../actions/UserActions";
6 |
7 | const initialState = {};
8 |
9 | const userReducer = function (state = initialState, action) {
10 | switch (action.type) {
11 | case SET_USER_DATA: {
12 | return {
13 | ...state,
14 | ...action.data
15 | };
16 | }
17 | case REMOVE_USER_DATA: {
18 | return {
19 | ...state
20 | };
21 | }
22 | case USER_LOGGED_OUT: {
23 | return state;
24 | }
25 | default: {
26 | return state;
27 | }
28 | }
29 | };
30 |
31 | export default userReducer;
32 |
--------------------------------------------------------------------------------
/src/app/redux/reducers/UsersReducer.js:
--------------------------------------------------------------------------------
1 | import { GET_USERS_FAIL, GET_USERS_START, GET_USERS_SUCCESS } from "../actions/UsersActions";
2 |
3 | const initialState = {
4 | users: [],
5 | loading: true,
6 | };
7 |
8 | const UsersReducer = function (state = initialState, action) {
9 | switch (action.type) {
10 | case GET_USERS_START: {
11 | return {
12 | ...state,
13 | loading: true
14 | };
15 | }
16 | case GET_USERS_SUCCESS: {
17 | return {
18 | ...state,
19 | loading: false,
20 | users: [...action.payload]
21 | };
22 | }
23 | case GET_USERS_FAIL: {
24 | return {
25 | ...state,
26 | loading: false,
27 | }
28 | }
29 | default: {
30 | return {
31 | ...state
32 | };
33 | }
34 | }
35 | };
36 |
37 | export default UsersReducer;
38 |
--------------------------------------------------------------------------------
/src/app/services/jwtAuthService.js:
--------------------------------------------------------------------------------
1 | import instance from "./../../axios";
2 |
3 | class JwtAuthService {
4 | loginWithUsernameAndPassword = (username, password) => {
5 | return instance
6 | .post(`/api/auth/login`, {
7 | name: username,
8 | password,
9 | })
10 | .then(({ data }) => {
11 | this.setSession(data.access_token);
12 | return instance.get(
13 | `/api/users/profile`
14 | );
15 | })
16 | .then(({ data }) => {
17 | this.setUser({
18 | userId: data.id,
19 | displayName: data.name,
20 | role: data.admin ? "admin" : "user",
21 | });
22 | return {
23 | userId: data.id,
24 | displayName: data.name,
25 | role: data.admin ? "admin" : "user",
26 | };
27 | });
28 | };
29 |
30 | // You need to send http requst with existing token to your server to check token is valid
31 | // This method is being used when user logged in & app is reloaded
32 | loginWithToken = () => {
33 | if (window.localStorage.getItem("jwt_token")) {
34 | return instance
35 | .get(`/api/users/profile`)
36 | .then(({ data }) => {
37 | this.setUser({
38 | userId: data.id,
39 | displayName: data.name,
40 | role: data.admin ? "admin" : "user",
41 | });
42 | this.setSession(window.localStorage.getItem("jwt_token"));
43 | return {
44 | userId: data.id,
45 | displayName: data.name,
46 | role: data.admin ? "admin" : "user",
47 | };
48 | });
49 | }
50 | };
51 |
52 | logout = () => {
53 | this.setSession(null);
54 | this.removeUser();
55 | };
56 |
57 | // Set token to all http request header, so you don't need to attach everytime
58 | setSession = (token) => {
59 | if (token) {
60 | instance.defaults.headers.common["Authorization"] = "bearer " + token;
61 | window.localStorage.setItem("jwt_token", token);
62 | } else {
63 | window.localStorage.removeItem("jwt_token");
64 | delete instance.defaults.headers.common["Authorization"];
65 | }
66 | };
67 |
68 | // Save user to localstorage
69 | setUser = (user) => {
70 | window.localStorage.setItem("auth_user", JSON.stringify(user));
71 | };
72 | // Remove user from localstorage
73 | removeUser = () => {
74 | window.localStorage.removeItem("auth_user");
75 | };
76 | }
77 |
78 | export default new JwtAuthService();
79 |
--------------------------------------------------------------------------------
/src/app/ui/AdvanceTable.jsx:
--------------------------------------------------------------------------------
1 | import MaterialTable from "material-table";
2 | import React from "react";
3 | import { useTranslation } from "react-i18next";
4 |
5 | const AdvanceTable = (props) => {
6 | const { t } = useTranslation();
7 | return (
8 |
64 | );
65 | };
66 |
67 | export default AdvanceTable;
68 |
--------------------------------------------------------------------------------
/src/app/ui/BlockButton.jsx:
--------------------------------------------------------------------------------
1 | import { Button, CircularProgress, Icon } from "@material-ui/core";
2 | import React from "react";
3 |
4 | const BlockButton = (props) => {
5 | return (
6 |
7 |
8 |
18 | {props.loading ? (
19 |
30 | ) : null}
31 |
32 |
33 | );
34 | };
35 |
36 | export default BlockButton;
37 |
--------------------------------------------------------------------------------
/src/app/ui/Spinner.jsx:
--------------------------------------------------------------------------------
1 | import { CircularProgress } from "@material-ui/core";
2 | import React from "react";
3 |
4 | const Spinner = (props) => (
5 |
6 |
7 |
8 | );
9 |
10 | export default Spinner;
11 |
--------------------------------------------------------------------------------
/src/app/views/bot/AddRightModel.jsx:
--------------------------------------------------------------------------------
1 | import React, { useState } from "react";
2 | import Button from "@material-ui/core/Button";
3 | import TextField from "@material-ui/core/TextField";
4 | import Dialog from "@material-ui/core/Dialog";
5 | import DialogActions from "@material-ui/core/DialogActions";
6 | import DialogContent from "@material-ui/core/DialogContent";
7 | import DialogTitle from "@material-ui/core/DialogTitle";
8 | import { connect } from "react-redux";
9 | import { FormControlLabel, Switch } from "@material-ui/core";
10 | import { addBotRights } from "app/redux/actions/BotRightsActions";
11 | import { useTranslation } from "react-i18next";
12 |
13 | function AddRightModel(props) {
14 | const [value, setValue] = useState("");
15 | const [admin, setAdmin] = useState(false);
16 | const { t } = useTranslation();
17 |
18 | let field = (
19 |
setValue(+e.target.value)}
23 | id={props.type}
24 | value={value}
25 | label="Groupid"
26 | type="number"
27 | fullWidth
28 | />
29 | );
30 | if (props.type === "useruid") {
31 | field = (
32 | setValue(e.target.value)}
36 | id={props.type}
37 | value={value}
38 | label="Useruid"
39 | type="text"
40 | fullWidth
41 | />
42 | );
43 | }
44 |
45 | let adminField = null;
46 | if (props.user.role === "admin") {
47 | adminField = (
48 | setAdmin(!admin)}
53 | name="checkedB"
54 | color="primary"
55 | />
56 | }
57 | label="Admin"
58 | />
59 | );
60 | }
61 |
62 | return (
63 |
94 | );
95 | }
96 |
97 | const mapStateToProps = (props) => {
98 | return {
99 | user: props.user,
100 | };
101 | };
102 |
103 | const mapDispatchToProps = (dispatch) => {
104 | return {
105 | onAddRight: (id, data) => dispatch(addBotRights(id, data)),
106 | };
107 | };
108 |
109 | export default connect(mapStateToProps, mapDispatchToProps)(AddRightModel);
110 |
--------------------------------------------------------------------------------
/src/app/views/bot/BotRoutes.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | const botRoutes = [
4 | {
5 | exact: true,
6 | path: "/bot/:id/edit",
7 | component: React.lazy(() => import("./Bot")),
8 | },
9 | ];
10 |
11 | export default botRoutes;
12 |
--------------------------------------------------------------------------------
/src/app/views/bot/RightsTable.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component, Fragment } from "react";
2 | import { Button } from "@material-ui/core";
3 | import { connect } from "react-redux";
4 | import { delBotRights } from "app/redux/actions/BotRightsActions";
5 | import AdvanceTable from "app/ui/AdvanceTable";
6 |
7 | class RightsTable extends Component {
8 | state = {
9 | useruid: [
10 | {
11 | title: "username",
12 | field: "id",
13 | cellStyle: { textAlign: "center" },
14 | render: (rowData) => {
15 | return rowData.admin ? (
16 |
17 | {rowData.id}
18 |
21 |
22 | ) : (
23 | rowData.id
24 | );
25 | },
26 | },
27 | ],
28 | groupid: [
29 | {
30 | title: "id",
31 | field: "id",
32 | cellStyle: { textAlign: "center" },
33 | render: (rowData) => {
34 | return rowData.admin ? (
35 |
36 | {rowData.id}
37 |
40 |
41 | ) : (
42 | rowData.id
43 | );
44 | },
45 | },
46 | ],
47 | data: [],
48 | };
49 |
50 | shouldComponentUpdate(nextProps, nextState) {
51 | if (this.props.data !== nextProps.data) {
52 | return true;
53 | }
54 | return false;
55 | }
56 |
57 | render() {
58 | return (
59 |
60 |
69 | this.props.onRightDel(this.props.botId, {
70 | type: this.props.type,
71 | admin: rowData.admin,
72 | value: rowData.id,
73 | }),
74 | },
75 | ]}
76 | options={{
77 | paginationType: "stepped",
78 | headerStyle: {
79 | display: "none",
80 | },
81 | }}
82 | />
83 |
84 | );
85 | }
86 | }
87 |
88 | const mapDispatchToProps = (dispatch) => {
89 | return {
90 | onRightDel: (id, data) => dispatch(delBotRights(id, data)),
91 | };
92 | };
93 |
94 | export default connect(null, mapDispatchToProps)(RightsTable);
95 |
--------------------------------------------------------------------------------
/src/app/views/bot/SettingFormHeader.jsx:
--------------------------------------------------------------------------------
1 | import { Button, Icon, IconButton } from "@material-ui/core";
2 | import {
3 | startBotAndReloadBot,
4 | stopBotAndReloadBot,
5 | } from "app/redux/actions/BotSettingsActions";
6 | import React, { Fragment } from "react";
7 | import { useTranslation } from "react-i18next";
8 | import { connect } from "react-redux";
9 | import Spinner from "../../ui/Spinner";
10 |
11 | const SettingsFormHeader = (props) => {
12 | const { t } = useTranslation();
13 | let style = {};
14 | let status = "UNKNOW";
15 | switch (props.botStatus) {
16 | case 0:
17 | status = "OFFLINE";
18 | style = {
19 | color: "#f44336 ",
20 | border: "1px solid rgba(244, 67, 54, 0.5)",
21 | };
22 | break;
23 | case 2:
24 | style = {
25 | color: "#08ad6c ",
26 | border: "1px solid rgba(8, 173, 108, 0.5)",
27 | };
28 | status = "ONLINE";
29 | break;
30 | case 1:
31 | status = "DISCONNECTED";
32 | style = {
33 | color: "#ff9e43 ",
34 | border: "1px solid rgba(255, 158, 67, 0.5)",
35 | };
36 | break;
37 | default:
38 | return style;
39 | }
40 |
41 | const handlePowerOnOff = (id, status) => {
42 | switch (status) {
43 | case 0:
44 | return props.onStartBot(id);
45 | case 1:
46 | return props.onStopBot(id);
47 | case 2:
48 | return props.onStopBot(id);
49 | default:
50 | return true;
51 | }
52 | };
53 |
54 | let controls = (
55 |
56 |
59 | handlePowerOnOff(props.botId, props.botStatus)}
61 | aria-label="Delete"
62 | >
63 | power_settings_new
64 |
65 |
66 | );
67 | if (props.loading) {
68 | controls = (
69 |
70 |
71 |
72 | );
73 | }
74 |
75 | return (
76 |
77 |
78 |
{t("bot-settings-title")}
79 | {controls}
80 |
81 | Bot ID: {props.botId}
82 |
83 | );
84 | };
85 |
86 | const mapDispatchToProps = (dispatch) => {
87 | return {
88 | onStartBot: (id) => dispatch(startBotAndReloadBot(id)),
89 | onStopBot: (id) => dispatch(stopBotAndReloadBot(id)),
90 | };
91 | };
92 |
93 | export default connect(null, mapDispatchToProps)(SettingsFormHeader);
94 |
--------------------------------------------------------------------------------
/src/app/views/botcreate/BotCreateRoutes.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { authRoles } from "../../auth/authRoles";
3 |
4 | const botCreateRoutes = [
5 | {
6 | exact: true,
7 | path: "/createbot",
8 | component: React.lazy(() => import("./BotCreate")),
9 | auth: authRoles.admin,
10 | },
11 | ];
12 |
13 | export default botCreateRoutes;
14 |
--------------------------------------------------------------------------------
/src/app/views/dashboard/Analytics.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from "react";
2 | import { Card, Grid } from "@material-ui/core";
3 | import { connect } from "react-redux";
4 |
5 | import { withStyles } from "@material-ui/styles";
6 | import { getBotsList } from "app/redux/actions/BotsActions";
7 | import UserBotsTable from "./UserBotsTable";
8 | import OtherBotsTable from "./OtherBotsTable";
9 | import Spinner from "../../ui/Spinner";
10 | import StatsCard from "./StatsCard";
11 |
12 | class Dashboard1 extends Component {
13 | state = {
14 | myBots: [],
15 | otherBots: [],
16 | };
17 |
18 | componentDidMount() {
19 | this.props.onFetchBots();
20 | }
21 |
22 | componentDidUpdate(previousProps, previousState) {
23 | if (previousProps.bots !== this.props.bots) {
24 | const filterMyBots = this.props.bots.filter(
25 | (el) => el.owner === this.props.user.userId
26 | );
27 | const filterOtherBots = this.props.bots.filter(
28 | (el) => el.owner !== this.props.user.userId
29 | );
30 | this.setState({
31 | myBots: [...filterMyBots],
32 | otherBots: [...filterOtherBots],
33 | });
34 | }
35 | }
36 |
37 | render() {
38 | let myBots = ;
39 | let otherBots = ;
40 | if (!this.props.loading) {
41 | myBots = ;
42 | otherBots = ;
43 | }
44 | return (
45 |
46 |
47 |
48 | {this.props.user.role === "admin" ? (
49 |
50 |
51 | {otherBots}
52 |
53 |
54 | ) : null}
55 |
56 |
57 | {myBots}
58 |
59 |
60 |
61 |
62 | );
63 | }
64 | }
65 |
66 | const mapStateToProps = (state) => {
67 | return {
68 | loading: state.bots.loading,
69 | bots: state.bots.bots,
70 | user: state.user,
71 | };
72 | };
73 |
74 | const mapDispatchToProps = (dispatch) => {
75 | return {
76 | onFetchBots: () => {
77 | dispatch(getBotsList());
78 | },
79 | };
80 | };
81 |
82 | export default connect(
83 | mapStateToProps,
84 | mapDispatchToProps
85 | )(withStyles({}, { withTheme: true })(Dashboard1));
86 |
--------------------------------------------------------------------------------
/src/app/views/dashboard/DashboardRoutes.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | const dashboardRoutes = [
4 | {
5 | exact: true,
6 | path: "/dashboard",
7 | component: React.lazy(() => import("./Analytics")),
8 | },
9 | ];
10 |
11 | export default dashboardRoutes;
12 |
--------------------------------------------------------------------------------
/src/app/views/dashboard/RemoveBotModal.jsx:
--------------------------------------------------------------------------------
1 | import {
2 | Button,
3 | Dialog,
4 | DialogActions,
5 | DialogContent,
6 | DialogTitle,
7 | } from "@material-ui/core";
8 | import { removeBot } from "app/redux/actions/BotSettingsActions";
9 | import React from "react";
10 | import { useTranslation } from "react-i18next";
11 | import { connect } from "react-redux";
12 |
13 | const RemoveBotModal = (props) => {
14 | const { t } = useTranslation();
15 | return (
16 |
42 | );
43 | };
44 |
45 | const mapDispatchToProps = (dispatch) => {
46 | return {
47 | onBotRemove: (botId) => dispatch(removeBot(botId)),
48 | };
49 | };
50 |
51 | export default connect(null, mapDispatchToProps)(RemoveBotModal);
52 |
--------------------------------------------------------------------------------
/src/app/views/dashboard/StatsCard.jsx:
--------------------------------------------------------------------------------
1 | import React, { Fragment } from "react";
2 | import { Grid, Card, Icon, Fab } from "@material-ui/core";
3 | import { useTranslation } from "react-i18next";
4 |
5 |
6 | const StatsCard = (props) => {
7 |
8 | const { t } = useTranslation();
9 |
10 | return (
11 |
12 |
13 |
14 |
15 |
19 | power
20 |
21 |
{t('bot-stats.online')}{props.data.reduce((acc, cur) => cur.status === 2 ? ++acc : acc, 0)}
22 |
23 |
24 |
25 |
26 |
27 |
28 |
32 | power_off
33 |
34 |
{t('bot-stats.offline')}{props.data.reduce((acc, cur) => cur.status !== 2 ? ++acc : acc, 0)}
35 |
36 |
37 |
38 |
39 | );
40 | };
41 |
42 | export default StatsCard;
--------------------------------------------------------------------------------
/src/app/views/dashboard/TransferBotModal.jsx:
--------------------------------------------------------------------------------
1 | import {
2 | Button,
3 | CircularProgress,
4 | Dialog,
5 | DialogActions,
6 | DialogContent,
7 | DialogTitle,
8 | NativeSelect,
9 | } from "@material-ui/core";
10 | import { transferBot } from "app/redux/actions/BotSettingsActions";
11 | import { getUsersList } from "app/redux/actions/UsersActions";
12 | import React, { Component } from "react";
13 | import { withTranslation } from "react-i18next";
14 | import { connect } from "react-redux";
15 |
16 | class TransferBotModal extends Component {
17 | state = {
18 | uuid: this.props.user.userId,
19 | };
20 |
21 | componentDidMount() {
22 | this.props.onUsersFetch();
23 | }
24 |
25 | handleChange = (event) => {
26 | const name = event.target.name;
27 | this.setState({
28 | ...this.state,
29 | [name]: event.target.value,
30 | });
31 | };
32 |
33 | render() {
34 | const { t } = this.props;
35 | return (
36 |
81 | );
82 | }
83 | }
84 |
85 | const mapStateToProps = (state) => {
86 | return {
87 | user: state.user,
88 | users: state.users.users,
89 | loading: state.users.loading,
90 | };
91 | };
92 |
93 | const mapDispatchToProps = (dispatch) => {
94 | return {
95 | onUsersFetch: () => dispatch(getUsersList()),
96 | onOwnerChange: (botId, userId) => dispatch(transferBot(botId, userId)),
97 | };
98 | };
99 |
100 | export default connect(
101 | mapStateToProps,
102 | mapDispatchToProps
103 | )(withTranslation()(TransferBotModal));
104 |
--------------------------------------------------------------------------------
/src/app/views/sessions/NotFound.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from "react";
2 | import { Button } from "@material-ui/core";
3 | import { withStyles } from "@material-ui/styles";
4 |
5 | const styles = theme => ({
6 | flexCenter: {
7 | display: "flex",
8 | justifyContent: "center",
9 | alignItems: "center"
10 | },
11 | wrapper: {
12 | width: "100%",
13 | height: "100vh"
14 | },
15 | inner: {
16 | flexDirection: "column",
17 | maxWidth: "320px"
18 | }
19 | });
20 |
21 | class NotFound extends Component {
22 | state = {};
23 |
24 | render() {
25 | const { classes } = this.props;
26 | return (
27 |
28 |
29 |

34 |
42 |
43 |
44 | );
45 | }
46 | }
47 |
48 | export default withStyles(styles, { withTheme: true })(NotFound);
49 |
--------------------------------------------------------------------------------
/src/app/views/sessions/SessionRoutes.js:
--------------------------------------------------------------------------------
1 | import SignIn from "./SignIn";
2 | import NotFound from "./NotFound";
3 |
4 | const settings = {
5 | activeLayout: "layout1",
6 | layout1Settings: {
7 | topbar: {
8 | show: false,
9 | },
10 | leftSidebar: {
11 | show: false,
12 | mode: "close",
13 | },
14 | },
15 | layout2Settings: {
16 | mode: "full",
17 | topbar: {
18 | show: false,
19 | },
20 | navbar: { show: false },
21 | },
22 | secondarySidebar: { show: false },
23 | footer: { show: false },
24 | };
25 |
26 | const sessionRoutes = [
27 | {
28 | path: "/session/signin",
29 | component: SignIn,
30 | settings,
31 | },
32 | {
33 | path: "/session/404",
34 | component: NotFound,
35 | settings,
36 | },
37 | ];
38 |
39 | export default sessionRoutes;
40 |
--------------------------------------------------------------------------------
/src/app/views/users/PasswordChangeModal.jsx:
--------------------------------------------------------------------------------
1 | import {
2 | Button,
3 | Dialog,
4 | DialogActions,
5 | DialogContent,
6 | DialogTitle,
7 | Input,
8 | } from "@material-ui/core";
9 | import { transferBot } from "app/redux/actions/BotSettingsActions";
10 | import { getUsersList } from "app/redux/actions/UsersActions";
11 | import React, { useState } from "react";
12 | import { useTranslation } from "react-i18next";
13 | import { connect } from "react-redux";
14 |
15 | const TransferBotModal = (props) => {
16 | const [password, setPassword] = useState("");
17 |
18 | const { t } = useTranslation();
19 |
20 | return (
21 |
54 | );
55 | };
56 |
57 | const mapStateToProps = (state) => {
58 | return {
59 | user: state.user,
60 | users: state.users.users,
61 | loading: state.users.loading,
62 | };
63 | };
64 |
65 | const mapDispatchToProps = (dispatch) => {
66 | return {
67 | onUsersFetch: () => dispatch(getUsersList()),
68 | onOwnerChange: (botId, userId) => dispatch(transferBot(botId, userId)),
69 | };
70 | };
71 |
72 | export default connect(mapStateToProps, mapDispatchToProps)(TransferBotModal);
73 |
--------------------------------------------------------------------------------
/src/app/views/users/UsersRoutes.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { authRoles } from "../../auth/authRoles";
3 |
4 | const usersRoutes = [
5 | {
6 | exact: true,
7 | path: "/users",
8 | component: React.lazy(() => import("./Users")),
9 | auth: authRoles.admin,
10 | },
11 | ];
12 |
13 | export default usersRoutes;
14 |
--------------------------------------------------------------------------------
/src/axios.js:
--------------------------------------------------------------------------------
1 | import axios from "axios";
2 |
3 | const instance = axios.create();
4 |
5 | if (window.localStorage.getItem("jwt_token")) {
6 | instance.defaults.headers.common["Authorization"] =
7 | "bearer " + window.localStorage.getItem("jwt_token");
8 | }
9 |
10 | export default instance;
11 |
--------------------------------------------------------------------------------
/src/history.js:
--------------------------------------------------------------------------------
1 | import { createBrowserHistory } from 'history';
2 |
3 | export default createBrowserHistory();
--------------------------------------------------------------------------------
/src/i18n.js:
--------------------------------------------------------------------------------
1 | import i18n from "i18next";
2 | import { initReactI18next } from "react-i18next";
3 |
4 | import Backend from "i18next-http-backend";
5 | import LanguageDetector from "i18next-browser-languagedetector";
6 | // don't want to use this?
7 | // have a look at the Quick start guide
8 | // for passing in lng and translations on init
9 |
10 | i18n
11 | // load translation using http -> see /public/locales (i.e. https://github.com/i18next/react-i18next/tree/master/example/react/public/locales)
12 | // learn more: https://github.com/i18next/i18next-http-backend
13 | .use(Backend)
14 | // detect user language
15 | // learn more: https://github.com/i18next/i18next-browser-languageDetector
16 | .use(LanguageDetector)
17 | // pass the i18n instance to react-i18next.
18 | .use(initReactI18next)
19 | // init i18next
20 | // for all options read: https://www.i18next.com/overview/configuration-options
21 | .init({
22 | fallbackLng: "en",
23 | debug: true,
24 |
25 | interpolation: {
26 | escapeValue: false, // not needed for react as it escapes by default
27 | },
28 | });
29 |
30 | export default i18n;
31 |
--------------------------------------------------------------------------------
/src/index.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import ReactDOM from "react-dom";
3 | import "./_index.scss";
4 | import "./i18n";
5 |
6 | import * as serviceWorker from "./serviceWorker";
7 | import App from "./app/App";
8 |
9 | // cssVars();
10 |
11 | ReactDOM.render(, document.getElementById("root"));
12 |
13 | // for IE-11 support un-comment cssVars() and it's import in this file
14 | // and in MatxTheme file
15 |
16 | // If you want your app to work offline and load faster, you can change
17 | // unregister() to register() below. Note this comes with some pitfalls.
18 | // Learn more about service workers: https://bit.ly/CRA-PWA
19 | serviceWorker.unregister();
20 |
--------------------------------------------------------------------------------
/src/matx/components/Breadcrumb.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { Icon, Breadcrumbs, Hidden } from "@material-ui/core";
3 | import { NavLink } from "react-router-dom";
4 |
5 | const Breadcrumb = ({ routeSegments }) => {
6 | return (
7 |
8 | {routeSegments ? (
9 |
10 |
11 | {routeSegments[routeSegments.length - 1]["name"]}
12 |
13 | |
14 |
15 | ) : null}
16 | navigate_next}
18 | className="flex items-center position-relative"
19 | >
20 |
21 |
22 | home
23 |
24 |
25 | {routeSegments
26 | ? routeSegments.map((route, index) => {
27 | return index !== routeSegments.length - 1 ? (
28 |
29 | {route.name}
30 |
31 | ) : (
32 |
33 | {route.name}
34 |
35 | );
36 | })
37 | : null}
38 |
39 |
40 | );
41 | };
42 |
43 | export default Breadcrumb;
44 |
--------------------------------------------------------------------------------
/src/matx/components/ConfirmationDialog.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { Dialog, Button } from "@material-ui/core";
3 |
4 | const ConfirmationDialog = ({
5 | open,
6 | onConfirmDialogClose,
7 | text,
8 | title = "confirm",
9 | onYesClick
10 | }) => {
11 | return (
12 |
35 | );
36 | };
37 |
38 | export default ConfirmationDialog;
39 |
--------------------------------------------------------------------------------
/src/matx/components/LoaderBounce.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | const LoaderBounce = () => {
4 | return (
5 |
14 | );
15 | };
16 |
17 | export default LoaderBounce;
18 |
--------------------------------------------------------------------------------
/src/matx/components/MatxHorizontalNav/MatxHorizontalNav.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { NavLink } from "react-router-dom";
3 | import { Icon } from "@material-ui/core";
4 | import { useSelector } from "react-redux";
5 |
6 | const MatxHorizontalNav = ({ max, className }) => {
7 | let navigation = useSelector(({ navigations }) => navigations);
8 |
9 | if (!navigation || !navigation.length) {
10 | return null;
11 | }
12 |
13 | if (max && navigation.length > max) {
14 | let childItem = {
15 | name: "More",
16 | icon: "more_vert",
17 | children: navigation.slice(max, navigation.length)
18 | };
19 | navigation = navigation.slice(0, max);
20 | navigation.push(childItem);
21 | }
22 |
23 | function renderLevels(levels) {
24 | return levels.map((item, key) => {
25 | if (item.children) {
26 | return (
27 |
28 |
29 | {item.icon && (
30 | {item.icon}
31 | )}
32 | {item.name}
33 |
34 | {renderLevels(item.children)}
35 |
36 | );
37 | } else {
38 | return (
39 |
40 |
41 | {item.icon && (
42 | {item.icon}
43 | )}
44 | {item.name}
45 |
46 |
47 | );
48 | }
49 | });
50 | }
51 |
52 | return (
53 |
54 |
{renderLevels(navigation)}
55 |
56 | );
57 | };
58 |
59 | export default MatxHorizontalNav;
60 |
--------------------------------------------------------------------------------
/src/matx/components/MatxListItem1.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import RectangleAvatar from "./RectangleAvatar";
3 | import { IconButton, Icon } from "@material-ui/core";
4 | import { withStyles } from "@material-ui/core/styles";
5 |
6 | const styles = {
7 | root: {
8 | borderRadius: "8px",
9 | cursor: "pointer",
10 | transition: "all 300ms ease",
11 | "&:hover": {
12 | background: "rgba(0,0,0, .08)",
13 | paddingLeft: "8px",
14 | overflow: "hidden",
15 | "& .action-icon, & .rectangle-box": {
16 | opacity: 1
17 | }
18 | },
19 | "& .action-icon, & .rectangle-box": {
20 | opacity: 0.76
21 | }
22 | }
23 | };
24 |
25 | const MatxListItem1 = ({
26 | title,
27 | subtitle,
28 | iconText,
29 | iconColor,
30 | bulletIcon,
31 | actionIcon,
32 | classes
33 | }) => {
34 | return (
35 |
36 |
41 |
42 |
43 |
{title}
44 | {subtitle}
45 |
46 |
47 | {actionIcon && (
48 |
49 | {actionIcon}
50 |
51 | )}
52 |
53 | );
54 | };
55 |
56 | export default withStyles(styles, { withTheme: true })(MatxListItem1);
57 |
--------------------------------------------------------------------------------
/src/matx/components/MatxLoadable/Loading.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { makeStyles } from "@material-ui/core/styles";
3 | import CircularProgress from "@material-ui/core/CircularProgress";
4 |
5 | const useStyles = makeStyles(theme => ({
6 | loading: {
7 | position: "fixed",
8 | left: 0,
9 | right: 0,
10 | top: "calc(50% - 20px)",
11 | margin: "auto",
12 | height: "40px",
13 | width: "40px",
14 | "& img": {
15 | position: "absolute",
16 | height: "25px",
17 | width: "auto",
18 | top: 0,
19 | bottom: 0,
20 | left: 0,
21 | right: 0,
22 | margin: "auto"
23 | }
24 | }
25 | }));
26 |
27 | const Loading = props => {
28 | const classes = useStyles();
29 |
30 | return (
31 |
32 |

33 |
34 |
35 | );
36 | };
37 |
38 | export default Loading;
39 |
--------------------------------------------------------------------------------
/src/matx/components/MatxLoadable/MatxLoadable.jsx:
--------------------------------------------------------------------------------
1 | import Loadable from "react-loadable";
2 | import Loading from "./Loading";
3 |
4 | const MatxLoadable = opts => {
5 | return Loadable(
6 | Object.assign(
7 | {
8 | loading: Loading,
9 | delay: 100,
10 | timeout: 10000
11 | },
12 | opts
13 | )
14 | );
15 | };
16 |
17 | export default MatxLoadable;
18 |
--------------------------------------------------------------------------------
/src/matx/components/MatxLoading/MatxLoading.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { makeStyles } from "@material-ui/core/styles";
3 | import CircularProgress from "@material-ui/core/CircularProgress";
4 |
5 | const useStyles = makeStyles(theme => ({
6 | loading: {
7 | position: "fixed",
8 | left: 0,
9 | right: 0,
10 | top: "calc(50% - 20px)",
11 | margin: "auto",
12 | height: "40px",
13 | width: "40px",
14 | "& img": {
15 | position: "absolute",
16 | height: "25px",
17 | width: "auto",
18 | top: 0,
19 | bottom: 0,
20 | left: 0,
21 | right: 0,
22 | margin: "auto"
23 | }
24 | }
25 | }));
26 |
27 | const Loading = props => {
28 | const classes = useStyles();
29 |
30 | return (
31 |
32 |

33 |
34 |
35 | );
36 | };
37 |
38 | export default Loading;
39 |
--------------------------------------------------------------------------------
/src/matx/components/MatxMenu.jsx:
--------------------------------------------------------------------------------
1 | import React, { Fragment } from "react";
2 | import Menu from "@material-ui/core/Menu";
3 |
4 | const MatxMenu = props => {
5 | const [anchorEl, setAnchorEl] = React.useState(null);
6 | const children = React.Children.toArray(props.children);
7 | let { shouldCloseOnItemClick = true, horizontalPosition = "left" } = props;
8 |
9 | const handleClick = event => {
10 | setAnchorEl(event.currentTarget);
11 | };
12 |
13 | const handleClose = () => {
14 | setAnchorEl(null);
15 | };
16 |
17 | return (
18 |
19 |
25 | {props.menuButton}
26 |
27 |
51 |
52 | );
53 | };
54 |
55 | export default MatxMenu;
56 |
--------------------------------------------------------------------------------
/src/matx/components/MatxProgressBar.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { Grid, LinearProgress, Typography } from "@material-ui/core";
3 | import { withStyles } from "@material-ui/styles";
4 |
5 | const CustomLinearProgress = withStyles(theme => ({
6 | root: {
7 | borderRadius: 2,
8 | background: "rgba(0, 0, 0, 0.1)"
9 | }
10 | }))(LinearProgress);
11 |
12 | const MatxProgressBar = ({
13 | value = 75,
14 | color = "primary",
15 | text = "",
16 | spacing = 2,
17 | coloredText = false,
18 | className
19 | }) => {
20 | return (
21 |
22 |
23 |
28 |
29 | {text !== "" && (
30 |
31 |
32 |
33 | {text}
34 |
35 |
36 |
37 | )}
38 |
39 | );
40 | };
41 |
42 | export default MatxProgressBar;
43 |
--------------------------------------------------------------------------------
/src/matx/components/MatxSearchBox.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from "react";
2 | import { Icon, IconButton } from "@material-ui/core";
3 | import { withStyles } from "@material-ui/core/styles";
4 |
5 | const styles = theme => ({
6 | root: {
7 | backgroundColor: theme.palette.primary.main,
8 | color: theme.palette.primary.contrastText,
9 | "&::placeholder": {
10 | color: theme.palette.primary.contrastText
11 | }
12 | }
13 | });
14 |
15 | class MatxSearchBox extends Component {
16 | state = {
17 | open: false
18 | };
19 |
20 | toggle = () => {
21 | this.setState({ open: !this.state.open });
22 | };
23 |
24 | render() {
25 | let { classes } = this.props;
26 | return (
27 |
28 | {!this.state.open && (
29 |
30 | search
31 |
32 | )}
33 |
34 | {this.state.open && (
35 |
38 |
44 |
45 | close
46 |
47 |
48 | )}
49 |
50 | );
51 | }
52 | }
53 |
54 | export default withStyles(styles, { withTheme: true })(MatxSearchBox);
55 |
--------------------------------------------------------------------------------
/src/matx/components/MatxSidenav/MatxSidenav.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from "react";
2 | import { isMobile } from "utils";
3 |
4 | class MatxSidenav extends Component {
5 | handleResizeRef;
6 |
7 | state = {
8 | mobile: isMobile()
9 | };
10 |
11 | handleWindowResize = () => {
12 | return event => {
13 | if (event.target.innerWidth < 768) {
14 | this.setState({ mobile: true });
15 | } else this.setState({ mobile: false });
16 | };
17 | };
18 |
19 | componentDidMount() {
20 | this.handleResizeRef = this.handleWindowResize();
21 | if (window) window.addEventListener("resize", this.handleResizeRef);
22 | }
23 |
24 | componentWillUnmount() {
25 | if (this.handleResizeRef)
26 | window.removeEventListener("resize", this.handleResizeRef);
27 | }
28 |
29 | render() {
30 | let {
31 | open,
32 | children,
33 | toggleSidenav,
34 | width = "220px",
35 | bgClass
36 | } = this.props;
37 |
38 | let { mobile } = this.state;
39 |
40 | return (
41 |
42 |
46 | {children}
47 |
48 | {open && mobile && (
49 |
50 | )}
51 |
52 | );
53 | }
54 | }
55 |
56 | export default MatxSidenav;
57 |
--------------------------------------------------------------------------------
/src/matx/components/MatxSidenav/MatxSidenavContainer.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | const MatxSidenavContainer = ({ children }) => {
4 | return {children}
;
5 | };
6 |
7 | export default MatxSidenavContainer;
8 |
--------------------------------------------------------------------------------
/src/matx/components/MatxSidenav/MatxSidenavContent.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | const MatxSidenavContent = ({ children }) => {
4 | return {children}
;
5 | };
6 |
7 | export default MatxSidenavContent;
8 |
--------------------------------------------------------------------------------
/src/matx/components/MatxSnackbar.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { IconButton, Icon, Snackbar } from "@material-ui/core";
3 |
4 | const MatxSnackbar = ({
5 | open,
6 | message,
7 | duration = 6000,
8 | horizontal = "center",
9 | vertical = "bottom",
10 | handleClose
11 | }) => {
12 | return (
13 | {message}}
25 | action={[
26 |
33 | close
34 |
35 | ]}
36 | />
37 | );
38 | };
39 |
40 | export default MatxSnackbar;
41 |
--------------------------------------------------------------------------------
/src/matx/components/MatxSuspense/MatxSuspense.jsx:
--------------------------------------------------------------------------------
1 | import React, { Suspense } from "react";
2 | import { MatxLoading } from "matx";
3 |
4 | const MatxSuspense = props => {
5 | return }>{props.children};
6 | };
7 |
8 | export default MatxSuspense;
9 |
--------------------------------------------------------------------------------
/src/matx/components/MatxToolbarMenu.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from "react";
2 | import { Icon, IconButton, Hidden } from "@material-ui/core";
3 | import { classList } from "utils";
4 |
5 | class MatxToolbarMenu extends Component {
6 | state = {
7 | open: false
8 | };
9 |
10 | handleToggle = () => {
11 | this.setState({ open: !this.state.open });
12 | };
13 |
14 | render() {
15 | let { offsetTop, children } = this.props;
16 |
17 | return (
18 |
24 |
25 |
26 | {this.state.open ? "close" : "more_vert"}
27 |
28 |
29 |
30 |
34 | {children}
35 |
36 |
37 | );
38 | }
39 | }
40 |
41 | export default MatxToolbarMenu;
42 |
--------------------------------------------------------------------------------
/src/matx/components/MatxVerticalNav/MatxVerticalNav.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { NavLink } from "react-router-dom";
3 | import { Icon } from "@material-ui/core";
4 | import TouchRipple from "@material-ui/core/ButtonBase";
5 | import MatxVerticalNavExpansionPanel from "./MatxVerticalNavExpansionPanel";
6 | import { withStyles } from "@material-ui/styles";
7 | import { useSelector } from "react-redux";
8 | import { useTranslation } from "react-i18next";
9 |
10 | const styles = (theme) => ({
11 | expandIcon: {
12 | transition: "transform 225ms cubic-bezier(0, 0, 0.2, 1) 0ms",
13 | transform: "rotate(90deg)",
14 | },
15 | collapseIcon: {
16 | transition: "transform 225ms cubic-bezier(0, 0, 0.2, 1) 0ms",
17 | transform: "rotate(0deg)",
18 | },
19 | });
20 |
21 | const MatxVerticalNav = (props) => {
22 | const navigations = useSelector(({ navigations }) => navigations);
23 | const { t } = useTranslation();
24 | const renderLevels = (data) => {
25 | return data.map((item, index) => {
26 | if (item.children) {
27 | return (
28 |
29 | {renderLevels(item.children)}
30 |
31 | );
32 | } else if (item.type === "extLink") {
33 | return (
34 |
41 |
42 | {(() => {
43 | if (item.icon) {
44 | return (
45 | {item.icon}
46 | );
47 | } else {
48 | return (
49 | {item.iconText}
50 | );
51 | }
52 | })()}
53 | {t(item.name)}
54 |
55 | {item.badge && (
56 |
57 | {item.badge.value}
58 |
59 | )}
60 |
61 |
62 | );
63 | } else {
64 | return (
65 |
66 |
67 | {(() => {
68 | if (item.icon) {
69 | return (
70 | {item.icon}
71 | );
72 | } else {
73 | return (
74 | {item.iconText}
75 | );
76 | }
77 | })()}
78 | {t(item.name)}
79 |
80 | {item.badge && (
81 |
82 | {item.badge.value}
83 |
84 | )}
85 |
86 |
87 | );
88 | }
89 | });
90 | };
91 |
92 | return {renderLevels(navigations)}
;
93 | };
94 |
95 | export default withStyles(styles)(MatxVerticalNav);
96 |
--------------------------------------------------------------------------------
/src/matx/components/MatxVerticalNav/MatxVerticalNavExpansionPanel.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from "react";
2 | import { Icon } from "@material-ui/core";
3 | import { withStyles } from "@material-ui/core/styles";
4 | import TouchRipple from "@material-ui/core/ButtonBase";
5 | import { withRouter } from "react-router-dom";
6 | import { classList } from "utils";
7 |
8 | const styles = theme => {
9 | return {
10 | expandIcon: {
11 | transition: "transform 0.3s cubic-bezier(0, 0, 0.2, 1) 0ms",
12 | transform: "rotate(90deg)"
13 | // marginRight: "16px"
14 | },
15 | collapseIcon: {
16 | transition: "transform 0.3s cubic-bezier(0, 0, 0.2, 1) 0ms",
17 | transform: "rotate(0deg)"
18 | // marginRight: "16px"
19 | },
20 | "expansion-panel": {
21 | overflow: "hidden",
22 | transition: "max-height 0.3s cubic-bezier(0, 0, 0.2, 1)"
23 | },
24 | highlight: {
25 | background: theme.palette.primary.main
26 | }
27 | };
28 | };
29 |
30 | class MatxVerticalNavExpansionPanel extends Component {
31 | state = {
32 | collapsed: true
33 | };
34 | elementRef = React.createRef();
35 |
36 | componentHeight = 0;
37 |
38 | handleClick = () => {
39 | this.setState({ collapsed: !this.state.collapsed });
40 | };
41 |
42 | calcaulateHeight(node) {
43 | if (node.name !== "child") {
44 | for (let child of node.children) {
45 | this.calcaulateHeight(child);
46 | }
47 | }
48 | this.componentHeight += node.clientHeight;
49 | return;
50 | }
51 | componentDidMount() {
52 | let { location } = this.props;
53 | this.calcaulateHeight(this.elementRef);
54 |
55 | // OPEN DROPDOWN IF CHILD IS ACTIVE
56 | for (let child of this.elementRef.children) {
57 | if (child.getAttribute("href") === location.pathname) {
58 | this.setState({ collapsed: false });
59 | }
60 | }
61 | }
62 | render() {
63 | let { collapsed } = this.state;
64 | let { classes, children } = this.props;
65 | let { name, icon, iconText, badge } = this.props.item;
66 | return (
67 |
68 |
75 |
76 | {(icon && {icon})}
77 | {(iconText && {iconText})}
78 | {name}
79 |
80 | {badge && (
81 | {badge.value}
82 | )}
83 |
90 | chevron_right
91 |
92 |
93 |
94 |
(this.elementRef = el)}
96 | className={classes["expansion-panel"] + " submenu"}
97 | style={
98 | collapsed
99 | ? { maxHeight: "0px" }
100 | : { maxHeight: this.componentHeight + "px" }
101 | }
102 | >
103 | {children}
104 |
105 |
106 | );
107 | }
108 | }
109 |
110 | export default withRouter(withStyles(styles)(MatxVerticalNavExpansionPanel));
111 |
--------------------------------------------------------------------------------
/src/matx/components/RectangleAvatar.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { Icon } from "@material-ui/core";
3 |
4 | const RectangleAvatar = ({ color = "primary", icon, textIcon, style }) => {
5 | return (
6 |
10 | {textIcon ? (
11 |
{textIcon}
12 | ) : (
13 | {icon}
14 | )}
15 |
16 | );
17 | };
18 |
19 | export default RectangleAvatar;
20 |
--------------------------------------------------------------------------------
/src/matx/components/RichTextEditor.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import PropTypes from "prop-types";
3 | import ReactQuill from "react-quill";
4 |
5 | /*
6 | * Simple editor component that takes placeholder text as a prop
7 | */
8 |
9 | const RichTextEditor = ({ content, placeholder, handleContentChange }) => {
10 | return (
11 |
19 | );
20 | };
21 |
22 | /*
23 | * Quill modules to attach to editor
24 | * See https://quilljs.com/docs/modules/ for complete options
25 | */
26 | RichTextEditor.modules = {
27 | toolbar: [
28 | [{ font: [] }],
29 | [{ size: ["small", false, "large", "huge"] }], // custom dropdown
30 | [{ header: [1, 2, 3, 4, 5, 6, false] }],
31 |
32 | ["bold", "italic", "underline", "strike"], // toggled buttons
33 | ["blockquote", "code-block", "link"],
34 |
35 | [{ script: "sub" }, { script: "super" }], // superscript/subscript
36 | [{ color: [] }, { background: [] }], // dropdown with defaults from theme
37 | [{ align: [] }],
38 |
39 | ["image", "video"],
40 |
41 | [{ header: 1 }, { header: 2 }], // custom button values
42 | [{ list: "ordered" }, { list: "bullet" }],
43 | [{ indent: "-1" }, { indent: "+1" }], // outdent/indent
44 | [{ direction: "rtl" }], // text direction
45 |
46 | ["clean"]
47 | ],
48 | clipboard: {
49 | // toggle to add extra line breaks when pasting HTML:
50 | matchVisual: true
51 | }
52 | };
53 |
54 | /*
55 | * Quill editor formats
56 | * See https://quilljs.com/docs/formats/
57 | */
58 | RichTextEditor.formats = [
59 | "align",
60 | "background",
61 | "bold",
62 | "blockquote",
63 | "bullet",
64 | "color",
65 | "code",
66 | "code-block",
67 | "clean",
68 | "direction",
69 | "font",
70 | "header",
71 | "italic",
72 | "indent",
73 | "image",
74 | "list",
75 | "link",
76 | "size",
77 | "strike",
78 | "script",
79 | "underline",
80 | "video"
81 | ];
82 |
83 | /*
84 | * PropType validation
85 | */
86 | RichTextEditor.propTypes = {
87 | placeholder: PropTypes.string
88 | };
89 |
90 | export default RichTextEditor;
91 |
--------------------------------------------------------------------------------
/src/matx/components/cards/CardWidget1.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { Card, Icon, Button, Divider } from "@material-ui/core";
3 |
4 | const CardWidget1 = ({ backgroundClass }) => {
5 | return (
6 |
12 |
13 |
14 | person
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
Last week
23 |
New Users
24 |
200
25 |
26 |
27 | );
28 | };
29 |
30 | export default CardWidget1;
31 |
--------------------------------------------------------------------------------
/src/matx/components/cards/SimpleCard.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { Card } from "@material-ui/core";
3 | import { classList } from "utils";
4 |
5 | const SimpleCard = ({ children, title, subtitle, icon }) => {
6 | return (
7 |
8 |
14 | {title}
15 |
16 | {subtitle && {subtitle}
}
17 | {children}
18 |
19 | );
20 | };
21 |
22 | export default SimpleCard;
23 |
--------------------------------------------------------------------------------
/src/matx/index.js:
--------------------------------------------------------------------------------
1 | export { default as Breadcrumb } from "./components/Breadcrumb";
2 | export { default as MatxMenu } from "./components/MatxMenu";
3 | export { default as MatxToolbarMenu } from "./components/MatxToolbarMenu";
4 | export { default as MatxLoading } from "./components/MatxLoading/MatxLoading";
5 | export { default as MatxSuspense } from "./components/MatxSuspense/MatxSuspense";
6 | export { default as MatxSearchBox } from "./components/MatxSearchBox";
7 | export { default as MatxVerticalNav } from "./components/MatxVerticalNav/MatxVerticalNav";
8 | export { default as MatxVerticalNavExpansionPanel } from "./components/MatxVerticalNav/MatxVerticalNavExpansionPanel";
9 | export { default as MatxHorizontalNav } from "./components/MatxHorizontalNav/MatxHorizontalNav";
10 | export { default as MatxSidenavContainer } from "./components/MatxSidenav/MatxSidenavContainer";
11 | export { default as MatxSidenav } from "./components/MatxSidenav/MatxSidenav";
12 | export { default as MatxSidenavContent } from "./components/MatxSidenav/MatxSidenavContent";
13 |
14 | export { default as RectangleAvatar } from "./components/RectangleAvatar";
15 | export { default as MatxListItem1 } from "./components/MatxListItem1";
16 | export { default as MatxSnackbar } from "./components/MatxSnackbar";
17 |
18 | export { default as ConfirmationDialog } from "./components/ConfirmationDialog";
19 | export { default as MatxProgressBar } from "./components/MatxProgressBar";
20 | export { default as SimpleCard } from "./components/cards/SimpleCard";
21 |
--------------------------------------------------------------------------------
/src/matx/theme/EchartTheme.jsx:
--------------------------------------------------------------------------------
1 | import echarts from "echarts";
2 |
3 | export const EchartTheme = MuiTheme => ({
4 | backgroundColor: MuiTheme.palette.background.paper,
5 | // Global palette:
6 | color: [
7 | "#7467EF",
8 | "#ABA4F4",
9 | "#D3D0F4",
10 | "rgba(0, 255, 33, 1)",
11 | "#91c7ae",
12 | "#749f83",
13 | "#ca8622",
14 | "#bda29a",
15 | "#6e7074",
16 | "#546570",
17 | "#c4ccd3"
18 | ],
19 | series: [
20 | {
21 | type: "bar"
22 | // A palette only work for the series:
23 | // itemStyle: {
24 | // normal: {
25 | // color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
26 | // { offset: 0, color: "#83bff6" },
27 | // { offset: 0.5, color: "#188df0" },
28 | // { offset: 1, color: "#188df0" }
29 | // ])
30 | // },
31 | // emphasis: {
32 | // color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
33 | // { offset: 0, color: "#2378f7" },
34 | // { offset: 0.7, color: "#2378f7" },
35 | // { offset: 1, color: "#83bff6" }
36 | // ])
37 | // }
38 | // }
39 | },
40 | {
41 | type: "pie",
42 | // A palette only work for the series:
43 | color: [
44 | "#37A2DA",
45 | "#32C5E9",
46 | "#67E0E3",
47 | "#9FE6B8",
48 | "#FFDB5C",
49 | "#ff9f7f",
50 | "#fb7293",
51 | "#E062AE",
52 | "#E690D1",
53 | "#e7bcf3",
54 | "#9d96f5",
55 | "#8378EA",
56 | "#96BFFF"
57 | ]
58 | },
59 | {
60 | type: "line",
61 | color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
62 | { offset: 0, color: "#83bff6" },
63 | { offset: 0.5, color: "#188df0" },
64 | { offset: 1, color: "#188df0" }
65 | ])
66 | }
67 | ]
68 | });
69 |
--------------------------------------------------------------------------------
/src/styles/_app.scss:
--------------------------------------------------------------------------------
1 | @import "./variables";
2 | @import "./mixins";
3 | @import "./reboot";
4 | @import "./utilities/utilities";
5 | @import "~perfect-scrollbar/css/perfect-scrollbar.css";
6 | @import "~react-vis/dist/style";
7 | @import "./components/index";
8 | @import "./layouts/index";
9 | @import "./views/index";
10 |
--------------------------------------------------------------------------------
/src/styles/_mixins.scss:
--------------------------------------------------------------------------------
1 | // Spacing
2 |
3 | @mixin generate-margin-padding-in-rem($from, $to) {
4 | @for $i from $from through $to {
5 | .m-#{$i} {
6 | margin: #{$i * 0.25}rem;
7 | }
8 | .mt-#{$i} {
9 | margin-top: #{$i * 0.25}rem !important;
10 | }
11 | .mr-#{$i} {
12 | margin-right: #{$i * 0.25}rem !important;
13 | }
14 | .mb-#{$i} {
15 | margin-bottom: #{$i * 0.25}rem !important;
16 | }
17 | .ml-#{$i} {
18 | margin-left: #{$i * 0.25}rem !important;
19 | }
20 | .mx-#{$i} {
21 | margin-left: #{$i * 0.25}rem !important;
22 | margin-right: #{$i * 0.25}rem !important;
23 | }
24 | .my-#{$i} {
25 | margin-top: #{$i * 0.25}rem !important;
26 | margin-bottom: #{$i * 0.25}rem !important;
27 | }
28 |
29 | .p-#{$i} {
30 | padding: #{$i * 0.25}rem !important;
31 | }
32 | .pt-#{$i} {
33 | padding-top: #{$i * 0.25}rem !important;
34 | }
35 | .pr-#{$i} {
36 | padding-right: #{$i * 0.25}rem !important;
37 | }
38 | .pb-#{$i} {
39 | padding-bottom: #{$i * 0.25}rem !important;
40 | }
41 | .pl-#{$i} {
42 | padding-left: #{$i * 0.25}rem !important;
43 | }
44 | .px-#{$i} {
45 | padding-left: #{$i * 0.25}rem !important;
46 | padding-right: #{$i * 0.25}rem !important;
47 | }
48 | .py-#{$i} {
49 | padding-top: #{$i * 0.25}rem !important;
50 | padding-bottom: #{$i * 0.25}rem !important;
51 | }
52 | }
53 | }
54 |
55 | @mixin generate-margin-padding-in-px($from, $to) {
56 | @for $i from $from through $to {
57 | .m-#{$i}px {
58 | margin: #{$i}px;
59 | }
60 | .mt-#{$i}px {
61 | margin-top: #{$i}px !important;
62 | }
63 | .mr-#{$i}px {
64 | margin-right: #{$i}px !important;
65 | }
66 | .mb-#{$i}px {
67 | margin-bottom: #{$i}px !important;
68 | }
69 | .ml-#{$i}px {
70 | margin-left: #{$i}px !important;
71 | }
72 | .mx-#{$i}px {
73 | margin-left: #{$i}px !important;
74 | margin-right: #{$i}px !important;
75 | }
76 | .my-#{$i}px {
77 | margin-top: #{$i}px !important;
78 | margin-bottom: #{$i}px !important;
79 | }
80 |
81 | .p-#{$i}px {
82 | padding: #{$i}px !important;
83 | }
84 | .pt-#{$i}px {
85 | padding-top: #{$i}px !important;
86 | }
87 | .pr-#{$i}px {
88 | padding-right: #{$i}px !important;
89 | }
90 | .pb-#{$i}px {
91 | padding-bottom: #{$i}px !important;
92 | }
93 | .pl-#{$i}px {
94 | padding-left: #{$i}px !important;
95 | }
96 | .px-#{$i}px {
97 | padding-left: #{$i}px !important;
98 | padding-right: #{$i}px !important;
99 | }
100 | .py-#{$i}px {
101 | padding-top: #{$i}px !important;
102 | padding-bottom: #{$i}px !important;
103 | }
104 | }
105 | }
106 |
107 | @mixin generate-height-width($from, $to) {
108 | @for $i from $from through $to {
109 | @if $i % 4 == 0 {
110 | .w-#{$i} {
111 | width: #{$i}px !important;
112 | }
113 | .min-w-#{$i} {
114 | min-width: #{$i}px !important;
115 | }
116 | .max-w-#{$i} {
117 | max-width: #{$i}px !important;
118 | }
119 | .h-#{$i} {
120 | height: #{$i}px !important;
121 | }
122 | .min-h-#{$i} {
123 | min-height: #{$i}px !important;
124 | }
125 | .max-h-#{$i} {
126 | max-height: #{$i}px !important;
127 | }
128 | }
129 | }
130 | }
131 |
132 | // media
133 | @mixin media($width) {
134 | @media screen and (max-width: $width) {
135 | @content;
136 | }
137 | }
138 |
139 | // Animation
140 | @mixin keyframeMaker($name) {
141 | @keyframes #{$name} {
142 | @content;
143 | }
144 | @-webkit-keyframes #{$name} {
145 | @content;
146 | }
147 | @-o-keyframes #{$name} {
148 | @content;
149 | }
150 | @-moz-keyframes #{$name} {
151 | @content;
152 | }
153 | }
154 |
--------------------------------------------------------------------------------
/src/styles/_reboot.scss:
--------------------------------------------------------------------------------
1 | #root,
2 | body,
3 | html {
4 | width: 100%;
5 | height: 100%;
6 | overflow: hidden;
7 | position: relative;
8 | }
9 |
10 | div,
11 | a {
12 | box-sizing: border-box;
13 | }
14 |
15 | img {
16 | max-width: 100%;
17 | }
18 | h1,
19 | h2,
20 | h3,
21 | h4,
22 | h5,
23 | h6,
24 | .card-title {
25 | color: $text-body !important;
26 | // font-weight: 500;
27 | }
28 |
29 | .layout1,
30 | .layout2,
31 | .MuiPaper-root,
32 | .MuiTableCell-body,
33 | .matx-customizer {
34 | color: $text-body !important;
35 | }
36 |
37 | code {
38 | font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New",
39 | monospace;
40 | color: rgba(255, 0, 221, 0.863);
41 | font-size: 16px;
42 | }
--------------------------------------------------------------------------------
/src/styles/_variables.scss:
--------------------------------------------------------------------------------
1 | // Colors
2 | $white: #ffffff;
3 | $black: rgba(0, 0, 0, 0.87);
4 | $muted: var(--text-muted);
5 |
6 | $primary: var(--primary);
7 | $secondary: var(--secondary);
8 | $error: var(--error);
9 | $brand: $primary;
10 |
11 | $bg-default: var(--bg-default);
12 | $bg-paper: var(--bg-paper);
13 |
14 | $text-body: var(--text-body);
15 | $text-muted: var(--text-muted);
16 | $text-disabled: var(--text-disabled);
17 | $text-hint: var(--text-hint);
18 | $light-gray: rgba(0, 0, 0, 0.08);
19 |
20 | // Layout
21 | $topbar-mobile-width: 220px;
22 | $topbar-height: 64px;
23 | $sidenav-width: 260px;
24 | $sidenav-button-width: 220px;
25 | $sidenav-compact-width: 80px;
26 | $contained-layout-width: 1200px;
27 |
28 | // Typography
29 | $font: var(--font);
30 | $font-h1: var(--font-h1);
31 | $font-h2: var(--font-h2);
32 | $font-h3: var(--font-h3);
33 | $font-h4: var(--font-h4);
34 | $font-h5: var(--font-h5);
35 | $font-h6: var(--font-h6);
36 | $font-caption: var(--font-caption);
37 | $font-overline: var(--font-overline);
38 | $font-button: var(--font-button);
39 | $font-body-1: var(--font-body-1);
40 | $font-body-2: var(--font-body-2);
41 | $font-subtitle-1: var(--font-subtitle-1);
42 | $font-subtitle-2: var(--font-subtitle-2);
43 | $font-heading: var(--font-heading);
44 | $font-title: var(--font-title);
45 | $font-display-1: var(--font-display-1);
46 | $font-display-2: var(--font-display-2);
47 | $font-display-3: var(--font-display-3);
48 | $font-display-4: var(--font-display-4);
49 |
50 | // box shadow
51 | $elevation-z0: var(--elevation-z0);
52 | $elevation-z1: var(--elevation-z1);
53 | $elevation-z2: var(--elevation-z2);
54 | $elevation-z3: var(--elevation-z3);
55 | $elevation-z4: var(--elevation-z4);
56 | $elevation-z5: var(--elevation-z5);
57 | $elevation-z6: var(--elevation-z6);
58 | $elevation-z7: var(--elevation-z7);
59 | $elevation-z8: var(--elevation-z8);
60 | $elevation-z9: var(--elevation-z9);
61 | $elevation-z10: var(--elevation-z10);
62 | $elevation-z11: var(--elevation-z11);
63 | $elevation-z12: var(--elevation-z12);
64 | $elevation-z13: var(--elevation-z13);
65 | $elevation-z14: var(--elevation-z14);
66 | $elevation-z15: var(--elevation-z15);
67 | $elevation-z16: var(--elevation-z16);
68 | $elevation-z17: var(--elevation-z17);
69 | $elevation-z18: var(--elevation-z18);
70 | $elevation-z19: var(--elevation-z19);
71 | $elevation-z20: var(--elevation-z20);
72 | $elevation-z21: var(--elevation-z21);
73 | $elevation-z22: var(--elevation-z22);
74 | $elevation-z23: var(--elevation-z23);
75 | $elevation-z24: var(--elevation-z24);
76 |
--------------------------------------------------------------------------------
/src/styles/components/_avatar.scss:
--------------------------------------------------------------------------------
1 | .rectangle-box {
2 | height: 40px;
3 | width: 40px;
4 | min-width: 40px;
5 | background-color: rgba(255, 255, 255, 0.1);
6 | border-radius: 8px;
7 | overflow: hidden;
8 | .MuiIcon-root {
9 | font-size: 18px;
10 | }
11 | }
--------------------------------------------------------------------------------
/src/styles/components/_customizer.scss:
--------------------------------------------------------------------------------
1 | .matx-customizer {
2 | display: flex;
3 | flex-direction: column;
4 | width: 320px;
5 | position: fixed;
6 | right: 0;
7 | box-shadow: $elevation-z12;
8 | z-index: 50;
9 | top: 0;
10 | height: 100vh;
11 | .customizer-close {
12 | position: absolute;
13 | right: 8px;
14 | top: 8px;
15 | }
16 | .layout-boxes {
17 | display: flex;
18 | flex-wrap: wrap;
19 | flex-direction: column;
20 | &.sidebar-bg {
21 | flex-direction: row;
22 | }
23 | // margin: 0 -8px;
24 | .layout-box {
25 | width: 100%;
26 | margin: 12px 0;
27 | max-height: 150px;
28 | cursor: pointer;
29 | > div {
30 | overflow: hidden;
31 | display: flex;
32 | position: relative;
33 | // height: 76px;
34 | width: 100%;
35 | &:hover {
36 | &::before,
37 | .layout-name {
38 | display: block;
39 | }
40 | }
41 | &::before,
42 | .layout-name {
43 | text-align: center;
44 | position: absolute;
45 | top: 0;
46 | left: 0;
47 | right: 0;
48 | display: none;
49 | }
50 | &::before {
51 | content: " ";
52 | width: 100%;
53 | height: 100%;
54 | background: rgba(0,0,0,0.3);
55 | }
56 | .layout-name {
57 | color: #ffffff;
58 | top: calc(50% - 18px)
59 | }
60 | img {
61 | // position: absolute;
62 | top: 0;
63 | left: 0;
64 | }
65 | }
66 | }
67 | }
68 | .colors {
69 | display: flex;
70 | flex-wrap: wrap;
71 | .color {
72 | position: relative;
73 | display: flex;
74 | align-items: center;
75 | justify-content: center;
76 | height: 40px;
77 | width: 40px;
78 | margin-top: 4px;
79 | margin-right: 12px;
80 | margin-bottom: 12px;
81 | cursor: pointer;
82 | border-radius: 4px;
83 | overflow: hidden;
84 | box-shadow: 0px 1px 3px 0px rgba(0,0,0,0.2), 0px 1px 1px 0px rgba(0,0,0,0.14), 0px 2px 1px -1px rgba(0,0,0,0.12);
85 | .light, .dark {
86 | position: absolute;
87 | border: 12px solid transparent;
88 | transform: rotate(45deg);
89 | bottom: -12px;
90 | left: -12px;
91 | border-radius: 50%;
92 | }
93 | .light {
94 | border-top-color: rgba(215, 215, 215, 0.6);
95 | }
96 | .dark {
97 | // border-top-color: rgb(34, 41, 69);
98 | border-top-color: rgba(0, 0, 0, .5);
99 | }
100 | }
101 | }
102 | }
--------------------------------------------------------------------------------
/src/styles/components/_index.scss:
--------------------------------------------------------------------------------
1 | @import "./customizer.scss";
2 | @import "./loader.scss";
3 | @import "./matx-sidenav.scss";
4 | @import "./matx-search-box.scss";
5 | @import "./matx-tootbar-menu.scss";
6 | @import "./notification.scss";
7 | @import "./avatar.scss";
8 | @import "./list.scss";
9 | @import "./shopping-cart";
10 |
--------------------------------------------------------------------------------
/src/styles/components/_list.scss:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/elipeF/TS3AudioBot-Control-Panel/069dd3c1d72391db25847392eba49a08ea574f6d/src/styles/components/_list.scss
--------------------------------------------------------------------------------
/src/styles/components/_loader.scss:
--------------------------------------------------------------------------------
1 | .loader-bounce {
2 | height: 100vh !important;
3 | width: 100%;
4 | // position: absolute;
5 | display: flex;
6 | align-items: center;
7 | }
8 | .spinner {
9 | width: 40px;
10 | height: 40px;
11 | position: relative;
12 | margin: auto;
13 | }
14 | .double-bounce1,
15 | .double-bounce2 {
16 | width: 100%;
17 | height: 100%;
18 | border-radius: 50%;
19 | opacity: 0.6;
20 | position: absolute;
21 | top: 0;
22 | left: 0;
23 | -webkit-animation: sk-bounce 2s infinite ease-in-out;
24 | animation: sk-bounce 2s infinite ease-in-out;
25 | }
26 | .double-bounce2 {
27 | -webkit-animation-delay: -1s;
28 | animation-delay: -1s;
29 | }
30 | @-webkit-keyframes sk-bounce {
31 | 0%,
32 | 100% {
33 | -webkit-transform: scale(0);
34 | }
35 | 50% {
36 | -webkit-transform: scale(1);
37 | }
38 | }
39 | @keyframes sk-bounce {
40 | 0%,
41 | 100% {
42 | transform: scale(0);
43 | -webkit-transform: scale(0);
44 | }
45 | 50% {
46 | transform: scale(1);
47 | -webkit-transform: scale(1);
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/src/styles/components/_matx-search-box.scss:
--------------------------------------------------------------------------------
1 | .matx-search-box {
2 | position: absolute;
3 | width: 100%;
4 | left: 0;
5 | z-index: 9;
6 | .search-box {
7 | outline: none;
8 | border: none;
9 | font-size: 1rem;
10 | height: calc(100% - 5px);
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/src/styles/components/_matx-sidenav.scss:
--------------------------------------------------------------------------------
1 | .matx-sidenav-container {
2 | position: relative;
3 | display: flex;
4 | flex-direction: row;
5 | height: 100%;
6 |
7 | .matx-sidenav {
8 | position: relative;
9 | transition: width 250ms ease;
10 | overflow: hidden;
11 | z-index: 91;
12 |
13 | @include media(767px) {
14 | position: absolute;
15 | top: 0;
16 | left: 0;
17 | bottom: 0;
18 | }
19 | }
20 |
21 | .matx-sidenav-content {
22 | position: relative;
23 | flex: 1 1 0;
24 | height: 100%;
25 | }
26 |
27 | .matx-sidenav-overlay {
28 | position: absolute;
29 | width: 100%;
30 | height: 100%;
31 | background: rgba(0, 0, 0, 0.74);
32 | z-index: 90;
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/src/styles/components/_matx-tootbar-menu.scss:
--------------------------------------------------------------------------------
1 | .toolbar-menu-wrap {
2 | position: relative;
3 | .menu-area {
4 | @include media(959px) {
5 | position: fixed;
6 | background: #1a2038;
7 | height: 60px;
8 | width: 100%;
9 | left: 0;
10 | z-index: -10;
11 | opacity: 0;
12 | display: none;
13 | transition: all 0.15s ease;
14 | justify-content: flex-end;
15 | }
16 | }
17 | &.open {
18 | .menu-area {
19 | z-index: 9;
20 | opacity: 1;
21 | display: flex;
22 | }
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/src/styles/components/_notification.scss:
--------------------------------------------------------------------------------
1 | .notification {
2 | width: $sidenav-width;
3 | .notification__topbar {
4 | height: $topbar-height;
5 | box-shadow: $elevation-z6;
6 | }
7 |
8 | .notification__card {
9 | &:hover {
10 | .delete-button {
11 | cursor: pointer;
12 | display: unset;
13 | right: 0;
14 | margin-top: 6px;
15 | top: 0;
16 | z-index: 2;
17 | }
18 | .card__topbar__time {
19 | display: none;
20 | }
21 | }
22 | .card__topbar {
23 | }
24 |
25 | .delete-button {
26 | display: none;
27 | position: absolute;
28 | right: 0;
29 | margin-top: 9px;
30 | }
31 | .card__topbar__button {
32 | height: 24px;
33 | width: 24px;
34 | border-radius: 15px;
35 | overflow: hidden;
36 | display: flex;
37 | justify-content: center;
38 | align-items: center;
39 | opacity: 0.9;
40 | }
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/src/styles/components/_shopping-cart.scss:
--------------------------------------------------------------------------------
1 | .mini-cart {
2 | width: $sidenav-width;
3 | .cart__topbar {
4 | height: $topbar-height;
5 | box-shadow: $elevation-z6;
6 | }
7 | .mini-cart__item {
8 | transition: background 300ms ease;
9 | &:hover {
10 | background: $light-gray;
11 | }
12 | img {
13 | width: 80px;
14 | }
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/src/styles/layouts/_index.scss:
--------------------------------------------------------------------------------
1 | @import "./shared/index";
2 |
3 | @import "./layout1/index";
4 | @import "./layout2/index";
5 |
--------------------------------------------------------------------------------
/src/styles/layouts/layout1/_index.scss:
--------------------------------------------------------------------------------
1 | @import 'layout1';
--------------------------------------------------------------------------------
/src/styles/layouts/layout2/_index.scss:
--------------------------------------------------------------------------------
1 | @import 'variables';
2 | @import 'layout2';
3 | @import 'topbar';
4 | @import 'navbar';
--------------------------------------------------------------------------------
/src/styles/layouts/layout2/_layout2.scss:
--------------------------------------------------------------------------------
1 | .layout2 {
2 | flex: 1 1 auto;
3 | display: flex;
4 | overflow: hidden;
5 | position: relative;
6 | flex-direction: column;
7 | height: 100%;
8 | transition: all .15s ease;
9 | .scrollable-content {
10 | display: flex;
11 | flex-direction: column;
12 | flex: 1 1;
13 | width: 100%;
14 | overflow-y: auto;
15 | }
16 | &.sidenav-close {
17 | .sidenav {
18 | // width: 0px;
19 | left: -#{$sidenav-width}
20 | }
21 | }
22 | }
--------------------------------------------------------------------------------
/src/styles/layouts/layout2/_navbar.scss:
--------------------------------------------------------------------------------
1 | $item-x-padding: 20px;
2 |
3 | .layout2 {
4 | .navbar {
5 | position: relative;
6 | height: $navbar-height;
7 | box-shadow: $elevation-z8;
8 | z-index: 98;
9 | }
10 | }
11 |
12 | .horizontal-nav {
13 | ul {
14 | padding: 0;
15 | margin: 0;
16 | list-style: none;
17 | position: relative;
18 | }
19 | ul.menu {
20 | float: left;
21 | padding-right: 45px;
22 | margin-left: -#{$item-x-padding};
23 | z-index: 99;
24 | > li {
25 | float: left;
26 | > div {
27 | > a,
28 | > div {
29 | border-bottom: 2px solid;
30 | height: 48px;
31 | box-sizing: border-box;
32 | border-color: transparent;
33 | margin: 0 6px;
34 | }
35 | }
36 | }
37 | }
38 | ul li {
39 | position: relative;
40 | margin: 0px;
41 | display: inline-block;
42 | ul a {
43 | padding: 8px $item-x-padding;
44 | height: 48px;
45 | }
46 | }
47 |
48 | a,
49 | label {
50 | display: flex;
51 | flex-direction: row;
52 | align-items: center;
53 | padding: 13px $item-x-padding;
54 | height: $navbar-height;
55 | font-size: 0.875rem;
56 | text-decoration: none;
57 | box-sizing: border-box;
58 | // color: $white;
59 | .material-icons {
60 | font-size: 14px;
61 | margin: 0 4px;
62 | }
63 | }
64 |
65 |
66 | ul ul {
67 | opacity: 0;
68 | visibility: hidden;
69 | position: absolute;
70 |
71 | /* has to be the same number as the "line-height" of "nav a" */
72 | left: $item-x-padding;
73 | box-shadow: $elevation-z8;
74 | top: 60px;
75 | transform: translateY(-10px);
76 | transition: all 0.3s ease-in-out;
77 | z-index: -1;
78 | }
79 |
80 | ul li:hover > div > div > ul,
81 | ul li:hover > div > ul,
82 | li:hover > ul {
83 | opacity: 1;
84 | visibility: visible;
85 | transform: translateY(0);
86 | }
87 |
88 | ul ul li {
89 | width: 170px;
90 | float: none;
91 | display: list-item;
92 | position: relative;
93 | }
94 | ul ul ul {
95 | top: 0;
96 | left: 170px;
97 | }
98 | ul ul ul li {
99 | position: relative;
100 | top: 0;
101 | }
102 |
103 | li > a:after {
104 | content: "arrow_drop_down";
105 | font-family: 'Material Icons';
106 | font-weight: normal;
107 | font-style: normal;
108 | font-size: 14px;
109 | line-height: 1;
110 | margin-left: auto;
111 | letter-spacing: normal;
112 | text-transform: none;
113 | display: inline-block;
114 | white-space: nowrap;
115 | word-wrap: normal;
116 | direction: ltr;
117 | -webkit-font-feature-settings: 'liga';
118 | -webkit-font-smoothing: antialiased;
119 | }
120 | li > a:only-child:after {
121 | content: "";
122 | }
123 | }
124 |
--------------------------------------------------------------------------------
/src/styles/layouts/layout2/_topbar.scss:
--------------------------------------------------------------------------------
1 | .layout2 {
2 | .topbar {
3 | position: relative;
4 | width: 100%;
5 | display: table;
6 | height: $topbar-height;
7 | border-bottom: 1px solid transparent;
8 | padding-top: 1rem;
9 | padding-bottom: 1rem;
10 | z-index: 98;
11 | .brand {
12 | height: 100%;
13 | img {
14 | height: 32px;
15 | }
16 | .brand__text {
17 | font-weight: 500;
18 | font-size: 1.5rem;
19 | margin: 0 1rem;
20 | }
21 | }
22 |
23 | .MuiIconButton-root {
24 | color: $white;
25 | }
26 | }
27 | }
--------------------------------------------------------------------------------
/src/styles/layouts/layout2/_variables.scss:
--------------------------------------------------------------------------------
1 | $topbar-height: 80px;
2 | $navbar-height: 60px;
--------------------------------------------------------------------------------
/src/styles/layouts/shared/_footer.scss:
--------------------------------------------------------------------------------
1 | .footer {
2 | min-height: $topbar-height;
3 |
4 | @include media(480px) {
5 | display: table;
6 | width: 100%;
7 | min-height: auto;
8 | padding: 1rem 0;
9 | .container {
10 | flex-direction: column !important;
11 | a {
12 | margin: 0 0 16px !important;
13 | }
14 | }
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/src/styles/layouts/shared/_index.scss:
--------------------------------------------------------------------------------
1 | @import "layout";
2 | @import "sidenav";
3 | @import "footer";
--------------------------------------------------------------------------------
/src/styles/layouts/shared/_layout.scss:
--------------------------------------------------------------------------------
1 | .layout-full {
2 | .container {
3 | padding-left: 30px;
4 | padding-right: 30px;
5 | }
6 | }
7 |
8 | .layout-contained, .layout-boxed {
9 | .container {
10 | padding-left: 30px;
11 | padding-right: 30px;
12 | }
13 | }
14 |
15 |
16 | .layout-contained {
17 | .container {
18 | max-width: $contained-layout-width;
19 | margin: auto;
20 | width: 100%;
21 | @include media(767px) {
22 | max-width: 100%;
23 | }
24 | }
25 | }
26 |
27 | .layout-boxed {
28 | max-width: $contained-layout-width;
29 | margin: auto;
30 | box-shadow: $elevation-z12;
31 | background: $white;
32 | @include media(767px) {
33 | max-width: 100%;
34 | box-shadow: none;
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/src/styles/utilities/_animations.scss:
--------------------------------------------------------------------------------
1 | .fade-in {
2 | @include keyframeMaker(fade-in) {
3 | from {
4 | opacity: 0;
5 | }
6 | to {
7 | opacity: 1;
8 | }
9 | }
10 | animation: fade-in 1s #{bezier()};
11 | }
12 |
13 | @keyframes spin {
14 | 0% {transform: rotate(0)}
15 | 100% {transform: rotate(360deg)}
16 | }
17 |
18 | .spin {
19 | animation: spin 3s infinite linear;
20 | }
21 |
--------------------------------------------------------------------------------
/src/styles/utilities/_border.scss:
--------------------------------------------------------------------------------
1 | .border-radius-0 {
2 | border-radius: 0px !important;
3 | overflow: hidden;
4 | }
5 | .border-radius-4 {
6 | border-radius: 4px !important;
7 | overflow: hidden;
8 | }
9 | .border-radius-8 {
10 | border-radius: 8px !important;
11 | overflow: hidden;
12 | }
13 | .border-radius-circle {
14 | border-radius: 50% !important;
15 | }
16 | .border-none {
17 | border: none !important;
18 | }
19 |
--------------------------------------------------------------------------------
/src/styles/utilities/_carousel.scss:
--------------------------------------------------------------------------------
1 | .swiper-slide {
2 | height: auto;
3 | }
4 |
5 | .swiper-pagination-bullet {
6 | opacity: 1;
7 | background: rgba(0, 0, 0, 0.55);
8 | transition: transform 400ms cubic-bezier(0.17, 0.67, 0.83, 0.67);
9 | }
10 |
11 | .bullet-active {
12 | transform: scale(1.8);
13 | }
14 |
15 | .carousel__button-next,
16 | .carousel__button-prev {
17 | position: absolute !important;
18 | top: 50%;
19 | transform: translateY(calc(-50% - 50px));
20 | z-index: 1;
21 | }
22 | .carousel__button-prev {
23 | left: 0px;
24 | }
25 | .carousel__button-next {
26 | right: 0px;
27 | }
28 |
--------------------------------------------------------------------------------
/src/styles/utilities/_color.scss:
--------------------------------------------------------------------------------
1 | .bg-primary {
2 | background: $primary !important;
3 | }
4 | .bg-secondary {
5 | background: $secondary !important;
6 | }
7 | .bg-green {
8 | background-color: rgba($color: green, $alpha: 0.75) !important;
9 | }
10 | .bg-error {
11 | background: $error !important;
12 | }
13 | .bg-white {
14 | background: #fff !important;
15 | color: inherit;
16 | }
17 | .bg-default {
18 | background: $bg-default !important;
19 | }
20 | .bg-paper {
21 | background: $bg-paper;
22 | }
23 | .bg-light-gray {
24 | background: $light-gray !important;
25 | }
26 | .bg-dark {
27 | background: #000000;
28 | color: #fff;
29 | }
30 | .bg-light-dark {
31 | background: #212121;
32 | color: white;
33 | }
34 | .bg-error {
35 | background: $error !important;
36 | color: white !important;
37 | }
38 | .bg-green {
39 | background: #08ad6c !important;
40 | }
41 | .bg-light-primary {
42 | &::after {
43 | background: $primary;
44 | }
45 | }
46 | .bg-light-secondary {
47 | position: relative;
48 | z-index: 0;
49 | &::after {
50 | background: $secondary;
51 | }
52 | }
53 | .bg-light-error {
54 | position: relative;
55 | z-index: 0;
56 | &::after {
57 | background: $error;
58 | }
59 | }
60 | .bg-light-green {
61 | background: rgba($color: #08ad6c, $alpha: 0.5) !important;
62 | }
63 |
64 | [class^="bg-light-"],
65 | [class*=" bg-light-"] {
66 | position: relative;
67 | z-index: 0;
68 | &::after {
69 | content: "";
70 | position: absolute;
71 | top: 0;
72 | right: 0;
73 | bottom: 0;
74 | left: 0;
75 | opacity: 0.15;
76 | z-index: -1;
77 | border-radius: 8px;
78 | }
79 | }
80 |
81 | .bg-transperant {
82 | background: transparent !important;
83 | }
84 |
85 | .text-white {
86 | color: #fff !important;
87 | }
88 | .text-white-secondary {
89 | color: rgba(#fff, 0.87) !important;
90 | }
91 | .text-muted-white {
92 | color: rgba(#fff, 0.54) !important;
93 | }
94 | .text-light-white {
95 | color: rgba(255, 255, 255, 0.54) !important;
96 | }
97 | .text-muted {
98 | color: $text-muted !important;
99 | }
100 | .text-hint {
101 | color: $text-hint !important;
102 | }
103 | .text-gray {
104 | color: rgba(0, 0, 0, 0.74) !important;
105 | }
106 | .text-brand {
107 | color: $brand !important;
108 | }
109 | .text-primary {
110 | color: $primary !important;
111 | }
112 | .text-secondary {
113 | color: $secondary !important;
114 | }
115 | .text-green {
116 | color: #08ad6c !important;
117 | }
118 | .text-error {
119 | color: $error !important;
120 | }
121 |
122 | .gray-on-hover {
123 | transition: background 250ms ease;
124 | &:hover {
125 | background: rgba(0, 0, 0, 0.054);
126 | }
127 | }
128 |
129 | // Border color
130 | .border-color-white {
131 | border-color: #ffffff !important;
132 | }
133 | .border-color-default {
134 | border-color: $bg-default !important;
135 | }
136 | .border-color-paper {
137 | border-color: $bg-paper !important;
138 | }
139 |
--------------------------------------------------------------------------------
/src/styles/utilities/_common.scss:
--------------------------------------------------------------------------------
1 | .circular-image-small {
2 | height: 48px;
3 | width: 48px;
4 | border-radius: 50%;
5 | overflow: hidden;
6 | }
7 | .card {
8 | transition: all 0.3s ease;
9 | &:hover {
10 | box-shadow: $elevation-z12;
11 | }
12 | }
13 | .card-title {
14 | font-size: 1rem;
15 | text-transform: capitalize;
16 | font-weight: 500;
17 | }
18 | .card-subtitle {
19 | font-size: 0.875rem;
20 | color: $muted;
21 | .theme-dark & {
22 | color: rgba(#fff, 0.54);
23 | }
24 | }
25 |
26 | .hide-on-mobile {
27 | display: inherit;
28 | @media screen and (max-width: 767px) {
29 | display: none !important;
30 | }
31 | }
32 |
33 | .hide-on-pc {
34 | @media screen and (min-width: 1200px) {
35 | display: none !important;
36 | }
37 | }
38 |
39 | .show-on-pc {
40 | @media screen and (max-width: 1200px) {
41 | display: none !important;
42 | }
43 | }
44 |
45 | .VictoryContainer {
46 | svg {
47 | height: 100% !important;
48 | }
49 | }
50 |
51 | .box-shadow-none {
52 | box-shadow: none !important;
53 | }
54 |
55 | .circle-44 {
56 | height: 44px !important;
57 | width: 44px !important;
58 | }
59 |
60 | .circle-32 {
61 | height: 32px !important;
62 | min-height: 32px !important;
63 | width: 32px !important;
64 |
65 | .MuiFab-root {
66 | min-height: 32px !important;
67 | }
68 | .MuiIcon-root {
69 | font-size: 13px !important;
70 | }
71 | }
72 |
73 | .show-on-mobile {
74 | display: none !important;
75 | @include media(767px) {
76 | display: inherit !important;
77 | }
78 | }
79 | .invisible-on-pc {
80 | visibility: hidden;
81 | @include media(767px) {
82 | visibility: visible;
83 | }
84 | }
85 |
86 | .highlight-js {
87 | pre {
88 | white-space: pre-line;
89 | }
90 | }
91 |
--------------------------------------------------------------------------------
/src/styles/utilities/_functions.scss:
--------------------------------------------------------------------------------
1 | @function bezier() {
2 | @return cubic-bezier(0.17, 0.67, 0.83, 0.67);
3 | }
4 |
--------------------------------------------------------------------------------
/src/styles/utilities/_misc.scss:
--------------------------------------------------------------------------------
1 | .cursor-pointer {
2 | cursor: pointer;
3 | }
4 | .cursor-move {
5 | cursor: move;
6 | }
--------------------------------------------------------------------------------
/src/styles/utilities/_positionings.scss:
--------------------------------------------------------------------------------
1 | // display
2 | .hidden {
3 | display: none;
4 | }
5 | .block {
6 | display: block;
7 | }
8 | .inline-block {
9 | display: inline-block !important;
10 | }
11 |
12 | .flex {
13 | display: flex;
14 | }
15 | .flex-column {
16 | display: flex;
17 | flex-direction: column;
18 | }
19 | .flex-wrap {
20 | flex-wrap: wrap;
21 | }
22 | .justify-start {
23 | justify-content: flex-start !important;
24 | }
25 | .justify-center {
26 | justify-content: center;
27 | }
28 | .justify-end {
29 | justify-content: flex-end;
30 | }
31 | .justify-between {
32 | justify-content: space-between !important;
33 | }
34 | .justify-around {
35 | justify-content: space-around;
36 | }
37 | .items-center {
38 | align-items: center;
39 | }
40 | .items-start {
41 | align-items: flex-start;
42 | }
43 | .items-end {
44 | align-items: flex-end;
45 | }
46 | .items-stretch {
47 | align-items: stretch;
48 | }
49 | .flex-grow {
50 | flex-grow: 1;
51 | }
52 | .overflow-auto {
53 | overflow: auto !important;
54 | }
55 | .overflow-hidden {
56 | overflow: hidden;
57 | }
58 | .scroll-y {
59 | overflow-x: hidden;
60 | overflow-y: scroll;
61 | }
62 |
63 | // postions
64 | .position-relative {
65 | position: relative;
66 | }
67 | .position-bottom {
68 | position: absolute;
69 | bottom: 0;
70 | }
71 | .text-center {
72 | text-align: center;
73 | }
74 | .align-middle {
75 | vertical-align: middle;
76 | }
77 | .text-right {
78 | text-align: right;
79 | }
80 | .text-left {
81 | text-align: left;
82 | }
83 | .x-center {
84 | left: 50%;
85 | transform: translateX(-50%);
86 | }
87 | .y-center {
88 | top: 50%;
89 | transform: translateY(-50%);
90 | }
91 |
--------------------------------------------------------------------------------
/src/styles/utilities/_shadow.scss:
--------------------------------------------------------------------------------
1 | @for $i from 0 through 24 {
2 | .elevation-z#{$i} {
3 | box-shadow: var(--elevation-z#{$i});
4 | }
5 | }
--------------------------------------------------------------------------------
/src/styles/utilities/_spacing.scss:
--------------------------------------------------------------------------------
1 | @include generate-margin-padding-in-rem(0, 25);
2 | @include generate-margin-padding-in-rem(0, -25);
3 | @include generate-margin-padding-in-px(1, 16);
4 |
5 | .px-80 {
6 | padding-right: 80px;
7 | padding-left: 80px;
8 | @media screen and (max-width: 460px) {
9 | padding-right: 16px;
10 | padding-left: 16px;
11 | }
12 | }
13 |
14 | .px-sm-30 {
15 | padding: 0px 30px;
16 | @include media(767px) {
17 | padding: 0px 16px;
18 | }
19 | }
20 | .p-sm-24 {
21 | padding: 24px !important;
22 | @include media(767px) {
23 | padding: 16px !important;
24 | }
25 | }
26 | .px-sm-24 {
27 | padding: 0px 24px !important;
28 | @include media(767px) {
29 | padding: 0px 16px !important;
30 | }
31 | }
32 | .pt-sm-24 {
33 | padding-top: 24px !important;
34 | @include media(767px) {
35 | padding-top: 16px !important;
36 | }
37 | }
38 | .pl-sm-24 {
39 | padding-left: 24px !important;
40 | @include media(767px) {
41 | padding: 12px !important;
42 | }
43 | }
44 |
45 | .m-auto {
46 | margin: auto !important;
47 | }
48 | .mx-auto {
49 | margin-left: auto !important;
50 | margin-right: auto !important;
51 | }
52 | .my-auto {
53 | margin-top: auto !important;
54 | margin-bottom: auto !important;
55 | }
56 |
57 | .m-sm-30 {
58 | margin: 30px;
59 | @include media(767px) {
60 | margin: 16px;
61 | }
62 | }
63 | .mb-sm-30 {
64 | margin-bottom: 30px;
65 | @include media(767px) {
66 | margin-bottom: 16px;
67 | }
68 | }
69 |
70 | @include generate-height-width(0, 400);
71 |
72 | .w-full {
73 | width: 100%;
74 | }
75 | .w-full-screen {
76 | width: 100vw;
77 | }
78 | .min-w-750 {
79 | min-width: 750px;
80 | }
81 | .max-w-770 {
82 | max-width: 770px;
83 | }
84 |
85 | .h-full {
86 | height: 100% !important;
87 | }
88 | .h-full-screen {
89 | height: 100vh;
90 | }
91 | .h-150px {
92 | height: 150px !important;
93 | }
94 |
95 | .size-36 {
96 | height: 36px !important;
97 | width: 36px !important;
98 | }
99 | .size-24 {
100 | height: 24px !important;
101 | width: 24px !important;
102 | }
103 |
--------------------------------------------------------------------------------
/src/styles/utilities/_typography.scss:
--------------------------------------------------------------------------------
1 | .h1,
2 | .h2,
3 | .h3,
4 | .h4,
5 | .h5,
6 | .h6,
7 | h1,
8 | h2,
9 | h3,
10 | h4,
11 | h5,
12 | h6 {
13 | margin: 0 0 0.5rem;
14 | line-height: 1.1;
15 | color: inherit;
16 | }
17 | .h1,
18 | h1 {
19 | font-size: 2rem;
20 | }
21 | .h2,
22 | h2 {
23 | font-size: 1.75rem;
24 | }
25 | .h3,
26 | h3 {
27 | font-size: 1.5rem;
28 | }
29 | .h4,
30 | h4 {
31 | font-size: 1.25rem;
32 | }
33 | .h5,
34 | h5 {
35 | font-size: 1rem;
36 | }
37 | .h6,
38 | h6 {
39 | font-size: 0.875rem;
40 | }
41 |
42 | a {
43 | text-decoration: none;
44 | color: inherit;
45 | }
46 |
47 | .caption {
48 | font: $font-caption;
49 | }
50 | .subtitle-1 {
51 | font: $font-subtitle-1;
52 | }
53 | .subtitle-2 {
54 | font: $font-subtitle-2;
55 | }
56 | .heading {
57 | font: $font-heading;
58 | }
59 | .title {
60 | font: $font-title;
61 | }
62 | .display-1 {
63 | font: $font-display-1;
64 | }
65 | .display-2 {
66 | font: $font-display-2;
67 | }
68 | .display-3 {
69 | font: $font-display-3;
70 | }
71 | .display-4 {
72 | font: $font-display-4;
73 | }
74 |
75 | .capitalize {
76 | text-transform: capitalize !important;
77 | }
78 | .uppercase {
79 | text-transform: uppercase !important;
80 | }
81 | .lowercase {
82 | text-transform: lowercase !important;
83 | }
84 |
85 | // font weight
86 | .font-normal {
87 | font-weight: normal !important;
88 | }
89 | .font-light {
90 | font-weight: 300 !important;
91 | }
92 | .font-medium {
93 | font-weight: 500 !important;
94 | }
95 | .font-semibold {
96 | font-weight: 600 !important;
97 | }
98 | .font-bold {
99 | font-weight: 700 !important;
100 | }
101 |
102 | // font size
103 |
104 | .text-13 {
105 | font-size: 13px;
106 | }
107 | .text-14 {
108 | font-size: 14px;
109 | }
110 | .text-16 {
111 | font-size: 16px;
112 | }
113 | .text-18 {
114 | font-size: 18px;
115 | }
116 | .text-20 {
117 | font-size: 20px;
118 | }
119 | .text-22 {
120 | font-size: 22px;
121 | }
122 | .text-24 {
123 | font-size: 24px;
124 | }
125 | .text-30 {
126 | font-size: 30px !important;
127 | }
128 | .text-32 {
129 | font-size: 32px;
130 | }
131 | .text-small {
132 | font-size: 0.8125rem !important; // 13px
133 | }
134 |
135 | .whitespace-pre-wrap {
136 | white-space: pre-wrap;
137 | word-break: break-word;
138 | }
139 |
140 | .whitespace-pre {
141 | white-space: pre;
142 | }
143 | .whitespace-no-wrap {
144 | white-space: nowrap;
145 | }
146 |
--------------------------------------------------------------------------------
/src/styles/utilities/_utilities.scss:
--------------------------------------------------------------------------------
1 | @import "../mixins";
2 | @import "./spacing";
3 | @import "./color.scss";
4 | @import "./typography";
5 | @import "./functions";
6 | @import "./animations";
7 | @import "./positionings";
8 | @import "./border";
9 | @import "./shadow";
10 | @import "./misc";
11 | @import "./common";
12 |
--------------------------------------------------------------------------------
/src/styles/views/_CRUDTable.scss:
--------------------------------------------------------------------------------
1 | .crud-table {
2 | thead {
3 | tr {
4 | th:first-child {
5 | padding-left: 16px !important;
6 | }
7 | }
8 | }
9 | tbody {
10 | tr {
11 | transition: background 300ms ease;
12 | &:hover {
13 | background: $light-gray;
14 | }
15 | td {
16 | border-bottom: none;
17 | text-transform: capitalize;
18 | }
19 | td:first-child {
20 | padding-left: 16px !important;
21 | }
22 | }
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/src/styles/views/_calendar.scss:
--------------------------------------------------------------------------------
1 | .rbc-event {
2 | background-color: $primary !important;
3 | &.rbc-selected {
4 | background-color: $secondary !important;
5 | }
6 | }
7 |
8 | .rbc-calendar {
9 | height: auto;
10 | flex-grow: 1;
11 | }
12 | .rbc-header {
13 | padding: 12px 16px !important;
14 | a {
15 | padding-bottom: 8px !important;
16 | }
17 | span {
18 | font-size: 15px !important;
19 | font-weight: 500;
20 | }
21 | }
22 | .calendar-header {
23 | border-top-right-radius: 6px;
24 | border-top-left-radius: 6px;
25 | }
26 |
--------------------------------------------------------------------------------
/src/styles/views/_carousel.scss:
--------------------------------------------------------------------------------
1 | .swiper-slide {
2 | height: auto;
3 | }
4 |
5 | .swiper-pagination-bullet {
6 | opacity: 1;
7 | background: rgba(0, 0, 0, 0.55);
8 | transition: transform 400ms cubic-bezier(0.17, 0.67, 0.83, 0.67);
9 | }
10 |
11 | .bullet-active {
12 | transform: scale(1.8);
13 | }
14 |
15 | .carousel__button-next,
16 | .carousel__button-prev {
17 | position: absolute !important;
18 | top: 50%;
19 | transform: translateY(calc(-50% - 50px));
20 | z-index: 1;
21 | }
22 | .carousel__button-prev {
23 | left: 0px;
24 | }
25 | .carousel__button-next {
26 | right: 0px;
27 | }
28 |
--------------------------------------------------------------------------------
/src/styles/views/_chat-box.scss:
--------------------------------------------------------------------------------
1 | .chat-sidenav {
2 | border-right: 1px solid $light-gray;
3 | height: 450px;
4 | .chat-contact-list {
5 | height: 100%;
6 | }
7 | }
8 | .chat-container {
9 | background: rgba(0, 0, 0, 0.05);
10 | height: 450px;
11 | .chat-message-list {
12 | .list__message {
13 | border-radius: 4px;
14 | overflow: hidden;
15 | }
16 | }
17 | .empty-message-circle {
18 | height: 220px;
19 | width: 220px;
20 | border-radius: 50%;
21 | box-shadow: $elevation-z6;
22 |
23 | .MuiIcon-root {
24 | font-size: 4rem !important;
25 | }
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/src/styles/views/_dashboard.scss:
--------------------------------------------------------------------------------
1 | .product-table {
2 | white-space: pre;
3 | min-width: 400px;
4 | overflow: auto;
5 | small {
6 | height: 15px;
7 | width: 50px;
8 | border-radius: 500px;
9 | box-shadow: 0 0 2px 0 rgba(0, 0, 0, 0.12), 0 2px 2px 0 rgba(0, 0, 0, 0.24);
10 | }
11 |
12 | tbody {
13 | tr {
14 | transition: background 300ms ease;
15 | &:hover {
16 | background: $light-gray;
17 | }
18 | td {
19 | border-bottom: none;
20 | text-transform: capitalize;
21 | }
22 | td:first-child {
23 | padding-left: 16px !important;
24 | }
25 | }
26 | }
27 | }
28 |
29 | .play-card {
30 | display: flex;
31 | flex-wrap: wrap;
32 | flex-direction: row;
33 | justify-content: space-between;
34 | align-items: center;
35 |
36 | small {
37 | line-height: 1;
38 | }
39 | }
40 |
41 | .upgrade-card {
42 | box-shadow: none;
43 | text-align: center;
44 | position: relative;
45 | h6 {
46 | position: relative;
47 | left: 50%;
48 | transform: translateX(-50%);
49 | width: 150px;
50 | }
51 | }
52 |
53 | .sales {
54 | .bills {
55 | .bills__icon {
56 | border-radius: 8px;
57 | height: 52px;
58 | width: 52px;
59 | overflow: hidden;
60 | background-color: rgba(24, 42, 136, 0.08);
61 | h4,
62 | h5 {
63 | color: rgba(0, 0, 0, 0.87);
64 | }
65 | img {
66 | height: 23px;
67 | width: 36.76px;
68 | }
69 | }
70 | }
71 |
72 | }
73 |
74 | .analytics {
75 | .face-group {
76 | .avatar {
77 | border: 2px solid white;
78 | &:not(:first-child) {
79 | margin-left: -14px;
80 | }
81 | }
82 | .number-avatar {
83 | background: $error;
84 | }
85 | }
86 |
87 | .small-circle {
88 | display: flex;
89 | justify-content: center;
90 | align-items: center;
91 | height: 16px;
92 | width: 16px;
93 | border-radius: 50%;
94 |
95 | .small-icon {
96 | font-size: 8px;
97 | }
98 | }
99 |
100 | .project-card {
101 | .card__roject-name {
102 | margin-left: 24px;
103 | @include media(767px) {
104 | margin-left: 4px;
105 | }
106 | }
107 | }
108 | }
109 |
110 | // online education
111 | .learning-management {
112 | position: relative;
113 |
114 | .welcome-card {
115 | position: relative;
116 | padding: 36px 50px !important;
117 | overflow: visible;
118 |
119 | img {
120 | margin-top: -82px;
121 | max-width: 230px;
122 | }
123 |
124 | @include media(767px) {
125 | img {
126 | display: none;
127 | }
128 | }
129 | }
130 |
131 |
132 |
133 | .product-table {
134 | white-space: pre;
135 | min-width: 400px;
136 | overflow: auto;
137 | small {
138 | height: 15px;
139 | width: 50px;
140 | border-radius: 500px;
141 | box-shadow: 0 0 2px 0 rgba(0, 0, 0, 0.12), 0 2px 2px 0 rgba(0, 0, 0, 0.24);
142 | }
143 |
144 | tbody {
145 | tr {
146 | transition: background 300ms ease;
147 | &:hover {
148 | background: $light-gray;
149 | }
150 | td {
151 | border-bottom: none;
152 | text-transform: capitalize;
153 | }
154 | td:first-child {
155 | padding-left: 16px !important;
156 | }
157 | }
158 | }
159 | }
160 |
161 |
162 | }
163 |
--------------------------------------------------------------------------------
/src/styles/views/_ecommerce.scss:
--------------------------------------------------------------------------------
1 | .cart {
2 | min-width: 900px;
3 | overflow-x: scroll;
4 | }
5 |
6 | .ecommerce__product-card {
7 | position: relative;
8 |
9 | .product__image-box {
10 | .product__price {
11 | position: absolute;
12 | font-weight: 500;
13 | background: $primary;
14 | color: white;
15 | padding: 4px 12px;
16 | right: 0;
17 | top: 24px;
18 | border-top-left-radius: 26px;
19 | border-bottom-left-radius: 26px;
20 | overflow: hidden;
21 | z-index: 4;
22 | }
23 | .image-box__overlay {
24 | position: absolute;
25 | top: 0;
26 | bottom: 0;
27 | left: 0;
28 | right: 0;
29 | display: none;
30 | background: rgba(0, 0, 0, 0.74);
31 | z-index: 2;
32 | }
33 | }
34 |
35 | &:hover {
36 | .image-box__overlay {
37 | display: flex;
38 | justify-content: center;
39 | align-items: center;
40 | }
41 | }
42 | }
43 |
44 | .checkout {
45 | .checkout__product-list {
46 | hr:last-of-type {
47 | display: none !important;
48 | }
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/src/styles/views/_form.scss:
--------------------------------------------------------------------------------
1 | .upload-drop-box {
2 | height: 120px;
3 | width: 100%;
4 | border: 2px solid $light-gray;
5 | border-radius: 4px;
6 | }
7 | .drag-shadow {
8 | background: $primary;
9 | box-shadow: 3px 3px 10px rgba($color: #000000, $alpha: 0.2);
10 | }
11 |
--------------------------------------------------------------------------------
/src/styles/views/_inbox.scss:
--------------------------------------------------------------------------------
1 | .inbox {
2 | .inbox__topbar {
3 | border-top-right-radius: 4px;
4 | border-top-left-radius: 4px;
5 | button {
6 | color: white !important;
7 | }
8 | }
9 | }
10 |
11 | .ql-container {
12 | min-height: 250px;
13 | // border-bottom-left-radius: 0.5em;
14 | // border-bottom-right-radius: 0.5em;
15 | // background: #fefcfc;
16 | p,
17 | code {
18 | font-size: 16px;
19 | }
20 | }
21 |
22 | .ql-toolbar {
23 | background: white;
24 | // background: #eaecec;
25 | // border-top-left-radius: 0.5em;
26 | // border-top-right-radius: 0.5em;
27 | border-bottom: none;
28 | }
29 |
--------------------------------------------------------------------------------
/src/styles/views/_index.scss:
--------------------------------------------------------------------------------
1 | @import "./topbar";
2 | @import "./sales";
3 | @import "./sessions";
4 | @import "./page-layouts";
5 | @import "./invoice";
6 | @import "./calendar";
7 | @import "./CRUDTable";
8 | @import "./inbox";
9 | @import "./chat-box";
10 | @import "./todo";
11 | @import "./dashboard";
12 | @import "./list";
13 | @import "./landing";
14 | @import "./carousel";
15 | @import "./pricing";
16 | @import "./form";
17 | @import "./scrum-board";
18 | @import "./ecommerce";
19 |
--------------------------------------------------------------------------------
/src/styles/views/_invoice.scss:
--------------------------------------------------------------------------------
1 | .invoice-viewer {
2 | h5 {
3 | font-size: 15px;
4 | }
5 | .viewer__order-info {
6 | }
7 | .viewer__billing-info {
8 | }
9 | }
10 |
11 | // Media print
12 | @media print {
13 | body,
14 | *,
15 | html {
16 | visibility: hidden;
17 | }
18 | .ps {
19 | overflow: scroll !important;
20 | overflow-anchor: none;
21 | -ms-overflow-style: none;
22 | touch-action: auto;
23 | -ms-touch-action: auto;
24 | }
25 | #print-area {
26 | position: fixed;
27 | top: 0;
28 | left: 0;
29 | right: 0;
30 | height: 100%;
31 | * {
32 | visibility: visible;
33 | }
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/src/styles/views/_list.scss:
--------------------------------------------------------------------------------
1 | .list {
2 | .list-view {
3 | .list__card {
4 | .project-image {
5 | height: 75px;
6 | width: 100px;
7 | }
8 |
9 | .card__button-group {
10 | display: none;
11 | position: absolute;
12 | top: 0;
13 | bottom: 0;
14 | z-index: 1;
15 | right: 0;
16 | }
17 |
18 | &:hover {
19 | .card__button-group {
20 | display: flex;
21 | }
22 | }
23 | }
24 | }
25 |
26 | .grid-view {
27 | .grid__card {
28 | position: relative;
29 |
30 | &:hover {
31 | .grid__card-top {
32 | &::after {
33 | content: " ";
34 | position: absolute;
35 | top: 0;
36 | left: 0;
37 | right: 0;
38 | bottom: 0;
39 | background: rgba(0, 0, 0, 0.54);
40 | z-index: 1;
41 |
42 | @include keyframeMaker(fade-in) {
43 | from {
44 | opacity: 0;
45 | }
46 | to {
47 | opacity: 1;
48 | }
49 | }
50 | animation: fade-in 250ms #{bezier()};
51 | }
52 |
53 | .grid__card-overlay {
54 | display: block;
55 | }
56 | }
57 |
58 | .grid__card-bottom {
59 | .email {
60 | display: block;
61 | }
62 | .date {
63 | display: none;
64 | }
65 | }
66 | }
67 |
68 | .grid__card-top {
69 | position: relative;
70 |
71 | .grid__card-overlay {
72 | position: absolute;
73 | top: 0;
74 | left: 0;
75 | right: 0;
76 | bottom: 0;
77 | display: none;
78 | z-index: 2;
79 |
80 | & > div:nth-child(2) {
81 | position: absolute;
82 | top: 0;
83 | bottom: 0;
84 | right: 0;
85 | left: 0;
86 | z-index: -1;
87 | }
88 | }
89 |
90 | img {
91 | // max-height: 200px;
92 | }
93 | }
94 | .grid__card-bottom {
95 | .email {
96 | display: none;
97 | }
98 | }
99 | }
100 | }
101 | }
102 |
--------------------------------------------------------------------------------
/src/styles/views/_page-layouts.scss:
--------------------------------------------------------------------------------
1 | .left-sidenav-card {
2 | position: relative;
3 |
4 | .header-bg {
5 | height: 200px;
6 | background: $primary;
7 | background-image: url("/assets/images/home-bg-black.png");
8 | background-size: contain;
9 | }
10 |
11 | .left-sidenav-card__content {
12 | margin-top: -200px;
13 | margin-right: 24px;
14 | @include media(767px) {
15 | margin-right: 0px;
16 | }
17 | }
18 |
19 | .left-sidenav-card__sidenav {
20 | .sidenav__header {
21 | color: white !important;
22 | @include media(767px) {
23 | color: inherit !important;
24 | }
25 | }
26 | @include media(767px) {
27 | background: $bg-default;
28 | }
29 | }
30 |
31 | .content-card {
32 | .card-header {
33 | height: 64px;
34 | }
35 | }
36 | }
37 |
38 | .user-profile {
39 | position: relative;
40 |
41 | .header-bg {
42 | height: 345px;
43 | @include media(959px) {
44 | height: 400px;
45 | }
46 | @include media(767px) {
47 | height: 400px;
48 | }
49 | }
50 |
51 | .user-profile__content {
52 | margin-top: -345px;
53 | padding-top: 74px;
54 | padding-right: 30px;
55 | padding-left: 4px;
56 | .menu-button {
57 | display: none;
58 | }
59 | @include media(959px) {
60 | margin-top: -390px;
61 | padding-top: 24px;
62 | padding-right: 16px;
63 | padding-left: 16px;
64 | }
65 | @include media(767px) {
66 | margin-top: -410px;
67 | padding-top: 16px;
68 | padding-right: 16px;
69 | padding-left: 16px;
70 | .menu-button {
71 | display: flex;
72 | }
73 | }
74 | .content__top-card-holder {
75 | .content__top-card {
76 | height: 95px;
77 | background-color: rgba(0, 0, 0, 0.12);
78 | }
79 | .content__chart {
80 | width: 54px;
81 | height: 35px;
82 | }
83 | }
84 |
85 | .user-profile__card {
86 | overflow: unset;
87 | .card__edge-button {
88 | position: relative;
89 | margin-top: -56px;
90 | }
91 |
92 | .edge-vertical-line::after {
93 | content: " ";
94 | position: absolute;
95 | height: 35px;
96 | width: 5px;
97 | top: -30px;
98 | background: $primary;
99 | }
100 |
101 | .card__button-holder {
102 | width: 100px;
103 | min-width: 100px;
104 | }
105 |
106 | .card__gray-box {
107 | img {
108 | height: 128px;
109 | width: calc(100% - 16px);
110 | border-radius: 8px;
111 | }
112 | }
113 | }
114 |
115 | .bills {
116 | .bills__icon {
117 | border-radius: 8px;
118 | height: 52px;
119 | width: 52px;
120 | overflow: hidden;
121 | background-color: rgba(24, 42, 136, 0.08);
122 | h4,
123 | h5 {
124 | color: rgba(0, 0, 0, 0.87);
125 | }
126 | img {
127 | height: 23px;
128 | width: 36.76px;
129 | }
130 | }
131 | }
132 | }
133 |
134 | .user-profile__sidenav {
135 | margin-top: -345px;
136 | padding-top: 74px;
137 | .avatar {
138 | height: 82px;
139 | width: 82px;
140 | }
141 | // .text-white {
142 | // color: rgba(255, 255, 255, 0.87) !important;
143 | // }
144 | .sidenav__square-card {
145 | height: 104px;
146 | width: 104px;
147 | }
148 | @include media(767px) {
149 | margin-top: -410px;
150 | }
151 | }
152 | }
153 |
--------------------------------------------------------------------------------
/src/styles/views/_pricing.scss:
--------------------------------------------------------------------------------
1 | .pricing {
2 | .pricing__card {
3 | border-radius: 20px;
4 | overflow: hidden;
5 | h1,
6 | h5 {
7 | margin: 0;
8 | color: $primary !important;
9 | text-transform: uppercase;
10 | }
11 |
12 | h5 {
13 | font-weight: 400;
14 | letter-spacing: 3px;
15 | }
16 |
17 | h1 {
18 | line-height: 1;
19 | font-size: 3rem;
20 | padding-top: 8px;
21 | padding-bottom: 4px;
22 | font-weight: 500;
23 | }
24 |
25 | p {
26 | color: $text-muted;
27 | font-size: 1rem;
28 | }
29 |
30 | img {
31 | height: 150px;
32 | width: 150px;
33 | }
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/src/styles/views/_sales.scss:
--------------------------------------------------------------------------------
1 | .bg-circle-primary {
2 | background: url("/assets/images/circles.png"),
3 | linear-gradient(90deg, #{$primary} -19.83%, #{$primary} 189.85%);
4 | background-size: cover;
5 | background-repeat: no-repeat;
6 | }
7 | .bg-circle-secondary {
8 | background: url("/assets/images/circles.png"),
9 | linear-gradient(90deg, #{$secondary} -19.83%, #{$secondary} 189.85%);
10 | background-size: cover;
11 | background-repeat: no-repeat;
12 | }
13 | .bg-circle-error {
14 | background: url("/assets/images/circles.png"),
15 | linear-gradient(90deg, #{$error} -19.83%, #{$error} 189.85%);
16 | background-size: cover;
17 | background-repeat: no-repeat;
18 | }
19 |
--------------------------------------------------------------------------------
/src/styles/views/_scrum-board.scss:
--------------------------------------------------------------------------------
1 | .scrum-board {
2 | .face-group,
3 | .face-group-9 {
4 | .avatar {
5 | border: 2px solid white;
6 | height: 24px;
7 | width: 24px;
8 | &:not(:first-child) {
9 | margin-left: -8px;
10 | }
11 | }
12 | .number-avatar {
13 | font-size: 12px;
14 | background: $error;
15 | }
16 | }
17 |
18 | .face-group-9 {
19 | .avatar {
20 | height: 36px;
21 | width: 36px;
22 | &:not(:first-child) {
23 | margin-left: -12px;
24 | }
25 | }
26 | .number-avatar {
27 | font-size: 14px;
28 | }
29 | }
30 |
31 | .button-group {
32 | button {
33 | min-width: 32px !important;
34 | }
35 | }
36 |
37 | .list-column {
38 | margin: 0px 12px;
39 | .list-column__card {
40 | margin-bottom: 16px;
41 | }
42 | // .list-column__card:first-child {
43 | // margin-top: 0px;
44 | // }
45 | .list-column__card:last-child {
46 | margin-bottom: 0px;
47 | }
48 | }
49 | .list-column:first-child {
50 | margin: 0px 12px 0px 0px;
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/src/styles/views/_sessions.scss:
--------------------------------------------------------------------------------
1 | .signup {
2 | background: #1A2038;
3 |
4 |
5 | .signup-card {
6 | max-width: 800px;
7 | border-radius: 12px !important;
8 | img {
9 | width: 200px;
10 | }
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/src/styles/views/_todo.scss:
--------------------------------------------------------------------------------
1 | .todo {
2 | $top: 94px;
3 |
4 | .todo__search-box-holder {
5 | background: $primary;
6 | height: 220px;
7 | & > div {
8 | height: calc(220px - #{$top} + 30px);
9 | @include media(767px) {
10 | height: calc(220px - #{$top} - 16px + 30px);
11 | }
12 |
13 | .todo__search-box {
14 | width: calc(100% - 60px);
15 | height: 48px;
16 | border-radius: 24px;
17 | overflow: hidden;
18 |
19 | input[type="text"] {
20 | font-size: 18px;
21 | outline: none;
22 | border: none;
23 | }
24 | }
25 | }
26 | }
27 | .todo__content {
28 | margin-top: -$top;
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/src/styles/views/_topbar.scss:
--------------------------------------------------------------------------------
1 | .circular-image-small {
2 | height: 36px;
3 | width: 36px;
4 | border-radius: 50%;
5 | }
6 |
7 |
--------------------------------------------------------------------------------