├── .babelrc ├── .eslintrc ├── .gitignore ├── .gitlab-ci.yml ├── .husky └── pre-commit ├── .nvmrc ├── Dockerfile ├── LICENSE.md ├── README.md ├── config └── jest │ ├── cssTransform.js │ └── fileTransform.js ├── configs ├── cmd.sh ├── default.conf ├── nginx-docker.conf ├── nginx.conf └── redirect.html ├── jest.config.js ├── package-lock.json ├── package.json ├── public ├── VERSION.txt ├── apple-touch-icon.png ├── config.json ├── favicon-16x16.png ├── favicon-32x32.png ├── favicon.ico ├── index.html ├── locales │ ├── locale-ar.json │ ├── locale-bn.json │ ├── locale-ca.json │ ├── locale-cs.json │ ├── locale-da.json │ ├── locale-de.json │ ├── locale-en.json │ ├── locale-es.json │ ├── locale-fi.json │ ├── locale-fr.json │ ├── locale-he.json │ ├── locale-hi.json │ ├── locale-hr.json │ ├── locale-hu.json │ ├── locale-it.json │ ├── locale-ja.json │ ├── locale-ko.json │ ├── locale-nl.json │ ├── locale-no.json │ ├── locale-pl.json │ ├── locale-pt-BR.json │ ├── locale-pt.json │ ├── locale-ru.json │ ├── locale-sk.json │ ├── locale-sv.json │ ├── locale-uk.json │ ├── locale-vi.json │ ├── locale-zh-Hans.json │ ├── locale-zh-Hant.json │ └── locale-zh.json └── manifest.json ├── src ├── actions │ ├── actionCreators.js │ ├── actionTypes.js │ └── boundActionCreators.js ├── assets │ ├── css │ │ └── material-dashboard-react.css │ ├── img │ │ ├── apple-touch-icon.png │ │ ├── background.jpg │ │ ├── faces │ │ │ └── marc.jpg │ │ ├── favicon-16x16.png │ │ ├── favicon-32x32.png │ │ ├── favicon.ico │ │ ├── logo.png │ │ └── sc-logo.png │ └── jss │ │ ├── material-dashboard-react.js │ │ └── material-dashboard-react │ │ ├── appStyle.jsx │ │ ├── buttonStyle.jsx │ │ ├── chartCardStyle.jsx │ │ ├── components │ │ ├── buttonStyle.js │ │ ├── cardAvatarStyle.js │ │ ├── cardBodyStyle.js │ │ ├── cardFooterStyle.js │ │ ├── cardHeaderStyle.js │ │ ├── cardIconStyle.js │ │ ├── cardStyle.js │ │ ├── customInputStyle.js │ │ ├── customTabsStyle.js │ │ ├── footerStyle.js │ │ ├── headerLinksStyle.js │ │ ├── headerStyle.js │ │ ├── rtlHeaderLinksStyle.js │ │ ├── sidebarStyle.js │ │ ├── snackbarContentStyle.js │ │ ├── tableStyle.js │ │ ├── tasksStyle.js │ │ └── typographyStyle.js │ │ ├── customInputStyle.jsx │ │ ├── dashboardStyle.jsx │ │ ├── dropdownStyle.js │ │ ├── footerStyle.jsx │ │ ├── headerLinksStyle.jsx │ │ ├── headerStyle.jsx │ │ ├── iconButtonStyle.jsx │ │ ├── iconsStyle.jsx │ │ ├── profileCardStyle.jsx │ │ ├── regularCardStyle.jsx │ │ ├── sidebarStyle.jsx │ │ ├── snackbarContentStyle.jsx │ │ ├── statsCardStyle.jsx │ │ ├── tableStyle.jsx │ │ ├── tasksCardStyle.jsx │ │ ├── tasksStyle.jsx │ │ └── typographyStyle.jsx ├── components │ ├── Card │ │ ├── Card.js │ │ ├── CardBody.js │ │ ├── CardFooter.js │ │ ├── CardHeader.js │ │ ├── CardIcon.js │ │ ├── ChartCard.jsx │ │ ├── FileserverCard.jsx │ │ ├── GroupCard.jsx │ │ ├── HealthcheckCard.jsx │ │ ├── LDAPCard.jsx │ │ ├── LicenseCard.jsx │ │ ├── OIDCCard.jsx │ │ ├── ProfileCard.jsx │ │ ├── RegularCard.jsx │ │ ├── ReleaseCard.jsx │ │ ├── SAMLCard.jsx │ │ ├── SCIMCard.jsx │ │ ├── Sessions.jsx │ │ ├── StatsCard.jsx │ │ ├── UserCard.jsx │ │ └── VersionCard.jsx │ ├── CustomButtons │ │ ├── Button.jsx │ │ └── IconButton.jsx │ ├── CustomInput │ │ └── CustomInput.jsx │ ├── CustomTabs │ │ └── CustomTabs.js │ ├── Dialog │ │ └── DeleteConfirmDialog.js │ ├── Footer │ │ └── Footer.jsx │ ├── Form │ │ └── LoginForm.jsx │ ├── Grid │ │ └── GridItem.jsx │ ├── Header │ │ ├── Header.jsx │ │ └── HeaderLinks.jsx │ ├── Notification │ │ └── Notification.jsx │ ├── Sidebar │ │ └── Sidebar.jsx │ ├── Snackbar │ │ ├── Snackbar.jsx │ │ └── SnackbarContent.jsx │ ├── Table │ │ ├── CustomMaterialTable.jsx │ │ └── Table.jsx │ ├── Tasks │ │ └── Tasks.jsx │ ├── Typography │ │ ├── A.jsx │ │ ├── Danger.jsx │ │ ├── Info.jsx │ │ ├── Muted.jsx │ │ ├── P.jsx │ │ ├── Primary.jsx │ │ ├── Quote.jsx │ │ ├── Small.jsx │ │ ├── Success.jsx │ │ └── Warning.jsx │ └── index.js ├── containers │ ├── App.js │ ├── ChartCard │ │ ├── browser.js │ │ ├── device.js │ │ ├── os.js │ │ └── two_factor.js │ ├── Index │ │ ├── Index.js │ │ └── SwitchRoutes.js │ └── Login │ │ └── Login.js ├── i18n.js ├── index.js ├── reducers │ ├── admin_client.js │ ├── client.js │ ├── index.js │ ├── notification.js │ ├── persistent.js │ ├── server.js │ └── user.js ├── routes │ ├── ee.js │ ├── ldap.js │ ├── oidc.js │ ├── other.js │ ├── saml.js │ └── sidebar.js ├── services │ ├── api-client.js │ ├── api-server.js │ ├── api-static.js │ ├── browser-client.js │ ├── clientjs.js │ ├── converter.js │ ├── converter.test.js │ ├── cryptoLibrary.js │ ├── device.js │ ├── helper.js │ ├── helper.test.js │ ├── host.js │ ├── ivalt.js │ ├── notification.js │ ├── store.js │ ├── user.js │ ├── webauthn.js │ └── worker.js ├── setupTests.js ├── variables │ ├── charts.jsx │ ├── general.jsx │ └── styles.jsx └── views │ ├── Dashboard │ ├── HealthCheck.js │ └── Index.jsx │ ├── Group │ ├── Create.js │ ├── Edit.jsx │ └── ShareRightCreate.js │ ├── Groups │ └── Index.jsx │ ├── Icons │ └── Icons.jsx │ ├── LDAP │ └── Index.jsx │ ├── Notifications │ └── Notifications.jsx │ ├── OIDC │ └── Index.jsx │ ├── Policies │ ├── Create.js │ ├── Edit.jsx │ └── Index.jsx │ ├── SAML │ └── Index.jsx │ ├── SCIM │ └── Index.jsx │ ├── SecurityReport │ └── Edit.jsx │ ├── SecurityReports │ └── Index.jsx │ ├── TableList │ └── TableList.jsx │ ├── Typography │ └── Typography.jsx │ ├── User │ ├── Create.js │ └── Edit.jsx │ ├── UserProfile │ └── UserProfile.jsx │ └── Users │ └── Index.jsx ├── var ├── build-ubuntu.sh ├── deploy-docker.sh ├── deploy-github.sh ├── deploy_changelog.sh ├── deploy_nightlyartifacts.sh ├── deploy_releaseartifacts.sh ├── download_translations_from_artifactory.sh ├── package-webclient.sh ├── sync_translations.py ├── translate.py ├── update_version.sh └── upload_translations.py ├── webpack.common.js ├── webpack.dev.js └── webpack.prod.js /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | ["@babel/preset-env", { "targets": { "node": "current" } }], 4 | "@babel/preset-react" 5 | ], 6 | "plugins": [ 7 | "@babel/plugin-proposal-class-properties", 8 | "@babel/plugin-transform-runtime" 9 | ] 10 | } -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "react-app", 3 | "rules": { 4 | "indent": "off" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | 6 | # testing 7 | /coverage 8 | 9 | # production 10 | /build 11 | 12 | # misc 13 | .DS_Store 14 | .env.local 15 | .env.development.local 16 | .env.test.local 17 | .env.production.local 18 | 19 | npm-debug.log* 20 | 21 | .idea 22 | untranslated -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | . "$(dirname -- "$0")/_/husky.sh" 3 | 4 | npx lint-staged -------------------------------------------------------------------------------- /.nvmrc: -------------------------------------------------------------------------------- 1 | 22.11.0 2 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM psono-docker.jfrog.io/nginx:alpine 2 | 3 | LABEL maintainer="Sascha Pfeiffer " 4 | 5 | RUN apk upgrade --no-cache \ 6 | && apk add --no-cache curl 7 | 8 | COPY ./configs/nginx.conf /etc/nginx/nginx.conf 9 | COPY ./configs/default.conf /etc/nginx/conf.d/default.conf 10 | COPY ./configs/cmd.sh /root/cmd.sh 11 | COPY ./build /usr/share/nginx/html/portal 12 | COPY ./configs/redirect.html /usr/share/nginx/html/index.html 13 | 14 | HEALTHCHECK --interval=30s --timeout=3s \ 15 | CMD curl -f http://localhost/ || exit 1 16 | 17 | CMD ["/bin/sh", "/root/cmd.sh"] 18 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | # License: 2 | 3 | Copyright 2017 Sascha Pfeiffer 4 | 5 | Licensed under the Apache License, Version 2.0 (the "License"); 6 | you may not use this file except in compliance with the License. 7 | You may obtain a copy of the License at 8 | 9 | http://www.apache.org/licenses/LICENSE-2.0 10 | 11 | Unless required by applicable law or agreed to in writing, software 12 | distributed under the License is distributed on an "AS IS" BASIS, 13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | See the License for the specific language governing permissions and 15 | limitations under the License. 16 | 17 | # Licenses of used software 18 | 19 | ## MIT: material-dashboard-react 20 | 21 | https://www.creative-tim.com/product/material-dashboard-react# 22 | 23 | MIT License 24 | 25 | Copyright (c) 2017 Creative Tim 26 | 27 | Permission is hereby granted, free of charge, to any person obtaining a copy 28 | of this software and associated documentation files (the "Software"), to deal 29 | in the Software without restriction, including without limitation the rights 30 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 31 | copies of the Software, and to permit persons to whom the Software is 32 | furnished to do so, subject to the following conditions: 33 | 34 | The above copyright notice and this permission notice shall be included in all 35 | copies or substantial portions of the Software. 36 | 37 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 38 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 39 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 40 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 41 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 42 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 43 | SOFTWARE. 44 | 45 | 46 | ## MIT: mui-org/material-ui 47 | 48 | https://github.com/mui-org/material-ui 49 | 50 | The MIT License (MIT) 51 | 52 | Copyright (c) 2014 Call-Em-All 53 | 54 | Permission is hereby granted, free of charge, to any person obtaining a copy 55 | of this software and associated documentation files (the "Software"), to deal 56 | in the Software without restriction, including without limitation the rights 57 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 58 | copies of the Software, and to permit persons to whom the Software is 59 | furnished to do so, subject to the following conditions: 60 | 61 | The above copyright notice and this permission notice shall be included in all 62 | copies or substantial portions of the Software. 63 | 64 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 65 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 66 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 67 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 68 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 69 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 70 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PSONO Admin Client - Password Manager 2 | 3 | [![coverage report](https://gitlab.com/psono/psono-admin-client/badges/master/coverage.svg)](https://gitlab.com/psono/psono-admin-client/commits/master) 4 | [![Code Climate](https://codeclimate.com/github/psono/psono-admin-client/badges/gpa.svg)](https://codeclimate.com/github/psono/psono-admin-client) 5 | [![build status](https://img.shields.io/docker/pulls/psono/psono-admin-client.svg)](https://hub.docker.com/r/psono/psono-admin-client/) 6 | [![Discord](https://img.shields.io/badge/Discord-join%20chat-738bd7.svg)](https://discord.gg/VmBMzTSbGV) 7 | [![POEditor](https://img.shields.io/badge/POEditor-Help%20translate-brightgreen.svg)](https://poeditor.com/join/project?hash=xwGlVVPZbM) 8 | 9 | # Canonical source 10 | 11 | The canonical source of PSONO Admin Client is [hosted on GitLab.com](https://gitlab.com/psono/psono-admin-client). 12 | 13 | # Documentation 14 | 15 | The documentation for the psono server can be found here: 16 | 17 | [Psono Documentation](https://doc.psono.com/) 18 | 19 | # Things that have not yet found a place in our docs 20 | 21 | This project was bootstrapped with [Create React App](https://github.com/facebookincubator/create-react-app). 22 | 23 | ## Install dependencies 24 | 25 | To start a server for development do the following 26 | 27 | npm install 28 | 29 | ## Start of a dev server 30 | 31 | To start a server for development do the following 32 | 33 | npm start 34 | 35 | Afterwards you can visit http://localhost:3000 36 | 37 | ## Build for production 38 | 39 | To build everything as standalone for production 40 | 41 | INLINE_RUNTIME_CHUNK=false npm run build 42 | 43 | Afterwards everthing can be found in the `/build` folder 44 | 45 | ## Run unittests 46 | 47 | To run unit tests 48 | 49 | npm test 50 | 51 | ## Run unittests (with coverage) 52 | 53 | To run unit tests 54 | 55 | npm test -- --coverage 56 | 57 | 58 | # LICENSE 59 | 60 | Visit the [License.md](/LICENSE.md) for more details 61 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /config/jest/cssTransform.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | process() { 5 | return 'module.exports = {};'; 6 | }, 7 | getCacheKey() { 8 | return 'cssTransform'; 9 | }, 10 | }; -------------------------------------------------------------------------------- /config/jest/fileTransform.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const path = require('path'); 4 | 5 | module.exports = { 6 | process(src, filename) { 7 | const assetFilename = JSON.stringify(path.basename(filename)); 8 | 9 | if (filename.match(/\.svg$/)) { 10 | // Create a simple component that just renders the file basename 11 | const pascalCaseName = path.basename(filename, '.svg') 12 | .split(/[-_\s]+/) 13 | .map(word => word.charAt(0).toUpperCase() + word.slice(1)) 14 | .join(''); 15 | 16 | const componentName = `Svg${pascalCaseName}`; 17 | 18 | return `const React = require('react'); 19 | module.exports = { 20 | __esModule: true, 21 | default: ${assetFilename}, 22 | ReactComponent: React.forwardRef(function ${componentName}(props, ref) { 23 | return { 24 | $$typeof: Symbol.for('react.element'), 25 | type: 'svg', 26 | ref: ref, 27 | key: null, 28 | props: Object.assign({}, props, { 29 | children: ${assetFilename} 30 | }) 31 | }; 32 | }), 33 | }; 34 | `; 35 | } 36 | 37 | return `module.exports = ${assetFilename};`; 38 | }, 39 | getCacheKey() { 40 | return 'fileTransform'; 41 | }, 42 | }; -------------------------------------------------------------------------------- /configs/cmd.sh: -------------------------------------------------------------------------------- 1 | if [ ! -z "$PSONO_PORTAL_CONFIG_JSON" ] 2 | then 3 | echo "$PSONO_PORTAL_CONFIG_JSON" > /usr/share/nginx/html/portal/config.json 4 | fi 5 | 6 | nginx -g "daemon off;" 7 | -------------------------------------------------------------------------------- /configs/default.conf: -------------------------------------------------------------------------------- 1 | server { 2 | listen 80; 3 | server_name localhost; 4 | 5 | #charset koi8-r; 6 | #access_log /var/log/nginx/host.access.log main; 7 | 8 | location / { 9 | root /usr/share/nginx/html; 10 | index index.html index.htm; 11 | try_files $uri /portal/index.html; # forward all requests to index.html 12 | } 13 | 14 | #error_page 404 /404.html; 15 | 16 | # redirect server error pages to the static page /50x.html 17 | # 18 | error_page 500 502 503 504 /50x.html; 19 | location = /50x.html { 20 | root /usr/share/nginx/html; 21 | } 22 | 23 | # proxy the PHP scripts to Apache listening on 127.0.0.1:80 24 | # 25 | #location ~ \.php$ { 26 | # proxy_pass http://127.0.0.1; 27 | #} 28 | 29 | # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000 30 | # 31 | #location ~ \.php$ { 32 | # root html; 33 | # fastcgi_pass 127.0.0.1:9000; 34 | # fastcgi_index index.php; 35 | # fastcgi_param SCRIPT_FILENAME /scripts$fastcgi_script_name; 36 | # include fastcgi_params; 37 | #} 38 | 39 | # deny access to .htaccess files, if Apache's document root 40 | # concurs with nginx's one 41 | # 42 | #location ~ /\.ht { 43 | # deny all; 44 | #} 45 | } 46 | -------------------------------------------------------------------------------- /configs/nginx.conf: -------------------------------------------------------------------------------- 1 | 2 | user nginx; 3 | worker_processes 1; 4 | 5 | error_log /var/log/nginx/error.log warn; 6 | pid /var/run/nginx.pid; 7 | 8 | 9 | events { 10 | worker_connections 1024; 11 | } 12 | 13 | 14 | http { 15 | include /etc/nginx/mime.types; 16 | default_type application/octet-stream; 17 | 18 | log_format main '$remote_addr - $remote_user [$time_local] "$request" ' 19 | '$status $body_bytes_sent "$http_referer" ' 20 | '"$http_user_agent" "$http_x_forwarded_for"'; 21 | 22 | access_log /var/log/nginx/access.log main; 23 | 24 | sendfile on; 25 | #tcp_nopush on; 26 | 27 | keepalive_timeout 65; 28 | 29 | #gzip on; 30 | 31 | include /etc/nginx/conf.d/*.conf; 32 | } -------------------------------------------------------------------------------- /configs/redirect.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 9 | Page Redirection 10 | 11 | 12 | 13 | If you are not redirected automatically, follow this link to example. 14 | 15 | 16 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | roots: ['/src'], 3 | collectCoverageFrom: ['src/**/*.{js,jsx}', '!src/**/*.d.ts'], 4 | setupFiles: ['react-app-polyfill/jsdom'], 5 | setupFilesAfterEnv: ['/src/setupTests.js'], 6 | testMatch: ['/src/**/__tests__/**/*.{js,jsx}', '/src/**/*.{spec,test}.{js,jsx}'], 7 | testEnvironment: 'jsdom', 8 | transform: { 9 | '^.+\\.(js|jsx)$': '/node_modules/babel-jest', 10 | '^.+\\.css$': '/config/jest/cssTransform.js', 11 | '^(?!.*\\.(js|jsx|css|json)$)': '/config/jest/fileTransform.js', 12 | }, 13 | transformIgnorePatterns: [ 14 | '[/\\\\]node_modules[/\\\\].+\\.(js|jsx)$', 15 | '^.+\\.module\\.(css|sass|scss)$', 16 | ], 17 | modulePaths: [], 18 | moduleNameMapper: { 19 | '^react-native$': 'react-native-web', 20 | '^.+\\.module\\.(css|sass|scss)$': 'identity-obj-proxy', 21 | '\\.(css|less)$': 'identity-obj-proxy', 22 | }, 23 | moduleFileExtensions: [ 24 | 'js', 25 | 'jsx', 26 | 'json', 27 | 'node', 28 | ], 29 | watchPlugins: [ 30 | 'jest-watch-typeahead/filename', 31 | 'jest-watch-typeahead/testname', 32 | ], 33 | coverageReporters: [ 34 | 'html', 35 | 'text', 36 | 'cobertura', 37 | 'text-summary', 38 | ], 39 | }; -------------------------------------------------------------------------------- /public/VERSION.txt: -------------------------------------------------------------------------------- 1 | 0.1.0 (Build dummy) -------------------------------------------------------------------------------- /public/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/psono/psono-admin-client/dfe097433b3428558a8524b24103ec6fe93ab727/public/apple-touch-icon.png -------------------------------------------------------------------------------- /public/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "backend_servers": [{ 3 | "title": "Psono.pw" 4 | }], 5 | "allow_custom_server": true, 6 | "allow_registration": true, 7 | "allow_lost_password": true, 8 | "authentication_methods": ["AUTHKEY", "LDAP"], 9 | "saml_provider": [], 10 | "oidc_provider": [] 11 | } -------------------------------------------------------------------------------- /public/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/psono/psono-admin-client/dfe097433b3428558a8524b24103ec6fe93ab727/public/favicon-16x16.png -------------------------------------------------------------------------------- /public/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/psono/psono-admin-client/dfe097433b3428558a8524b24103ec6fe93ab727/public/favicon-32x32.png -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/psono/psono-admin-client/dfe097433b3428558a8524b24103ec6fe93ab727/public/favicon.ico -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 11 | 12 | 13 | 14 | 15 | 16 | 20 | Psono Admin Panel 21 | 22 | 23 | 26 |
27 | 37 | 38 | -------------------------------------------------------------------------------- /public/locales/locale-ar.json: -------------------------------------------------------------------------------- 1 | { 2 | 3 | } -------------------------------------------------------------------------------- /public/locales/locale-bn.json: -------------------------------------------------------------------------------- 1 | { 2 | 3 | } -------------------------------------------------------------------------------- /public/locales/locale-ca.json: -------------------------------------------------------------------------------- 1 | { 2 | 3 | } 4 | -------------------------------------------------------------------------------- /public/locales/locale-cs.json: -------------------------------------------------------------------------------- 1 | { 2 | 3 | } -------------------------------------------------------------------------------- /public/locales/locale-da.json: -------------------------------------------------------------------------------- 1 | { 2 | 3 | } -------------------------------------------------------------------------------- /public/locales/locale-de.json: -------------------------------------------------------------------------------- 1 | { 2 | 3 | } -------------------------------------------------------------------------------- /public/locales/locale-es.json: -------------------------------------------------------------------------------- 1 | { 2 | 3 | } -------------------------------------------------------------------------------- /public/locales/locale-fi.json: -------------------------------------------------------------------------------- 1 | { 2 | 3 | } -------------------------------------------------------------------------------- /public/locales/locale-fr.json: -------------------------------------------------------------------------------- 1 | { 2 | 3 | } -------------------------------------------------------------------------------- /public/locales/locale-he.json: -------------------------------------------------------------------------------- 1 | { 2 | 3 | } -------------------------------------------------------------------------------- /public/locales/locale-hi.json: -------------------------------------------------------------------------------- 1 | { 2 | 3 | } -------------------------------------------------------------------------------- /public/locales/locale-hr.json: -------------------------------------------------------------------------------- 1 | { 2 | 3 | } -------------------------------------------------------------------------------- /public/locales/locale-hu.json: -------------------------------------------------------------------------------- 1 | { 2 | 3 | } -------------------------------------------------------------------------------- /public/locales/locale-it.json: -------------------------------------------------------------------------------- 1 | { 2 | 3 | } -------------------------------------------------------------------------------- /public/locales/locale-ja.json: -------------------------------------------------------------------------------- 1 | { 2 | 3 | } -------------------------------------------------------------------------------- /public/locales/locale-ko.json: -------------------------------------------------------------------------------- 1 | { 2 | 3 | } -------------------------------------------------------------------------------- /public/locales/locale-nl.json: -------------------------------------------------------------------------------- 1 | { 2 | 3 | } -------------------------------------------------------------------------------- /public/locales/locale-no.json: -------------------------------------------------------------------------------- 1 | { 2 | 3 | } -------------------------------------------------------------------------------- /public/locales/locale-pl.json: -------------------------------------------------------------------------------- 1 | { 2 | 3 | } -------------------------------------------------------------------------------- /public/locales/locale-pt-BR.json: -------------------------------------------------------------------------------- 1 | { 2 | 3 | } -------------------------------------------------------------------------------- /public/locales/locale-pt.json: -------------------------------------------------------------------------------- 1 | { 2 | 3 | } -------------------------------------------------------------------------------- /public/locales/locale-ru.json: -------------------------------------------------------------------------------- 1 | { 2 | 3 | } -------------------------------------------------------------------------------- /public/locales/locale-sk.json: -------------------------------------------------------------------------------- 1 | { 2 | 3 | } -------------------------------------------------------------------------------- /public/locales/locale-sv.json: -------------------------------------------------------------------------------- 1 | { 2 | 3 | } -------------------------------------------------------------------------------- /public/locales/locale-uk.json: -------------------------------------------------------------------------------- 1 | { 2 | 3 | } -------------------------------------------------------------------------------- /public/locales/locale-vi.json: -------------------------------------------------------------------------------- 1 | { 2 | 3 | } -------------------------------------------------------------------------------- /public/locales/locale-zh-Hans.json: -------------------------------------------------------------------------------- 1 | { 2 | 3 | } -------------------------------------------------------------------------------- /public/locales/locale-zh-Hant.json: -------------------------------------------------------------------------------- 1 | { 2 | 3 | } -------------------------------------------------------------------------------- /public/locales/locale-zh.json: -------------------------------------------------------------------------------- 1 | { 2 | 3 | } -------------------------------------------------------------------------------- /public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "Psono Admin Panel", 3 | "name": "The Admin Panel of the psono password manager", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | } 10 | ], 11 | "start_url": "./index.html", 12 | "display": "standalone", 13 | "theme_color": "#000000", 14 | "background_color": "#ffffff" 15 | } 16 | -------------------------------------------------------------------------------- /src/actions/actionTypes.js: -------------------------------------------------------------------------------- 1 | export const SET_KNOWN_HOSTS = 'SET_KNOWN_HOSTS'; 2 | export const SET_USER_USERNAME = 'SET_USER_USERNAME'; 3 | export const SET_USER_INFO_1 = 'SET_USER_INFO_1'; 4 | export const SET_USER_INFO_2 = 'SET_USER_INFO_2'; 5 | export const SET_USER_INFO_3 = 'SET_USER_INFO_3'; 6 | export const LOGOUT = 'LOGOUT'; 7 | export const SET_SERVER_URL = 'SET_SERVER_URL'; 8 | export const SET_SERVER_INFO = 'SET_SERVER_INFO'; 9 | export const SET_CLIENT_URL = 'SET_CLIENT_URL'; 10 | export const SET_ADMIN_CLIENT_CONFIG = 'SET_ADMIN_CLIENT_CONFIG'; 11 | export const NOTIFICATION_SEND = 'NOTIFICATION_SEND'; 12 | export const NOTIFICATION_SET = 'NOTIFICATION_SET'; 13 | export const SET_SERVER_SECRET_EXISTS = 'SET_SERVER_SECRET_EXISTS'; 14 | -------------------------------------------------------------------------------- /src/actions/boundActionCreators.js: -------------------------------------------------------------------------------- 1 | import store from '../services/store'; 2 | import actionCreators from './actionCreators'; 3 | import { bindActionCreators } from 'redux'; 4 | 5 | export default bindActionCreators(actionCreators, store.dispatch); 6 | -------------------------------------------------------------------------------- /src/assets/img/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/psono/psono-admin-client/dfe097433b3428558a8524b24103ec6fe93ab727/src/assets/img/apple-touch-icon.png -------------------------------------------------------------------------------- /src/assets/img/background.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/psono/psono-admin-client/dfe097433b3428558a8524b24103ec6fe93ab727/src/assets/img/background.jpg -------------------------------------------------------------------------------- /src/assets/img/faces/marc.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/psono/psono-admin-client/dfe097433b3428558a8524b24103ec6fe93ab727/src/assets/img/faces/marc.jpg -------------------------------------------------------------------------------- /src/assets/img/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/psono/psono-admin-client/dfe097433b3428558a8524b24103ec6fe93ab727/src/assets/img/favicon-16x16.png -------------------------------------------------------------------------------- /src/assets/img/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/psono/psono-admin-client/dfe097433b3428558a8524b24103ec6fe93ab727/src/assets/img/favicon-32x32.png -------------------------------------------------------------------------------- /src/assets/img/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/psono/psono-admin-client/dfe097433b3428558a8524b24103ec6fe93ab727/src/assets/img/favicon.ico -------------------------------------------------------------------------------- /src/assets/img/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/psono/psono-admin-client/dfe097433b3428558a8524b24103ec6fe93ab727/src/assets/img/logo.png -------------------------------------------------------------------------------- /src/assets/img/sc-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/psono/psono-admin-client/dfe097433b3428558a8524b24103ec6fe93ab727/src/assets/img/sc-logo.png -------------------------------------------------------------------------------- /src/assets/jss/material-dashboard-react/appStyle.jsx: -------------------------------------------------------------------------------- 1 | // ############################## 2 | // // // App styles 3 | // ############################# 4 | 5 | import { 6 | drawerWidth, 7 | transition, 8 | container 9 | } from '../material-dashboard-react.js'; 10 | 11 | const appStyle = theme => ({ 12 | wrapper: { 13 | position: 'relative', 14 | top: '0', 15 | height: '100vh' 16 | }, 17 | mainPanel: { 18 | [theme.breakpoints.up('md')]: { 19 | width: `calc(100% - ${drawerWidth}px)` 20 | }, 21 | overflow: 'auto', 22 | position: 'relative', 23 | float: 'right', 24 | ...transition, 25 | maxHeight: '100%', 26 | width: '100%', 27 | overflowScrolling: 'touch' 28 | }, 29 | content: { 30 | marginTop: '70px', 31 | padding: '30px 15px', 32 | minHeight: 'calc(100% - 123px)' 33 | }, 34 | container, 35 | map: { 36 | marginTop: '70px' 37 | } 38 | }); 39 | 40 | export default appStyle; 41 | -------------------------------------------------------------------------------- /src/assets/jss/material-dashboard-react/chartCardStyle.jsx: -------------------------------------------------------------------------------- 1 | // ############################## 2 | // // // ChartCard styles 3 | // ############################# 4 | 5 | import { 6 | card, 7 | cardHeader, 8 | defaultFont, 9 | orangeCardHeader, 10 | greenCardHeader, 11 | redCardHeader, 12 | blueCardHeader, 13 | purpleCardHeader, 14 | cardActions, 15 | grayColor, 16 | warningColor, 17 | dangerColor, 18 | successColor, 19 | infoColor, 20 | primaryColor, 21 | roseColor 22 | } from '../material-dashboard-react.js'; 23 | 24 | const chartCardStyle = { 25 | card: { 26 | ...card, 27 | overflow: 'visible' 28 | }, 29 | cardHeader: { 30 | ...cardHeader, 31 | padding: '0', 32 | minHeight: '160px', 33 | ...defaultFont 34 | }, 35 | orangeCardHeader, 36 | greenCardHeader, 37 | redCardHeader, 38 | blueCardHeader, 39 | purpleCardHeader, 40 | cardContent: { 41 | padding: '15px 20px' 42 | }, 43 | cardTitle: { 44 | marginTop: '0', 45 | marginBottom: '5px', 46 | ...defaultFont, 47 | fontSize: '1.175em' 48 | }, 49 | cardCategory: { 50 | marginBottom: '0', 51 | color: grayColor[0], 52 | ...defaultFont, 53 | fontSize: '0.9em' 54 | }, 55 | cardActions: { 56 | ...cardActions, 57 | padding: '0!important' 58 | }, 59 | cardStats: { 60 | lineHeight: '22px', 61 | color: grayColor[0], 62 | fontSize: '12px', 63 | display: 'inline-block', 64 | margin: '0!important' 65 | }, 66 | cardStatsIcon: { 67 | position: 'relative', 68 | top: '4px', 69 | width: '16px', 70 | height: '16px' 71 | }, 72 | warningCardStatsIcon: { 73 | color: warningColor[0] 74 | }, 75 | primaryCardStatsIcon: { 76 | color: primaryColor[0] 77 | }, 78 | dangerCardStatsIcon: { 79 | color: dangerColor[0] 80 | }, 81 | successCardStatsIcon: { 82 | color: successColor[0] 83 | }, 84 | infoCardStatsIcon: { 85 | color: infoColor[0] 86 | }, 87 | roseCardStatsIcon: { 88 | color: roseColor[0] 89 | }, 90 | grayCardStatsIcon: { 91 | color: grayColor[0] 92 | }, 93 | cardStatsLink: { 94 | color: primaryColor[0], 95 | textDecoration: 'none', 96 | ...defaultFont 97 | } 98 | }; 99 | 100 | export default chartCardStyle; 101 | -------------------------------------------------------------------------------- /src/assets/jss/material-dashboard-react/components/cardAvatarStyle.js: -------------------------------------------------------------------------------- 1 | import { hexToRgb, blackColor } from '../../material-dashboard-react.js'; 2 | 3 | const cardAvatarStyle = { 4 | cardAvatar: { 5 | '&$cardAvatarProfile img': { 6 | width: '100%', 7 | height: 'auto' 8 | } 9 | }, 10 | cardAvatarProfile: { 11 | maxWidth: '130px', 12 | maxHeight: '130px', 13 | margin: '-50px auto 0', 14 | borderRadius: '50%', 15 | overflow: 'hidden', 16 | padding: '0', 17 | boxShadow: 18 | '0 16px 38px -12px rgba(' + 19 | hexToRgb(blackColor) + 20 | ', 0.56), 0 4px 25px 0px rgba(' + 21 | hexToRgb(blackColor) + 22 | ', 0.12), 0 8px 10px -5px rgba(' + 23 | hexToRgb(blackColor) + 24 | ', 0.2)', 25 | '&$cardAvatarPlain': { 26 | marginTop: '0' 27 | } 28 | }, 29 | cardAvatarPlain: {} 30 | }; 31 | 32 | export default cardAvatarStyle; 33 | -------------------------------------------------------------------------------- /src/assets/jss/material-dashboard-react/components/cardBodyStyle.js: -------------------------------------------------------------------------------- 1 | const cardBodyStyle = { 2 | cardBody: { 3 | padding: '0.9375rem 20px', 4 | flex: '1 1 auto', 5 | WebkitBoxFlex: '1', 6 | position: 'relative' 7 | }, 8 | cardBodyPlain: { 9 | paddingLeft: '5px', 10 | paddingRight: '5px' 11 | }, 12 | cardBodyProfile: { 13 | marginTop: '15px' 14 | } 15 | }; 16 | 17 | export default cardBodyStyle; 18 | -------------------------------------------------------------------------------- /src/assets/jss/material-dashboard-react/components/cardFooterStyle.js: -------------------------------------------------------------------------------- 1 | import { grayColor } from '../../material-dashboard-react.js'; 2 | 3 | const cardFooterStyle = { 4 | cardFooter: { 5 | padding: '0', 6 | paddingTop: '10px', 7 | margin: '0 15px 10px', 8 | borderRadius: '0', 9 | justifyContent: 'space-between', 10 | alignItems: 'center', 11 | display: 'flex', 12 | backgroundColor: 'transparent', 13 | border: '0' 14 | }, 15 | cardFooterProfile: { 16 | marginTop: '-15px' 17 | }, 18 | cardFooterPlain: { 19 | paddingLeft: '5px', 20 | paddingRight: '5px', 21 | backgroundColor: 'transparent' 22 | }, 23 | cardFooterStats: { 24 | borderTop: '1px solid ' + grayColor[10], 25 | marginTop: '20px', 26 | '& svg': { 27 | position: 'relative', 28 | top: '4px', 29 | marginRight: '3px', 30 | marginLeft: '3px', 31 | width: '16px', 32 | height: '16px' 33 | }, 34 | '& .fab,& .fas,& .far,& .fal,& .material-icons': { 35 | fontSize: '16px', 36 | position: 'relative', 37 | top: '4px', 38 | marginRight: '3px', 39 | marginLeft: '3px' 40 | } 41 | }, 42 | cardFooterChart: { 43 | borderTop: '1px solid ' + grayColor[10] 44 | } 45 | }; 46 | 47 | export default cardFooterStyle; 48 | -------------------------------------------------------------------------------- /src/assets/jss/material-dashboard-react/components/cardIconStyle.js: -------------------------------------------------------------------------------- 1 | import { 2 | warningCardHeader, 3 | successCardHeader, 4 | dangerCardHeader, 5 | infoCardHeader, 6 | primaryCardHeader, 7 | roseCardHeader, 8 | grayColor 9 | } from '../../material-dashboard-react.js'; 10 | 11 | const cardIconStyle = { 12 | cardIcon: { 13 | '&$warningCardHeader,&$successCardHeader,&$dangerCardHeader,&$infoCardHeader,&$primaryCardHeader,&$roseCardHeader': { 14 | borderRadius: '3px', 15 | backgroundColor: grayColor[0], 16 | padding: '15px', 17 | marginTop: '-20px', 18 | marginRight: '15px', 19 | float: 'left' 20 | } 21 | }, 22 | warningCardHeader, 23 | successCardHeader, 24 | dangerCardHeader, 25 | infoCardHeader, 26 | primaryCardHeader, 27 | roseCardHeader 28 | }; 29 | 30 | export default cardIconStyle; 31 | -------------------------------------------------------------------------------- /src/assets/jss/material-dashboard-react/components/cardStyle.js: -------------------------------------------------------------------------------- 1 | import { 2 | blackColor, 3 | whiteColor, 4 | hexToRgb 5 | } from '../../material-dashboard-react.js'; 6 | 7 | const cardStyle = { 8 | card: { 9 | border: '0', 10 | marginBottom: '30px', 11 | marginTop: '30px', 12 | borderRadius: '6px', 13 | color: 'rgba(' + hexToRgb(blackColor) + ', 0.87)', 14 | background: whiteColor, 15 | width: '100%', 16 | boxShadow: '0 1px 4px 0 rgba(' + hexToRgb(blackColor) + ', 0.14)', 17 | position: 'relative', 18 | display: 'flex', 19 | flexDirection: 'column', 20 | minWidth: '0', 21 | wordWrap: 'break-word', 22 | fontSize: '.875rem' 23 | }, 24 | cardPlain: { 25 | background: 'transparent', 26 | boxShadow: 'none' 27 | }, 28 | cardProfile: { 29 | marginTop: '30px', 30 | textAlign: 'center' 31 | }, 32 | cardChart: { 33 | '& p': { 34 | marginTop: '0px', 35 | paddingTop: '0px' 36 | } 37 | } 38 | }; 39 | 40 | export default cardStyle; 41 | -------------------------------------------------------------------------------- /src/assets/jss/material-dashboard-react/components/customInputStyle.js: -------------------------------------------------------------------------------- 1 | import { 2 | primaryColor, 3 | dangerColor, 4 | successColor, 5 | grayColor, 6 | defaultFont 7 | } from '../../material-dashboard-react.js'; 8 | 9 | const customInputStyle = { 10 | disabled: { 11 | '&:before': { 12 | backgroundColor: 'transparent !important' 13 | } 14 | }, 15 | underline: { 16 | '&:hover:not($disabled):before,&:before': { 17 | borderColor: grayColor[4] + ' !important', 18 | borderWidth: '1px !important' 19 | }, 20 | '&:after': { 21 | borderColor: primaryColor[0] 22 | } 23 | }, 24 | underlineError: { 25 | '&:after': { 26 | borderColor: dangerColor[0] 27 | } 28 | }, 29 | underlineSuccess: { 30 | '&:after': { 31 | borderColor: successColor[0] 32 | } 33 | }, 34 | labelRoot: { 35 | ...defaultFont, 36 | color: grayColor[3] + ' !important', 37 | fontWeight: '400', 38 | fontSize: '14px', 39 | lineHeight: '1.42857', 40 | letterSpacing: 'unset' 41 | }, 42 | labelRootError: { 43 | color: dangerColor[0] 44 | }, 45 | labelRootSuccess: { 46 | color: successColor[0] 47 | }, 48 | feedback: { 49 | position: 'absolute', 50 | top: '18px', 51 | right: '0', 52 | zIndex: '2', 53 | display: 'block', 54 | width: '24px', 55 | height: '24px', 56 | textAlign: 'center', 57 | pointerEvents: 'none' 58 | }, 59 | marginTop: { 60 | marginTop: '16px' 61 | }, 62 | formControl: { 63 | paddingBottom: '10px', 64 | margin: '27px 0 0 0', 65 | position: 'relative', 66 | verticalAlign: 'unset' 67 | } 68 | }; 69 | 70 | export default customInputStyle; 71 | -------------------------------------------------------------------------------- /src/assets/jss/material-dashboard-react/components/customTabsStyle.js: -------------------------------------------------------------------------------- 1 | import { hexToRgb, whiteColor } from '../../material-dashboard-react.js'; 2 | 3 | const customTabsStyle = { 4 | cardTitle: { 5 | float: 'left', 6 | padding: '10px 10px 10px 0px', 7 | lineHeight: '24px' 8 | }, 9 | cardTitleRTL: { 10 | float: 'right', 11 | padding: '10px 0px 10px 10px !important' 12 | }, 13 | displayNone: { 14 | display: 'none !important' 15 | }, 16 | tabsRoot: { 17 | minHeight: 'unset !important', 18 | overflowX: 'visible', 19 | '& $tabRootButton': { 20 | fontSize: '0.875rem' 21 | } 22 | }, 23 | tabRootButton: { 24 | minHeight: 'unset !important', 25 | minWidth: 'unset !important', 26 | width: 'unset !important', 27 | height: 'unset !important', 28 | maxWidth: 'unset !important', 29 | maxHeight: 'unset !important', 30 | padding: '10px 15px', 31 | borderRadius: '3px', 32 | lineHeight: '24px', 33 | border: '0 !important', 34 | color: whiteColor + ' !important', 35 | marginLeft: '4px', 36 | '&:last-child': { 37 | marginLeft: '0px' 38 | } 39 | }, 40 | tabSelected: { 41 | backgroundColor: 'rgba(' + hexToRgb(whiteColor) + ', 0.2)', 42 | transition: '0.2s background-color 0.1s' 43 | }, 44 | tabWrapper: { 45 | display: 'inline-block', 46 | minHeight: 'unset !important', 47 | minWidth: 'unset !important', 48 | width: 'unset !important', 49 | height: 'unset !important', 50 | maxWidth: 'unset !important', 51 | maxHeight: 'unset !important', 52 | fontWeight: '500', 53 | fontSize: '12px', 54 | marginTop: '1px', 55 | '& > svg,& > .material-icons': { 56 | verticalAlign: 'middle', 57 | margin: '-1px 5px 0 0 !important' 58 | } 59 | } 60 | }; 61 | 62 | export default customTabsStyle; 63 | -------------------------------------------------------------------------------- /src/assets/jss/material-dashboard-react/components/footerStyle.js: -------------------------------------------------------------------------------- 1 | import { 2 | defaultFont, 3 | container, 4 | primaryColor, 5 | grayColor 6 | } from '../../material-dashboard-react.js'; 7 | 8 | const footerStyle = { 9 | block: { 10 | color: 'inherit', 11 | padding: '15px', 12 | textTransform: 'uppercase', 13 | borderRadius: '3px', 14 | textDecoration: 'none', 15 | position: 'relative', 16 | display: 'block', 17 | ...defaultFont, 18 | fontWeight: '500', 19 | fontSize: '12px' 20 | }, 21 | left: { 22 | float: 'left!important', 23 | display: 'block' 24 | }, 25 | right: { 26 | padding: '15px 0', 27 | margin: '0', 28 | fontSize: '14px', 29 | float: 'right!important' 30 | }, 31 | footer: { 32 | bottom: '0', 33 | borderTop: '1px solid ' + grayColor[11], 34 | padding: '15px 0', 35 | ...defaultFont 36 | }, 37 | container, 38 | a: { 39 | color: primaryColor, 40 | textDecoration: 'none', 41 | backgroundColor: 'transparent' 42 | }, 43 | list: { 44 | marginBottom: '0', 45 | padding: '0', 46 | marginTop: '0' 47 | }, 48 | inlineBlock: { 49 | display: 'inline-block', 50 | padding: '0px', 51 | width: 'auto' 52 | } 53 | }; 54 | export default footerStyle; 55 | -------------------------------------------------------------------------------- /src/assets/jss/material-dashboard-react/components/headerLinksStyle.js: -------------------------------------------------------------------------------- 1 | import { 2 | defaultFont, 3 | dangerColor, 4 | whiteColor 5 | } from '../../material-dashboard-react.js'; 6 | 7 | import dropdownStyle from '../../material-dashboard-react/dropdownStyle.js'; 8 | 9 | const headerLinksStyle = theme => ({ 10 | ...dropdownStyle(theme), 11 | search: { 12 | '& > div': { 13 | marginTop: '0' 14 | }, 15 | [theme.breakpoints.down('sm')]: { 16 | margin: '10px 15px !important', 17 | float: 'none !important', 18 | paddingTop: '1px', 19 | paddingBottom: '1px', 20 | padding: '0!important', 21 | width: '60%', 22 | marginTop: '40px', 23 | '& input': { 24 | color: whiteColor 25 | } 26 | } 27 | }, 28 | linkText: { 29 | zIndex: '4', 30 | ...defaultFont, 31 | fontSize: '14px', 32 | margin: '0px' 33 | }, 34 | buttonLink: { 35 | [theme.breakpoints.down('sm')]: { 36 | display: 'flex', 37 | margin: '10px 15px 0', 38 | width: '-webkit-fill-available', 39 | '& svg': { 40 | width: '24px', 41 | height: '30px', 42 | marginRight: '15px', 43 | marginLeft: '-15px' 44 | }, 45 | '& .fab,& .fas,& .far,& .fal,& .material-icons': { 46 | fontSize: '24px', 47 | lineHeight: '30px', 48 | width: '24px', 49 | height: '30px', 50 | marginRight: '15px', 51 | marginLeft: '-15px' 52 | }, 53 | '& > span': { 54 | justifyContent: 'flex-start', 55 | width: '100%' 56 | } 57 | } 58 | }, 59 | searchButton: { 60 | [theme.breakpoints.down('sm')]: { 61 | top: '-50px !important', 62 | marginRight: '22px', 63 | float: 'right' 64 | } 65 | }, 66 | margin: { 67 | zIndex: '4', 68 | margin: '0' 69 | }, 70 | searchIcon: { 71 | width: '17px', 72 | zIndex: '4' 73 | }, 74 | notifications: { 75 | zIndex: '4', 76 | [theme.breakpoints.up('md')]: { 77 | position: 'absolute', 78 | top: '2px', 79 | border: '1px solid ' + whiteColor, 80 | right: '4px', 81 | fontSize: '9px', 82 | background: dangerColor[0], 83 | color: whiteColor, 84 | minWidth: '16px', 85 | height: '16px', 86 | borderRadius: '10px', 87 | textAlign: 'center', 88 | lineHeight: '16px', 89 | verticalAlign: 'middle', 90 | display: 'block' 91 | }, 92 | [theme.breakpoints.down('sm')]: { 93 | ...defaultFont, 94 | fontSize: '14px', 95 | marginRight: '8px' 96 | } 97 | }, 98 | manager: { 99 | [theme.breakpoints.down('sm')]: { 100 | width: '100%' 101 | }, 102 | display: 'inline-block' 103 | }, 104 | searchWrapper: { 105 | [theme.breakpoints.down('sm')]: { 106 | width: '-webkit-fill-available', 107 | margin: '10px 15px 0' 108 | }, 109 | display: 'inline-block' 110 | } 111 | }); 112 | 113 | export default headerLinksStyle; 114 | -------------------------------------------------------------------------------- /src/assets/jss/material-dashboard-react/components/headerStyle.js: -------------------------------------------------------------------------------- 1 | import { 2 | container, 3 | defaultFont, 4 | primaryColor, 5 | defaultBoxShadow, 6 | infoColor, 7 | successColor, 8 | warningColor, 9 | dangerColor, 10 | whiteColor, 11 | grayColor 12 | } from '../../material-dashboard-react.js'; 13 | 14 | const headerStyle = () => ({ 15 | appBar: { 16 | backgroundColor: 'transparent', 17 | boxShadow: 'none', 18 | borderBottom: '0', 19 | marginBottom: '0', 20 | position: 'absolute', 21 | width: '100%', 22 | paddingTop: '10px', 23 | zIndex: '1029', 24 | color: grayColor[7], 25 | border: '0', 26 | borderRadius: '3px', 27 | padding: '10px 0', 28 | transition: 'all 150ms ease 0s', 29 | minHeight: '50px', 30 | display: 'block' 31 | }, 32 | container: { 33 | ...container, 34 | minHeight: '50px' 35 | }, 36 | flex: { 37 | flex: 1 38 | }, 39 | title: { 40 | ...defaultFont, 41 | letterSpacing: 'unset', 42 | lineHeight: '30px', 43 | fontSize: '18px', 44 | borderRadius: '3px', 45 | textTransform: 'none', 46 | color: 'inherit', 47 | margin: '0', 48 | '&:hover,&:focus': { 49 | background: 'transparent' 50 | } 51 | }, 52 | appResponsive: { 53 | top: '8px' 54 | }, 55 | primary: { 56 | backgroundColor: primaryColor[0], 57 | color: whiteColor, 58 | ...defaultBoxShadow 59 | }, 60 | info: { 61 | backgroundColor: infoColor[0], 62 | color: whiteColor, 63 | ...defaultBoxShadow 64 | }, 65 | success: { 66 | backgroundColor: successColor[0], 67 | color: whiteColor, 68 | ...defaultBoxShadow 69 | }, 70 | warning: { 71 | backgroundColor: warningColor[0], 72 | color: whiteColor, 73 | ...defaultBoxShadow 74 | }, 75 | danger: { 76 | backgroundColor: dangerColor[0], 77 | color: whiteColor, 78 | ...defaultBoxShadow 79 | } 80 | }); 81 | 82 | export default headerStyle; 83 | -------------------------------------------------------------------------------- /src/assets/jss/material-dashboard-react/components/rtlHeaderLinksStyle.js: -------------------------------------------------------------------------------- 1 | import { 2 | defaultFont, 3 | dangerColor, 4 | whiteColor 5 | } from '../../material-dashboard-react.js'; 6 | 7 | import dropdownStyle from '../../material-dashboard-react/dropdownStyle.js'; 8 | 9 | const headerLinksStyle = theme => ({ 10 | ...dropdownStyle(theme), 11 | search: { 12 | '& > div': { 13 | marginTop: '0' 14 | }, 15 | [theme.breakpoints.down('sm')]: { 16 | margin: '10px 15px !important', 17 | float: 'none !important', 18 | paddingTop: '1px', 19 | paddingBottom: '1px', 20 | padding: '0!important', 21 | width: '60%', 22 | marginTop: '40px', 23 | '& input': { 24 | color: whiteColor 25 | } 26 | } 27 | }, 28 | linkText: { 29 | zIndex: '4', 30 | ...defaultFont, 31 | fontSize: '14px', 32 | margin: '0px' 33 | }, 34 | buttonLink: { 35 | [theme.breakpoints.down('sm')]: { 36 | display: 'flex', 37 | margin: '10px 15px 0', 38 | width: '-webkit-fill-available', 39 | '& svg': { 40 | width: '24px', 41 | height: '30px', 42 | marginRight: '15px', 43 | marginLeft: '-15px' 44 | }, 45 | '& .fab,& .fas,& .far,& .fal,& .material-icons': { 46 | fontSize: '24px', 47 | lineHeight: '30px', 48 | width: '24px', 49 | height: '30px', 50 | marginRight: '15px', 51 | marginLeft: '-15px' 52 | }, 53 | '& > span': { 54 | justifyContent: 'flex-start', 55 | width: '100%' 56 | } 57 | } 58 | }, 59 | searchButton: { 60 | [theme.breakpoints.down('sm')]: { 61 | top: '-50px !important', 62 | marginRight: '22px', 63 | float: 'right' 64 | } 65 | }, 66 | margin: { 67 | zIndex: '4', 68 | margin: '0' 69 | }, 70 | searchIcon: { 71 | width: '17px', 72 | zIndex: '4' 73 | }, 74 | notifications: { 75 | zIndex: '4', 76 | [theme.breakpoints.up('md')]: { 77 | position: 'absolute', 78 | top: '2px', 79 | border: '1px solid ' + whiteColor, 80 | right: '4px', 81 | fontSize: '9px', 82 | background: dangerColor[0], 83 | color: whiteColor, 84 | minWidth: '16px', 85 | height: '16px', 86 | borderRadius: '10px', 87 | textAlign: 'center', 88 | lineHeight: '16px', 89 | verticalAlign: 'middle', 90 | display: 'block' 91 | }, 92 | [theme.breakpoints.down('sm')]: { 93 | ...defaultFont, 94 | fontSize: '14px', 95 | marginRight: '8px' 96 | } 97 | }, 98 | manager: { 99 | [theme.breakpoints.down('sm')]: { 100 | width: '100%' 101 | }, 102 | display: 'inline-block' 103 | }, 104 | searchWrapper: { 105 | [theme.breakpoints.down('sm')]: { 106 | width: '-webkit-fill-available', 107 | margin: '10px 15px 0' 108 | }, 109 | display: 'inline-block' 110 | } 111 | }); 112 | 113 | export default headerLinksStyle; 114 | -------------------------------------------------------------------------------- /src/assets/jss/material-dashboard-react/components/snackbarContentStyle.js: -------------------------------------------------------------------------------- 1 | import { 2 | defaultFont, 3 | primaryBoxShadow, 4 | infoBoxShadow, 5 | successBoxShadow, 6 | warningBoxShadow, 7 | dangerBoxShadow, 8 | roseBoxShadow, 9 | whiteColor, 10 | blackColor, 11 | grayColor, 12 | infoColor, 13 | successColor, 14 | dangerColor, 15 | roseColor, 16 | primaryColor, 17 | warningColor, 18 | hexToRgb 19 | } from '../../material-dashboard-react.js'; 20 | 21 | const snackbarContentStyle = { 22 | root: { 23 | ...defaultFont, 24 | flexWrap: 'unset', 25 | position: 'relative', 26 | padding: '20px 15px', 27 | lineHeight: '20px', 28 | marginBottom: '20px', 29 | fontSize: '14px', 30 | backgroundColor: whiteColor, 31 | color: grayColor[7], 32 | borderRadius: '3px', 33 | minWidth: 'unset', 34 | maxWidth: 'unset', 35 | boxShadow: 36 | '0 12px 20px -10px rgba(' + 37 | hexToRgb(whiteColor) + 38 | ', 0.28), 0 4px 20px 0px rgba(' + 39 | hexToRgb(blackColor) + 40 | ', 0.12), 0 7px 8px -5px rgba(' + 41 | hexToRgb(whiteColor) + 42 | ', 0.2)' 43 | }, 44 | top20: { 45 | top: '20px' 46 | }, 47 | top40: { 48 | top: '40px' 49 | }, 50 | info: { 51 | backgroundColor: infoColor[3], 52 | color: whiteColor, 53 | ...infoBoxShadow 54 | }, 55 | success: { 56 | backgroundColor: successColor[3], 57 | color: whiteColor, 58 | ...successBoxShadow 59 | }, 60 | warning: { 61 | backgroundColor: warningColor[3], 62 | color: whiteColor, 63 | ...warningBoxShadow 64 | }, 65 | danger: { 66 | backgroundColor: dangerColor[3], 67 | color: whiteColor, 68 | ...dangerBoxShadow 69 | }, 70 | primary: { 71 | backgroundColor: primaryColor[3], 72 | color: whiteColor, 73 | ...primaryBoxShadow 74 | }, 75 | rose: { 76 | backgroundColor: roseColor[3], 77 | color: whiteColor, 78 | ...roseBoxShadow 79 | }, 80 | message: { 81 | padding: '0', 82 | display: 'block', 83 | maxWidth: '89%' 84 | }, 85 | close: { 86 | width: '11px', 87 | height: '11px' 88 | }, 89 | iconButton: { 90 | width: '24px', 91 | height: '24px', 92 | padding: '0px' 93 | }, 94 | icon: { 95 | display: 'block', 96 | left: '15px', 97 | position: 'absolute', 98 | top: '50%', 99 | marginTop: '-15px', 100 | width: '30px', 101 | height: '30px' 102 | }, 103 | infoIcon: { 104 | color: infoColor[3] 105 | }, 106 | successIcon: { 107 | color: successColor[3] 108 | }, 109 | warningIcon: { 110 | color: warningColor[3] 111 | }, 112 | dangerIcon: { 113 | color: dangerColor[3] 114 | }, 115 | primaryIcon: { 116 | color: primaryColor[3] 117 | }, 118 | roseIcon: { 119 | color: roseColor[3] 120 | }, 121 | iconMessage: { 122 | paddingLeft: '50px', 123 | display: 'block' 124 | }, 125 | actionRTL: { 126 | marginLeft: '-8px', 127 | marginRight: 'auto' 128 | } 129 | }; 130 | 131 | export default snackbarContentStyle; 132 | -------------------------------------------------------------------------------- /src/assets/jss/material-dashboard-react/components/tableStyle.js: -------------------------------------------------------------------------------- 1 | import { 2 | warningColor, 3 | primaryColor, 4 | dangerColor, 5 | successColor, 6 | infoColor, 7 | roseColor, 8 | grayColor, 9 | defaultFont 10 | } from '../../material-dashboard-react.js'; 11 | 12 | const tableStyle = theme => ({ 13 | warningTableHeader: { 14 | color: warningColor[0] 15 | }, 16 | primaryTableHeader: { 17 | color: primaryColor[0] 18 | }, 19 | dangerTableHeader: { 20 | color: dangerColor[0] 21 | }, 22 | successTableHeader: { 23 | color: successColor[0] 24 | }, 25 | infoTableHeader: { 26 | color: infoColor[0] 27 | }, 28 | roseTableHeader: { 29 | color: roseColor[0] 30 | }, 31 | grayTableHeader: { 32 | color: grayColor[0] 33 | }, 34 | table: { 35 | marginBottom: '0', 36 | width: '100%', 37 | maxWidth: '100%', 38 | backgroundColor: 'transparent', 39 | borderSpacing: '0', 40 | borderCollapse: 'collapse' 41 | }, 42 | tableHeadCell: { 43 | color: 'inherit', 44 | ...defaultFont, 45 | '&, &$tableCell': { 46 | fontSize: '1em' 47 | } 48 | }, 49 | tableCell: { 50 | ...defaultFont, 51 | lineHeight: '1.42857143', 52 | padding: '12px 8px', 53 | verticalAlign: 'middle', 54 | fontSize: '0.8125rem' 55 | }, 56 | tableResponsive: { 57 | width: '100%', 58 | marginTop: theme.spacing(3), 59 | overflowX: 'auto' 60 | }, 61 | tableHeadRow: { 62 | height: '56px', 63 | color: 'inherit', 64 | display: 'table-row', 65 | outline: 'none', 66 | verticalAlign: 'middle' 67 | }, 68 | tableBodyRow: { 69 | height: '48px', 70 | color: 'inherit', 71 | display: 'table-row', 72 | outline: 'none', 73 | verticalAlign: 'middle' 74 | } 75 | }); 76 | 77 | export default tableStyle; 78 | -------------------------------------------------------------------------------- /src/assets/jss/material-dashboard-react/components/tasksStyle.js: -------------------------------------------------------------------------------- 1 | import { 2 | defaultFont, 3 | primaryColor, 4 | dangerColor, 5 | grayColor 6 | } from '../../material-dashboard-react.js'; 7 | import tooltipStyle from '../../material-dashboard-react/tooltipStyle.js'; 8 | import checkboxAdnRadioStyle from '../../material-dashboard-react/checkboxAdnRadioStyle.js'; 9 | const tasksStyle = { 10 | ...tooltipStyle, 11 | ...checkboxAdnRadioStyle, 12 | table: { 13 | marginBottom: '0', 14 | overflow: 'visible' 15 | }, 16 | tableRow: { 17 | position: 'relative', 18 | borderBottom: '1px solid ' + grayColor[5] 19 | }, 20 | tableActions: { 21 | display: 'flex', 22 | border: 'none', 23 | padding: '12px 8px !important', 24 | verticalAlign: 'middle' 25 | }, 26 | tableCell: { 27 | ...defaultFont, 28 | padding: '8px', 29 | verticalAlign: 'middle', 30 | border: 'none', 31 | lineHeight: '1.42857143', 32 | fontSize: '14px' 33 | }, 34 | tableCellRTL: { 35 | textAlign: 'right' 36 | }, 37 | tableActionButton: { 38 | width: '27px', 39 | height: '27px', 40 | padding: '0' 41 | }, 42 | tableActionButtonIcon: { 43 | width: '17px', 44 | height: '17px' 45 | }, 46 | edit: { 47 | backgroundColor: 'transparent', 48 | color: primaryColor[0], 49 | boxShadow: 'none' 50 | }, 51 | close: { 52 | backgroundColor: 'transparent', 53 | color: dangerColor[0], 54 | boxShadow: 'none' 55 | } 56 | }; 57 | export default tasksStyle; 58 | -------------------------------------------------------------------------------- /src/assets/jss/material-dashboard-react/components/typographyStyle.js: -------------------------------------------------------------------------------- 1 | import { 2 | defaultFont, 3 | primaryColor, 4 | infoColor, 5 | successColor, 6 | warningColor, 7 | dangerColor, 8 | grayColor 9 | } from '../../material-dashboard-react.js'; 10 | 11 | const typographyStyle = { 12 | defaultFontStyle: { 13 | ...defaultFont, 14 | fontSize: '14px' 15 | }, 16 | defaultHeaderMargins: { 17 | marginTop: '20px', 18 | marginBottom: '10px' 19 | }, 20 | quote: { 21 | padding: '10px 20px', 22 | margin: '0 0 20px', 23 | fontSize: '17.5px', 24 | borderLeft: '5px solid ' + grayColor[10] 25 | }, 26 | quoteText: { 27 | margin: '0 0 10px', 28 | fontStyle: 'italic' 29 | }, 30 | quoteAuthor: { 31 | display: 'block', 32 | fontSize: '80%', 33 | lineHeight: '1.42857143', 34 | color: grayColor[1] 35 | }, 36 | mutedText: { 37 | color: grayColor[1] 38 | }, 39 | primaryText: { 40 | color: primaryColor[0] 41 | }, 42 | infoText: { 43 | color: infoColor[0] 44 | }, 45 | successText: { 46 | color: successColor[0] 47 | }, 48 | warningText: { 49 | color: warningColor[0] 50 | }, 51 | dangerText: { 52 | color: dangerColor[0] 53 | } 54 | }; 55 | 56 | export default typographyStyle; 57 | -------------------------------------------------------------------------------- /src/assets/jss/material-dashboard-react/customInputStyle.jsx: -------------------------------------------------------------------------------- 1 | // ############################## 2 | // // // CustomInput styles 3 | // ############################# 4 | 5 | import { 6 | primaryColor, 7 | dangerColor, 8 | successColor, 9 | defaultFont 10 | } from '../material-dashboard-react.js'; 11 | 12 | const customInputStyle = { 13 | disabled: { 14 | '&:before': { 15 | backgroundColor: 'transparent !important' 16 | } 17 | }, 18 | underline: { 19 | '&:hover:not($disabled):before,&:before': { 20 | backgroundColor: '#D2D2D2', 21 | height: '1px !important' 22 | }, 23 | '&:after': { 24 | backgroundColor: primaryColor[0] 25 | } 26 | }, 27 | underlineError: { 28 | '&:after': { 29 | backgroundColor: dangerColor[0] 30 | } 31 | }, 32 | underlineSuccess: { 33 | '&:after': { 34 | backgroundColor: successColor[0] 35 | } 36 | }, 37 | labelRoot: { 38 | ...defaultFont, 39 | color: '#AAAAAA', 40 | fontWeight: '400', 41 | fontSize: '14px', 42 | lineHeight: '1.42857' 43 | }, 44 | labelRootError: { 45 | color: dangerColor[0] 46 | }, 47 | labelRootSuccess: { 48 | color: successColor[0] 49 | }, 50 | feedback: { 51 | position: 'absolute', 52 | top: '18px', 53 | right: '0', 54 | zIndex: '2', 55 | display: 'block', 56 | width: '24px', 57 | height: '24px', 58 | textAlign: 'center', 59 | pointerEvents: 'none' 60 | }, 61 | marginTop: { 62 | marginTop: '16px' 63 | }, 64 | formControl: { 65 | paddingBottom: '10px', 66 | margin: '27px 0 0 0', 67 | position: 'relative' 68 | } 69 | }; 70 | 71 | export default customInputStyle; 72 | -------------------------------------------------------------------------------- /src/assets/jss/material-dashboard-react/dashboardStyle.jsx: -------------------------------------------------------------------------------- 1 | import { 2 | successColor, 3 | whiteColor, 4 | grayColor, 5 | hexToRgb 6 | } from '../material-dashboard-react.js'; 7 | 8 | const dashboardStyle = { 9 | successText: { 10 | color: successColor[0] 11 | }, 12 | upArrowCardCategory: { 13 | width: '16px', 14 | height: '16px' 15 | }, 16 | stats: { 17 | color: grayColor[0], 18 | display: 'inline-flex', 19 | fontSize: '12px', 20 | lineHeight: '22px', 21 | '& svg': { 22 | top: '4px', 23 | width: '16px', 24 | height: '16px', 25 | position: 'relative', 26 | marginRight: '3px', 27 | marginLeft: '3px' 28 | }, 29 | '& .fab,& .fas,& .far,& .fal,& .material-icons': { 30 | top: '4px', 31 | fontSize: '16px', 32 | position: 'relative', 33 | marginRight: '3px', 34 | marginLeft: '3px' 35 | } 36 | }, 37 | cardCategory: { 38 | color: grayColor[0], 39 | margin: '0', 40 | fontSize: '14px', 41 | marginTop: '0', 42 | paddingTop: '10px', 43 | marginBottom: '0' 44 | }, 45 | cardCategoryWhite: { 46 | color: 'rgba(' + hexToRgb(whiteColor) + ',.62)', 47 | margin: '0', 48 | fontSize: '14px', 49 | marginTop: '0', 50 | marginBottom: '0' 51 | }, 52 | cardTitle: { 53 | color: grayColor[2], 54 | marginTop: '0px', 55 | minHeight: 'auto', 56 | fontWeight: '300', 57 | fontFamily: "'Roboto', 'Helvetica', 'Arial', sans-serif", 58 | marginBottom: '3px', 59 | textDecoration: 'none', 60 | '& small': { 61 | color: grayColor[1], 62 | fontWeight: '400', 63 | lineHeight: '1' 64 | } 65 | }, 66 | cardTitleWhite: { 67 | color: whiteColor, 68 | marginTop: '0px', 69 | minHeight: 'auto', 70 | fontWeight: '300', 71 | fontFamily: "'Roboto', 'Helvetica', 'Arial', sans-serif", 72 | marginBottom: '3px', 73 | textDecoration: 'none', 74 | '& small': { 75 | color: grayColor[1], 76 | fontWeight: '400', 77 | lineHeight: '1' 78 | } 79 | } 80 | }; 81 | 82 | export default dashboardStyle; 83 | -------------------------------------------------------------------------------- /src/assets/jss/material-dashboard-react/footerStyle.jsx: -------------------------------------------------------------------------------- 1 | import { 2 | defaultFont, 3 | container, 4 | primaryColor, 5 | grayColor 6 | } from '../material-dashboard-react.js'; 7 | 8 | const footerStyle = { 9 | block: { 10 | color: 'inherit', 11 | padding: '15px', 12 | textTransform: 'uppercase', 13 | borderRadius: '3px', 14 | textDecoration: 'none', 15 | position: 'relative', 16 | display: 'block', 17 | ...defaultFont, 18 | fontWeight: '500', 19 | fontSize: '12px' 20 | }, 21 | left: { 22 | float: 'left!important', 23 | display: 'block' 24 | }, 25 | right: { 26 | padding: '15px 0', 27 | margin: '0', 28 | fontSize: '14px', 29 | float: 'right!important' 30 | }, 31 | footer: { 32 | bottom: '0', 33 | borderTop: '1px solid ' + grayColor[11], 34 | padding: '15px 0', 35 | ...defaultFont 36 | }, 37 | container, 38 | a: { 39 | color: primaryColor, 40 | textDecoration: 'none', 41 | backgroundColor: 'transparent' 42 | }, 43 | list: { 44 | marginBottom: '0', 45 | padding: '0', 46 | marginTop: '0' 47 | }, 48 | inlineBlock: { 49 | display: 'inline-block', 50 | padding: '0px', 51 | width: 'auto' 52 | } 53 | }; 54 | export default footerStyle; 55 | -------------------------------------------------------------------------------- /src/assets/jss/material-dashboard-react/headerLinksStyle.jsx: -------------------------------------------------------------------------------- 1 | import { 2 | defaultFont, 3 | dangerColor, 4 | whiteColor 5 | } from '../material-dashboard-react.js'; 6 | 7 | import dropdownStyle from '../material-dashboard-react/dropdownStyle.js'; 8 | 9 | const headerLinksStyle = theme => ({ 10 | ...dropdownStyle(theme), 11 | search: { 12 | '& > div': { 13 | marginTop: '0' 14 | }, 15 | [theme.breakpoints.down('sm')]: { 16 | margin: '10px 15px !important', 17 | float: 'none !important', 18 | paddingTop: '1px', 19 | paddingBottom: '1px', 20 | padding: '0!important', 21 | width: '60%', 22 | marginTop: '40px', 23 | '& input': { 24 | color: whiteColor 25 | } 26 | } 27 | }, 28 | linkText: { 29 | zIndex: '4', 30 | ...defaultFont, 31 | fontSize: '14px', 32 | margin: '0px' 33 | }, 34 | buttonLink: { 35 | [theme.breakpoints.down('sm')]: { 36 | display: 'flex', 37 | margin: '10px 15px 0', 38 | width: '-webkit-fill-available', 39 | '& svg': { 40 | width: '24px', 41 | height: '30px', 42 | marginRight: '15px', 43 | marginLeft: '-15px' 44 | }, 45 | '& .fab,& .fas,& .far,& .fal,& .material-icons': { 46 | fontSize: '24px', 47 | lineHeight: '30px', 48 | width: '24px', 49 | height: '30px', 50 | marginRight: '15px', 51 | marginLeft: '-15px' 52 | }, 53 | '& > span': { 54 | justifyContent: 'flex-start', 55 | width: '100%' 56 | } 57 | } 58 | }, 59 | searchButton: { 60 | [theme.breakpoints.down('sm')]: { 61 | top: '-50px !important', 62 | marginRight: '22px', 63 | float: 'right' 64 | } 65 | }, 66 | margin: { 67 | zIndex: '4', 68 | margin: '0' 69 | }, 70 | searchIcon: { 71 | width: '17px', 72 | zIndex: '4' 73 | }, 74 | notifications: { 75 | zIndex: '4', 76 | [theme.breakpoints.up('md')]: { 77 | position: 'absolute', 78 | top: '2px', 79 | border: '1px solid ' + whiteColor, 80 | right: '4px', 81 | fontSize: '9px', 82 | background: dangerColor[0], 83 | color: whiteColor, 84 | minWidth: '16px', 85 | height: '16px', 86 | borderRadius: '10px', 87 | textAlign: 'center', 88 | lineHeight: '16px', 89 | verticalAlign: 'middle', 90 | display: 'block' 91 | }, 92 | [theme.breakpoints.down('sm')]: { 93 | ...defaultFont, 94 | fontSize: '14px', 95 | marginRight: '8px' 96 | } 97 | }, 98 | manager: { 99 | [theme.breakpoints.down('sm')]: { 100 | width: '100%' 101 | }, 102 | display: 'inline-block' 103 | }, 104 | searchWrapper: { 105 | [theme.breakpoints.down('sm')]: { 106 | width: '-webkit-fill-available', 107 | margin: '10px 15px 0' 108 | }, 109 | display: 'inline-block' 110 | } 111 | }); 112 | 113 | export default headerLinksStyle; 114 | -------------------------------------------------------------------------------- /src/assets/jss/material-dashboard-react/headerStyle.jsx: -------------------------------------------------------------------------------- 1 | import { 2 | container, 3 | defaultFont, 4 | primaryColor, 5 | defaultBoxShadow, 6 | infoColor, 7 | successColor, 8 | warningColor, 9 | dangerColor, 10 | whiteColor, 11 | grayColor 12 | } from '../material-dashboard-react.js'; 13 | 14 | const headerStyle = () => ({ 15 | appBar: { 16 | backgroundColor: 'transparent', 17 | boxShadow: 'none', 18 | borderBottom: '0', 19 | marginBottom: '0', 20 | position: 'absolute', 21 | width: '100%', 22 | paddingTop: '10px', 23 | zIndex: '1029', 24 | color: grayColor[7], 25 | border: '0', 26 | borderRadius: '3px', 27 | padding: '10px 0', 28 | transition: 'all 150ms ease 0s', 29 | minHeight: '50px', 30 | display: 'block' 31 | }, 32 | container: { 33 | ...container, 34 | minHeight: '50px' 35 | }, 36 | flex: { 37 | flex: 1 38 | }, 39 | title: { 40 | ...defaultFont, 41 | letterSpacing: 'unset', 42 | lineHeight: '30px', 43 | fontSize: '18px', 44 | borderRadius: '3px', 45 | textTransform: 'none', 46 | color: 'inherit', 47 | margin: '0', 48 | '&:hover,&:focus': { 49 | background: 'transparent' 50 | } 51 | }, 52 | appResponsive: { 53 | top: '8px' 54 | }, 55 | primary: { 56 | backgroundColor: primaryColor[0], 57 | color: whiteColor, 58 | ...defaultBoxShadow 59 | }, 60 | info: { 61 | backgroundColor: infoColor[0], 62 | color: whiteColor, 63 | ...defaultBoxShadow 64 | }, 65 | success: { 66 | backgroundColor: successColor[0], 67 | color: whiteColor, 68 | ...defaultBoxShadow 69 | }, 70 | warning: { 71 | backgroundColor: warningColor[0], 72 | color: whiteColor, 73 | ...defaultBoxShadow 74 | }, 75 | danger: { 76 | backgroundColor: dangerColor[0], 77 | color: whiteColor, 78 | ...defaultBoxShadow 79 | } 80 | }); 81 | 82 | export default headerStyle; 83 | -------------------------------------------------------------------------------- /src/assets/jss/material-dashboard-react/iconsStyle.jsx: -------------------------------------------------------------------------------- 1 | // ############################## 2 | // // // Icons styles 3 | // ############################# 4 | 5 | import { boxShadow } from '../material-dashboard-react.js'; 6 | 7 | const iconsStyle = { 8 | iframe: { 9 | width: '100%', 10 | height: '500px', 11 | border: '0', 12 | ...boxShadow 13 | }, 14 | iframeContainer: { 15 | margin: '0 -20px 0' 16 | } 17 | }; 18 | 19 | export default iconsStyle; 20 | -------------------------------------------------------------------------------- /src/assets/jss/material-dashboard-react/profileCardStyle.jsx: -------------------------------------------------------------------------------- 1 | // ############################## 2 | // // // ProfileCard styles 3 | // ############################# 4 | 5 | import { 6 | card, 7 | boxShadow, 8 | grayColor, 9 | defaultFont 10 | } from '../material-dashboard-react.js'; 11 | 12 | const profileCardStyle = { 13 | card: { 14 | marginTop: '30px', 15 | textAlign: 'center', 16 | ...card 17 | }, 18 | cardHeader: { 19 | display: 'inline-block', 20 | width: '100%', 21 | padding: '0px' 22 | }, 23 | cardAvatar: { 24 | maxWidth: '130px', 25 | maxHeight: '130px', 26 | margin: '-50px auto 0', 27 | borderRadius: '50%', 28 | overflow: 'hidden', 29 | ...boxShadow 30 | }, 31 | img: { 32 | width: '100%', 33 | height: 'auto', 34 | verticalAlign: 'middle', 35 | border: '0' 36 | }, 37 | textAlign: { 38 | textAlign: 'center' 39 | }, 40 | cardSubtitle: { 41 | color: grayColor, 42 | ...defaultFont, 43 | fontSize: '1em', 44 | textTransform: 'uppercase', 45 | marginTop: '10px', 46 | marginBottom: '10px' 47 | }, 48 | cardTitle: { 49 | ...defaultFont, 50 | fontSize: '1.3em', 51 | marginTop: '10px', 52 | marginBottom: '10px' 53 | }, 54 | cardDescription: { 55 | ...defaultFont, 56 | padding: '15px 20px', 57 | margin: '0 0 10px' 58 | }, 59 | cardActions: { 60 | height: 'auto', 61 | display: 'inline' 62 | } 63 | }; 64 | 65 | export default profileCardStyle; 66 | -------------------------------------------------------------------------------- /src/assets/jss/material-dashboard-react/regularCardStyle.jsx: -------------------------------------------------------------------------------- 1 | // ############################## 2 | // // // RegularCard styles 3 | // ############################# 4 | 5 | import { 6 | card, 7 | cardHeader, 8 | defaultFont, 9 | orangeCardHeader, 10 | greenCardHeader, 11 | redCardHeader, 12 | blueCardHeader, 13 | purpleCardHeader 14 | } from '../material-dashboard-react.js'; 15 | 16 | const regularCardStyle = { 17 | card: { 18 | ...card, 19 | overflow: 'visible' 20 | }, 21 | cardPlain: { 22 | background: 'transparent', 23 | boxShadow: 'none' 24 | }, 25 | cardHeader: { 26 | ...cardHeader, 27 | ...defaultFont 28 | }, 29 | cardPlainHeader: { 30 | marginLeft: 0, 31 | marginRight: 0 32 | }, 33 | orangeCardHeader, 34 | greenCardHeader, 35 | redCardHeader, 36 | blueCardHeader, 37 | purpleCardHeader, 38 | cardTitle: { 39 | color: '#FFFFFF', 40 | marginTop: '0', 41 | marginBottom: '5px', 42 | ...defaultFont, 43 | fontSize: '1.125em' 44 | }, 45 | cardSubtitle: { 46 | ...defaultFont, 47 | marginBottom: '0', 48 | color: 'rgba(255, 255, 255, 0.62)', 49 | margin: '0 0 10px' 50 | }, 51 | cardActions: { 52 | padding: '14px', 53 | display: 'block', 54 | height: 'auto' 55 | } 56 | }; 57 | 58 | export default regularCardStyle; 59 | -------------------------------------------------------------------------------- /src/assets/jss/material-dashboard-react/snackbarContentStyle.jsx: -------------------------------------------------------------------------------- 1 | // ############################## 2 | // // // SnackbarContent styles 3 | // ############################# 4 | 5 | import { 6 | defaultFont, 7 | primaryBoxShadow, 8 | infoBoxShadow, 9 | successBoxShadow, 10 | warningBoxShadow, 11 | dangerBoxShadow 12 | } from '../material-dashboard-react.js'; 13 | 14 | const snackbarContentStyle = { 15 | root: { 16 | ...defaultFont, 17 | position: 'relative', 18 | padding: '20px 15px', 19 | lineHeight: '20px', 20 | marginBottom: '20px', 21 | fontSize: '14px', 22 | backgroundColor: 'white', 23 | color: '#555555', 24 | borderRadius: '3px', 25 | boxShadow: 26 | '0 12px 20px -10px rgba(255, 255, 255, 0.28), 0 4px 20px 0px rgba(0, 0, 0, 0.12), 0 7px 8px -5px rgba(255, 255, 255, 0.2)' 27 | }, 28 | info: { 29 | backgroundColor: '#00d3ee', 30 | color: '#ffffff', 31 | ...infoBoxShadow 32 | }, 33 | success: { 34 | backgroundColor: '#5cb860', 35 | color: '#ffffff', 36 | ...successBoxShadow 37 | }, 38 | warning: { 39 | backgroundColor: '#ffa21a', 40 | color: '#ffffff', 41 | ...warningBoxShadow 42 | }, 43 | danger: { 44 | backgroundColor: '#f55a4e', 45 | color: '#ffffff', 46 | ...dangerBoxShadow 47 | }, 48 | primary: { 49 | backgroundColor: '#af2cc5', 50 | color: '#ffffff', 51 | ...primaryBoxShadow 52 | }, 53 | message: { 54 | padding: '0', 55 | display: 'block', 56 | maxWidth: '89%' 57 | }, 58 | close: { 59 | width: '14px', 60 | height: '14px' 61 | }, 62 | iconButton: { 63 | width: '24px', 64 | height: '24px' 65 | }, 66 | icon: { 67 | display: 'block', 68 | left: '15px', 69 | position: 'absolute', 70 | top: '50%', 71 | marginTop: '-15px', 72 | width: '30px', 73 | height: '30px' 74 | }, 75 | iconMessage: { 76 | paddingLeft: '65px', 77 | display: 'block' 78 | } 79 | }; 80 | 81 | export default snackbarContentStyle; 82 | -------------------------------------------------------------------------------- /src/assets/jss/material-dashboard-react/statsCardStyle.jsx: -------------------------------------------------------------------------------- 1 | // ############################## 2 | // // // StatsCard styles 3 | // ############################# 4 | 5 | import { 6 | card, 7 | cardHeader, 8 | defaultFont, 9 | orangeCardHeader, 10 | greenCardHeader, 11 | redCardHeader, 12 | blueCardHeader, 13 | purpleCardHeader, 14 | cardActions, 15 | grayColor, 16 | warningColor, 17 | dangerColor, 18 | successColor, 19 | infoColor, 20 | primaryColor, 21 | roseColor 22 | } from '../material-dashboard-react.js'; 23 | 24 | const statsCardStyle = { 25 | card: { 26 | ...card, 27 | overflow: 'visible' 28 | }, 29 | cardHeader: { 30 | ...cardHeader, 31 | float: 'left', 32 | textAlign: 'center' 33 | }, 34 | orangeCardHeader, 35 | greenCardHeader, 36 | redCardHeader, 37 | blueCardHeader, 38 | purpleCardHeader, 39 | cardContent: { 40 | textAlign: 'right', 41 | paddingTop: '10px', 42 | padding: '15px 20px' 43 | }, 44 | cardIcon: { 45 | width: '40px!important', 46 | height: '36px!important', 47 | fill: '#fff!important' 48 | }, 49 | cardAvatar: { 50 | margin: '8px' 51 | }, 52 | cardCategory: { 53 | marginBottom: '0', 54 | color: grayColor[0], 55 | margin: '0 0 10px', 56 | ...defaultFont 57 | }, 58 | cardTitle: { 59 | margin: '0', 60 | ...defaultFont, 61 | fontSize: '1.625em' 62 | }, 63 | cardTitleSmall: { 64 | fontSize: '65%', 65 | fontWeight: '400', 66 | lineHeight: '1', 67 | color: '#777' 68 | }, 69 | cardActions: { 70 | ...cardActions, 71 | padding: '0!important' 72 | }, 73 | cardStats: { 74 | lineHeight: '22px', 75 | color: grayColor[0], 76 | fontSize: '12px', 77 | display: 'inline-block', 78 | margin: '0!important' 79 | }, 80 | cardStatsIcon: { 81 | position: 'relative', 82 | top: '4px', 83 | width: '16px', 84 | height: '16px' 85 | }, 86 | warningCardStatsIcon: { 87 | color: warningColor[0] 88 | }, 89 | primaryCardStatsIcon: { 90 | color: primaryColor[0] 91 | }, 92 | dangerCardStatsIcon: { 93 | color: dangerColor[0] 94 | }, 95 | successCardStatsIcon: { 96 | color: successColor[0] 97 | }, 98 | infoCardStatsIcon: { 99 | color: infoColor[0] 100 | }, 101 | roseCardStatsIcon: { 102 | color: roseColor[0] 103 | }, 104 | grayCardStatsIcon: { 105 | color: grayColor[0] 106 | }, 107 | cardStatsLink: { 108 | color: primaryColor[0], 109 | textDecoration: 'none', 110 | ...defaultFont 111 | } 112 | }; 113 | 114 | export default statsCardStyle; 115 | -------------------------------------------------------------------------------- /src/assets/jss/material-dashboard-react/tableStyle.jsx: -------------------------------------------------------------------------------- 1 | // ############################## 2 | // // // Table styles 3 | // ############################# 4 | 5 | import { 6 | warningColor, 7 | primaryColor, 8 | dangerColor, 9 | successColor, 10 | infoColor, 11 | roseColor, 12 | grayColor, 13 | defaultFont 14 | } from '../material-dashboard-react.js'; 15 | 16 | const tableStyle = theme => ({ 17 | warningTableHeader: { 18 | color: warningColor[0] 19 | }, 20 | primaryTableHeader: { 21 | color: primaryColor[0] 22 | }, 23 | dangerTableHeader: { 24 | color: dangerColor[0] 25 | }, 26 | successTableHeader: { 27 | color: successColor[0] 28 | }, 29 | infoTableHeader: { 30 | color: infoColor[0] 31 | }, 32 | roseTableHeader: { 33 | color: roseColor[0] 34 | }, 35 | grayTableHeader: { 36 | color: grayColor[0] 37 | }, 38 | table: { 39 | marginBottom: '0', 40 | width: '100%', 41 | maxWidth: '100%', 42 | backgroundColor: 'transparent', 43 | borderSpacing: '0', 44 | borderCollapse: 'collapse' 45 | }, 46 | tableHeadCell: { 47 | color: 'inherit', 48 | ...defaultFont, 49 | fontSize: '1em' 50 | }, 51 | tableCell: { 52 | ...defaultFont, 53 | lineHeight: '1.42857143', 54 | padding: '12px 8px', 55 | verticalAlign: 'middle' 56 | }, 57 | tableResponsive: { 58 | width: '100%', 59 | marginTop: theme.spacing(3), 60 | overflowX: 'auto' 61 | } 62 | }); 63 | 64 | export default tableStyle; 65 | -------------------------------------------------------------------------------- /src/assets/jss/material-dashboard-react/tasksCardStyle.jsx: -------------------------------------------------------------------------------- 1 | // ############################## 2 | // // // TasksCard styles 3 | // ############################# 4 | 5 | import { 6 | card, 7 | cardHeader, 8 | defaultFont, 9 | primaryBoxShadow 10 | } from '../material-dashboard-react.js'; 11 | 12 | const tasksCardStyle = theme => ({ 13 | card: { 14 | ...card, 15 | overflow: 'visible' 16 | }, 17 | cardHeader: { 18 | flex: 'none', 19 | ...cardHeader, 20 | ...defaultFont, 21 | background: 'linear-gradient(60deg, #ab47bc, #8e24aa)', 22 | ...primaryBoxShadow 23 | }, 24 | cardTitle: { 25 | ...defaultFont, 26 | float: 'left', 27 | fontWeight: '500', 28 | padding: '10px 10px 10px 0', 29 | lineHeight: '24px', 30 | fontSize: '14px', 31 | color: '#FFFFFF' 32 | }, 33 | tabWrapper: { 34 | width: 'auto', 35 | display: 'inline-flex', 36 | alignItems: 'inherit', 37 | flexDirection: 'row', 38 | justifyContent: 'center', 39 | [theme.breakpoints.down('sm')]: { 40 | display: 'flex' 41 | } 42 | }, 43 | tabIcon: { 44 | float: 'left', 45 | [theme.breakpoints.down('sm')]: { 46 | marginTop: '-2px' 47 | } 48 | }, 49 | displayNone: { 50 | display: 'none' 51 | }, 52 | labelIcon: { 53 | height: '44px', 54 | width: '110px', 55 | minWidth: '72px', 56 | paddingLeft: '14px', 57 | borderRadius: '3px' 58 | }, 59 | tabsContainer: { 60 | marginTop: '4px', 61 | color: '#FFFFFF', 62 | [theme.breakpoints.down('sm')]: { 63 | display: 'grid' 64 | } 65 | }, 66 | tabs: { 67 | width: '110px', 68 | minWidth: '70px', 69 | paddingLeft: '12px' 70 | }, 71 | cardHeaderContent: { 72 | flex: 'none' 73 | }, 74 | label: { 75 | lineHeight: '19px', 76 | textTransform: 'uppercase', 77 | fontSize: '12px', 78 | fontWeight: '500', 79 | marginLeft: '-10px' 80 | }, 81 | textColorInheritSelected: { 82 | backgroundColor: 'rgba(255, 255, 255, 0.2)', 83 | transition: 'background-color .4s' 84 | } 85 | }); 86 | 87 | export default tasksCardStyle; 88 | -------------------------------------------------------------------------------- /src/assets/jss/material-dashboard-react/tasksStyle.jsx: -------------------------------------------------------------------------------- 1 | // ############################## 2 | // // // Tasks styles 3 | // ############################# 4 | 5 | import { 6 | defaultFont, 7 | primaryColor, 8 | dangerColor 9 | } from '../material-dashboard-react.js'; 10 | 11 | const tasksStyle = { 12 | table: { 13 | marginBottom: '0', 14 | overflow: 'visible' 15 | }, 16 | tableRow: { 17 | position: 'relative', 18 | borderBottom: '1px solid #dddddd' 19 | }, 20 | tableActions: { 21 | display: 'flex', 22 | border: 'none', 23 | padding: '12px 8px !important', 24 | verticalAlign: 'middle' 25 | }, 26 | tableCell: { 27 | ...defaultFont, 28 | padding: '8px', 29 | verticalAlign: 'middle', 30 | border: 'none', 31 | lineHeight: '1.42857143', 32 | fontSize: '14px' 33 | }, 34 | tableActionButton: { 35 | width: '27px', 36 | height: '27px' 37 | }, 38 | tableActionButtonIcon: { 39 | width: '17px', 40 | height: '17px' 41 | }, 42 | edit: { 43 | backgroundColor: 'transparent', 44 | color: primaryColor, 45 | boxShadow: 'none' 46 | }, 47 | close: { 48 | backgroundColor: 'transparent', 49 | color: dangerColor, 50 | boxShadow: 'none' 51 | }, 52 | checked: { 53 | color: primaryColor 54 | }, 55 | checkedIcon: { 56 | width: '20px', 57 | height: '20px', 58 | border: '1px solid rgba(0, 0, 0, .54)', 59 | borderRadius: '3px' 60 | }, 61 | uncheckedIcon: { 62 | width: '0px', 63 | height: '0px', 64 | padding: '10px', 65 | border: '1px solid rgba(0, 0, 0, .54)', 66 | borderRadius: '3px' 67 | }, 68 | tooltip: { 69 | padding: '10px 15px', 70 | minWidth: '130px', 71 | color: '#555555', 72 | lineHeight: '1.7em', 73 | background: '#FFFFFF', 74 | border: 'none', 75 | borderRadius: '3px', 76 | boxShadow: 77 | '0 8px 10px 1px rgba(0, 0, 0, 0.14), 0 3px 14px 2px rgba(0, 0, 0, 0.12), 0 5px 5px -3px rgba(0, 0, 0, 0.2)', 78 | maxWidth: '200px', 79 | textAlign: 'center', 80 | fontFamily: '"Helvetica Neue",Helvetica,Arial,sans-serif', 81 | fontSize: '12px', 82 | fontStyle: 'normal', 83 | fontWeight: '400', 84 | textShadow: 'none', 85 | textTransform: 'none', 86 | letterSpacing: 'normal', 87 | wordBreak: 'normal', 88 | wordSpacing: 'normal', 89 | wordWrap: 'normal', 90 | whiteSpace: 'normal', 91 | lineBreak: 'auto' 92 | } 93 | }; 94 | export default tasksStyle; 95 | -------------------------------------------------------------------------------- /src/assets/jss/material-dashboard-react/typographyStyle.jsx: -------------------------------------------------------------------------------- 1 | // ############################## 2 | // // // Typography styles 3 | // ############################# 4 | 5 | import { 6 | defaultFont, 7 | primaryColor, 8 | infoColor, 9 | successColor, 10 | warningColor, 11 | dangerColor 12 | } from '../material-dashboard-react.js'; 13 | 14 | const typographyStyle = { 15 | defaultFontStyle: { 16 | ...defaultFont, 17 | fontSize: '14px' 18 | }, 19 | defaultHeaderMargins: { 20 | marginTop: '20px', 21 | marginBottom: '10px' 22 | }, 23 | quote: { 24 | padding: '10px 20px', 25 | margin: '0 0 20px', 26 | fontSize: '17.5px', 27 | borderLeft: '5px solid #eee' 28 | }, 29 | quoteText: { 30 | margin: '0 0 10px', 31 | fontStyle: 'italic' 32 | }, 33 | quoteAuthor: { 34 | display: 'block', 35 | fontSize: '80%', 36 | lineHeight: '1.42857143', 37 | color: '#777' 38 | }, 39 | mutedText: { 40 | color: '#777' 41 | }, 42 | primaryText: { 43 | color: primaryColor[0] 44 | }, 45 | infoText: { 46 | color: infoColor[0] 47 | }, 48 | successText: { 49 | color: successColor[0] 50 | }, 51 | warningText: { 52 | color: warningColor[0] 53 | }, 54 | dangerText: { 55 | color: dangerColor[0] 56 | } 57 | }; 58 | 59 | export default typographyStyle; 60 | -------------------------------------------------------------------------------- /src/components/Card/Card.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | // nodejs library that concatenates classes 3 | import classNames from 'classnames'; 4 | // nodejs library to set properties for components 5 | import PropTypes from 'prop-types'; 6 | // @material-ui/core components 7 | import { makeStyles } from '@material-ui/core/styles'; 8 | // @material-ui/icons 9 | 10 | // core components 11 | import styles from '../../assets/jss/material-dashboard-react/components/cardStyle.js'; 12 | 13 | const useStyles = makeStyles(styles); 14 | 15 | export default function Card(props) { 16 | const classes = useStyles(); 17 | const { className, children, plain, profile, chart, ...rest } = props; 18 | const cardClasses = classNames({ 19 | [classes.card]: true, 20 | [classes.cardPlain]: plain, 21 | [classes.cardProfile]: profile, 22 | [classes.cardChart]: chart, 23 | [className]: className !== undefined 24 | }); 25 | return ( 26 |
27 | {children} 28 |
29 | ); 30 | } 31 | 32 | Card.propTypes = { 33 | className: PropTypes.string, 34 | plain: PropTypes.bool, 35 | profile: PropTypes.bool, 36 | chart: PropTypes.bool, 37 | children: PropTypes.node 38 | }; 39 | -------------------------------------------------------------------------------- /src/components/Card/CardBody.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | // nodejs library that concatenates classes 3 | import classNames from 'classnames'; 4 | // nodejs library to set properties for components 5 | import PropTypes from 'prop-types'; 6 | // @material-ui/core components 7 | import { makeStyles } from '@material-ui/core/styles'; 8 | // @material-ui/icons 9 | 10 | // core components 11 | import styles from '../../assets/jss/material-dashboard-react/components/cardBodyStyle.js'; 12 | 13 | const useStyles = makeStyles(styles); 14 | 15 | export default function CardBody(props) { 16 | const classes = useStyles(); 17 | const { className, children, plain, profile, ...rest } = props; 18 | const cardBodyClasses = classNames({ 19 | [classes.cardBody]: true, 20 | [classes.cardBodyPlain]: plain, 21 | [classes.cardBodyProfile]: profile, 22 | [className]: className !== undefined 23 | }); 24 | return ( 25 |
26 | {children} 27 |
28 | ); 29 | } 30 | 31 | CardBody.propTypes = { 32 | className: PropTypes.string, 33 | plain: PropTypes.bool, 34 | profile: PropTypes.bool, 35 | children: PropTypes.node 36 | }; 37 | -------------------------------------------------------------------------------- /src/components/Card/CardFooter.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | // nodejs library that concatenates classes 3 | import classNames from 'classnames'; 4 | // nodejs library to set properties for components 5 | import PropTypes from 'prop-types'; 6 | // @material-ui/core components 7 | import { makeStyles } from '@material-ui/core/styles'; 8 | // @material-ui/icons 9 | 10 | // core components 11 | import styles from '../../assets/jss/material-dashboard-react/components/cardFooterStyle.js'; 12 | 13 | const useStyles = makeStyles(styles); 14 | 15 | export default function CardFooter(props) { 16 | const classes = useStyles(); 17 | const { 18 | className, 19 | children, 20 | plain, 21 | profile, 22 | stats, 23 | chart, 24 | ...rest 25 | } = props; 26 | const cardFooterClasses = classNames({ 27 | [classes.cardFooter]: true, 28 | [classes.cardFooterPlain]: plain, 29 | [classes.cardFooterProfile]: profile, 30 | [classes.cardFooterStats]: stats, 31 | [classes.cardFooterChart]: chart, 32 | [className]: className !== undefined 33 | }); 34 | return ( 35 |
36 | {children} 37 |
38 | ); 39 | } 40 | 41 | CardFooter.propTypes = { 42 | className: PropTypes.string, 43 | plain: PropTypes.bool, 44 | profile: PropTypes.bool, 45 | stats: PropTypes.bool, 46 | chart: PropTypes.bool, 47 | children: PropTypes.node 48 | }; 49 | -------------------------------------------------------------------------------- /src/components/Card/CardHeader.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | // nodejs library that concatenates classes 3 | import classNames from 'classnames'; 4 | // nodejs library to set properties for components 5 | import PropTypes from 'prop-types'; 6 | // @material-ui/core components 7 | import { makeStyles } from '@material-ui/core/styles'; 8 | // @material-ui/icons 9 | 10 | // core components 11 | import styles from '../../assets/jss/material-dashboard-react/components/cardHeaderStyle.js'; 12 | 13 | const useStyles = makeStyles(styles); 14 | 15 | export default function CardHeader(props) { 16 | const classes = useStyles(); 17 | const { className, children, color, plain, stats, icon, ...rest } = props; 18 | const cardHeaderClasses = classNames({ 19 | [classes.cardHeader]: true, 20 | [classes[color + 'CardHeader']]: color, 21 | [classes.cardHeaderPlain]: plain, 22 | [classes.cardHeaderStats]: stats, 23 | [classes.cardHeaderIcon]: icon, 24 | [className]: className !== undefined 25 | }); 26 | return ( 27 |
28 | {children} 29 |
30 | ); 31 | } 32 | 33 | CardHeader.propTypes = { 34 | className: PropTypes.string, 35 | color: PropTypes.oneOf([ 36 | 'warning', 37 | 'success', 38 | 'danger', 39 | 'info', 40 | 'primary', 41 | 'rose' 42 | ]), 43 | plain: PropTypes.bool, 44 | stats: PropTypes.bool, 45 | icon: PropTypes.bool, 46 | children: PropTypes.node 47 | }; 48 | -------------------------------------------------------------------------------- /src/components/Card/CardIcon.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | // nodejs library that concatenates classes 3 | import classNames from 'classnames'; 4 | // nodejs library to set properties for components 5 | import PropTypes from 'prop-types'; 6 | // @material-ui/core components 7 | import { makeStyles } from '@material-ui/core/styles'; 8 | // @material-ui/icons 9 | 10 | // core components 11 | import styles from '../../assets/jss/material-dashboard-react/components/cardIconStyle.js'; 12 | 13 | const useStyles = makeStyles(styles); 14 | 15 | export default function CardIcon(props) { 16 | const classes = useStyles(); 17 | const { className, children, color, ...rest } = props; 18 | const cardIconClasses = classNames({ 19 | [classes.cardIcon]: true, 20 | [classes[color + 'CardHeader']]: color, 21 | [className]: className !== undefined 22 | }); 23 | return ( 24 |
25 | {children} 26 |
27 | ); 28 | } 29 | 30 | CardIcon.propTypes = { 31 | className: PropTypes.string, 32 | color: PropTypes.oneOf([ 33 | 'warning', 34 | 'success', 35 | 'danger', 36 | 'info', 37 | 'primary', 38 | 'rose' 39 | ]), 40 | children: PropTypes.node 41 | }; 42 | -------------------------------------------------------------------------------- /src/components/Card/ChartCard.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { 3 | withStyles, 4 | Card, 5 | CardContent, 6 | CardHeader, 7 | CardActions, 8 | Typography, 9 | } from '@material-ui/core'; 10 | import PropTypes from 'prop-types'; 11 | import FontAwesome from 'react-fontawesome'; 12 | 13 | import chartCardStyle from '../../assets/jss/material-dashboard-react/chartCardStyle'; 14 | 15 | const ChartCard = ({ 16 | classes, 17 | chartColor, 18 | statIconColor, 19 | chart, 20 | title, 21 | text, 22 | statLink, 23 | statText, 24 | fontAwesomeStatsIcon, 25 | statIcon: StatIcon, 26 | }) => { 27 | return ( 28 | 29 | 38 | 39 | 44 | {title} 45 | 46 | 47 | {text} 48 | 49 | 50 | 51 |
52 | {fontAwesomeStatsIcon ? ( 53 | 54 | ) : ( 55 | 62 | )} 63 | {statLink ? ( 64 | 68 | {statLink.text} 69 | 70 | ) : statText ? ( 71 | statText 72 | ) : null} 73 |
74 |
75 |
76 | ); 77 | }; 78 | 79 | ChartCard.defaultProps = { 80 | statIconColor: 'gray', 81 | chartColor: 'purple', 82 | }; 83 | 84 | ChartCard.propTypes = { 85 | classes: PropTypes.object.isRequired, 86 | chart: PropTypes.object.isRequired, 87 | title: PropTypes.node, 88 | text: PropTypes.node, 89 | statIcon: PropTypes.elementType, 90 | fontAwesomeStatsIcon: PropTypes.node, 91 | statIconColor: PropTypes.oneOf([ 92 | 'warning', 93 | 'primary', 94 | 'danger', 95 | 'success', 96 | 'info', 97 | 'rose', 98 | 'gray', 99 | ]), 100 | chartColor: PropTypes.oneOf(['orange', 'green', 'red', 'blue', 'purple']), 101 | statLink: PropTypes.object, 102 | statText: PropTypes.node, 103 | }; 104 | 105 | export default withStyles(chartCardStyle)(ChartCard); 106 | -------------------------------------------------------------------------------- /src/components/Card/FileserverCard.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { useTranslation } from 'react-i18next'; 3 | import PropTypes from 'prop-types'; 4 | import { Domain } from '@material-ui/icons'; 5 | 6 | import CustomTabs from '../../components/CustomTabs/CustomTabs.js'; 7 | import { CustomMaterialTable } from '../../components'; 8 | 9 | const FileserverCard = ({ headerColor, fileserver }) => { 10 | const { t } = useTranslation(); 11 | 12 | return ( 13 | 39 | ), 40 | }, 41 | ]} 42 | /> 43 | ); 44 | }; 45 | 46 | FileserverCard.defaultProps = { 47 | headerColor: 'primary', 48 | }; 49 | 50 | FileserverCard.propTypes = { 51 | headerColor: PropTypes.oneOf([ 52 | 'warning', 53 | 'success', 54 | 'danger', 55 | 'info', 56 | 'primary', 57 | 'rose', 58 | ]), 59 | fileserver: PropTypes.array, 60 | }; 61 | 62 | export default FileserverCard; 63 | -------------------------------------------------------------------------------- /src/components/Card/HealthcheckCard.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import { useTranslation } from 'react-i18next'; 4 | 5 | import { Warning, Done, Update, Favorite } from '@material-ui/icons'; 6 | 7 | import StatsCard from './StatsCard'; 8 | 9 | const HealthcheckCard = (props) => { 10 | const { t } = useTranslation(); 11 | const { healthcheck, title, sub_title_success, sub_title_error } = props; 12 | 13 | const sub_title = healthcheck ? sub_title_success : sub_title_error; 14 | 15 | if (typeof healthcheck === 'undefined') { 16 | return ( 17 | 26 | ); 27 | } else if (!healthcheck) { 28 | return ( 29 | 38 | ); 39 | } else { 40 | return ( 41 | 50 | ); 51 | } 52 | }; 53 | 54 | HealthcheckCard.defaultProps = { 55 | iconColor: 'purple', 56 | statIconColor: 'gray', 57 | }; 58 | 59 | HealthcheckCard.propTypes = { 60 | title: PropTypes.node, 61 | sub_title_success: PropTypes.node, 62 | sub_title_error: PropTypes.node, 63 | }; 64 | 65 | export default HealthcheckCard; 66 | -------------------------------------------------------------------------------- /src/components/Card/LicenseCard.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | 4 | import { Warning, Done, Update, Accessibility } from '@material-ui/icons'; 5 | import StatsCard from './StatsCard'; 6 | 7 | const LicenseCard = ({ active, licensed, total }) => { 8 | let ratio = `${active}/${total}`; 9 | let ratio_text = 'Active / Total'; 10 | 11 | if (licensed) { 12 | ratio = `${ratio}/${licensed}`; 13 | ratio_text += ' / Licensed'; 14 | } 15 | 16 | if (active === '') { 17 | return ( 18 | 27 | ); 28 | } else if (licensed && active === licensed) { 29 | return ( 30 | 39 | ); 40 | } else { 41 | return ( 42 | 51 | ); 52 | } 53 | }; 54 | 55 | LicenseCard.defaultProps = { 56 | iconColor: 'purple', 57 | statIconColor: 'gray', 58 | }; 59 | 60 | LicenseCard.propTypes = { 61 | active: PropTypes.node, 62 | total: PropTypes.node, 63 | licensed: PropTypes.node, 64 | valid_from: PropTypes.node, 65 | valid_till: PropTypes.node, 66 | }; 67 | 68 | export default LicenseCard; 69 | -------------------------------------------------------------------------------- /src/components/Card/OIDCCard.jsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react'; 2 | import { useTranslation } from 'react-i18next'; 3 | import PropTypes from 'prop-types'; 4 | 5 | import { withStyles } from '@material-ui/core'; 6 | import { Group } from '@material-ui/icons'; 7 | import Delete from '@material-ui/icons/Delete'; 8 | 9 | import CustomTabs from '../../components/CustomTabs/CustomTabs.js'; 10 | import { CustomMaterialTable } from '../../components'; 11 | 12 | import tasksCardStyle from '../../assets/jss/material-dashboard-react/tasksCardStyle'; 13 | import DeleteConfirmDialog from '../Dialog/DeleteConfirmDialog'; 14 | 15 | const OIDCCard = ({ oidc_groups, onDeleteOidcGroups }) => { 16 | const { t } = useTranslation(); 17 | const [deleteOidcGroups, setDeleteOidcGroups] = useState([]); 18 | 19 | return ( 20 | <> 21 | {deleteOidcGroups.length > 0 && ( 22 | { 25 | onDeleteOidcGroups(deleteOidcGroups); 26 | setDeleteOidcGroups([]); 27 | }} 28 | onAbort={() => { 29 | setDeleteOidcGroups([]); 30 | }} 31 | > 32 | {t('DELETE_OIDC_GROUP_CONFIRM_DIALOG')} 33 | 34 | )} 35 | { 65 | setDeleteOidcGroups([data]); 66 | }, 67 | }, 68 | ]} 69 | options={{ 70 | pageSize: 10, 71 | }} 72 | /> 73 | ), 74 | }, 75 | ]} 76 | /> 77 | 78 | ); 79 | }; 80 | 81 | OIDCCard.propTypes = { 82 | oidc_groups: PropTypes.array, 83 | onDeleteOidcGroups: PropTypes.func.isRequired, 84 | }; 85 | 86 | export default OIDCCard; 87 | -------------------------------------------------------------------------------- /src/components/Card/ProfileCard.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { 3 | withStyles, 4 | Card, 5 | CardHeader, 6 | CardContent, 7 | CardActions, 8 | Typography, 9 | } from '@material-ui/core'; 10 | import PropTypes from 'prop-types'; 11 | 12 | import profileCardStyle from '../../assets/jss/material-dashboard-react/profileCardStyle'; 13 | 14 | const ProfileCard = ({ 15 | classes, 16 | subtitle, 17 | title, 18 | description, 19 | footer, 20 | avatar, 21 | }) => { 22 | return ( 23 | 24 | } 30 | /> 31 | 32 | {subtitle && ( 33 | 34 | {subtitle} 35 | 36 | )} 37 | {title && ( 38 | 39 | {title} 40 | 41 | )} 42 | {description && ( 43 | 47 | {description} 48 | 49 | )} 50 | 51 | 54 | {footer} 55 | 56 | 57 | ); 58 | }; 59 | 60 | ProfileCard.propTypes = { 61 | classes: PropTypes.object.isRequired, 62 | title: PropTypes.node, 63 | subtitle: PropTypes.node, 64 | description: PropTypes.node, 65 | footer: PropTypes.node, 66 | avatar: PropTypes.string, 67 | }; 68 | 69 | export default withStyles(profileCardStyle)(ProfileCard); 70 | -------------------------------------------------------------------------------- /src/components/Card/RegularCard.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import classNames from 'classnames'; 3 | import PropTypes from 'prop-types'; 4 | // @material-ui/core components 5 | import withStyles from '@material-ui/core/styles/withStyles'; 6 | import Card from '@material-ui/core/Card'; 7 | import CardContent from '@material-ui/core/CardContent'; 8 | import CardHeader from '@material-ui/core/CardHeader'; 9 | import CardActions from '@material-ui/core/CardActions'; 10 | // core components 11 | import regularCardStyle from '../../assets/jss/material-dashboard-react/regularCardStyle'; 12 | 13 | function RegularCard({ ...props }) { 14 | const { 15 | classes, 16 | headerColor, 17 | plainCard, 18 | cardTitle, 19 | cardSubtitle, 20 | content, 21 | footer 22 | } = props; 23 | const plainCardClasses = classNames({ 24 | [' ' + classes.cardPlain]: plainCard 25 | }); 26 | const cardPlainHeaderClasses = classNames({ 27 | [' ' + classes.cardPlainHeader]: plainCard 28 | }); 29 | return ( 30 | 31 | 44 | {content} 45 | {footer !== undefined ? ( 46 | 47 | {footer} 48 | 49 | ) : null} 50 | 51 | ); 52 | } 53 | 54 | RegularCard.defaultProps = { 55 | headerColor: 'purple' 56 | }; 57 | 58 | RegularCard.propTypes = { 59 | plainCard: PropTypes.bool, 60 | classes: PropTypes.object.isRequired, 61 | headerColor: PropTypes.oneOf(['orange', 'green', 'red', 'blue', 'purple']), 62 | cardTitle: PropTypes.node, 63 | cardSubtitle: PropTypes.node, 64 | content: PropTypes.node, 65 | footer: PropTypes.node 66 | }; 67 | 68 | export default withStyles(regularCardStyle)(RegularCard); 69 | -------------------------------------------------------------------------------- /src/components/Card/SCIMCard.jsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react'; 2 | import { useTranslation } from 'react-i18next'; 3 | import PropTypes from 'prop-types'; 4 | 5 | import { withStyles } from '@material-ui/core'; 6 | import { Group } from '@material-ui/icons'; 7 | import Delete from '@material-ui/icons/Delete'; 8 | 9 | import CustomTabs from '../../components/CustomTabs/CustomTabs.js'; 10 | import { CustomMaterialTable } from '../../components'; 11 | 12 | import tasksCardStyle from '../../assets/jss/material-dashboard-react/tasksCardStyle'; 13 | import DeleteConfirmDialog from '../Dialog/DeleteConfirmDialog'; 14 | 15 | const SCIMCard = ({ scim_groups, onDeleteScimGroups }) => { 16 | const { t } = useTranslation(); 17 | const [deleteScimGroups, setDeleteScimGroups] = useState([]); 18 | 19 | return ( 20 | <> 21 | {deleteScimGroups.length > 0 && ( 22 | { 25 | onDeleteScimGroups(deleteScimGroups); 26 | setDeleteScimGroups([]); 27 | }} 28 | onAbort={() => { 29 | setDeleteScimGroups([]); 30 | }} 31 | > 32 | {t('DELETE_SCIM_GROUP_CONFIRM_DIALOG')} 33 | 34 | )} 35 | 44 | { 63 | setDeleteScimGroups([data]); 64 | }, 65 | }, 66 | ]} 67 | options={{ 68 | pageSize: 10, 69 | }} 70 | /> 71 | 72 | ), 73 | }, 74 | ]} 75 | /> 76 | 77 | ); 78 | }; 79 | 80 | SCIMCard.propTypes = { 81 | scim_groups: PropTypes.array, 82 | onDeleteScimGroups: PropTypes.func.isRequired, 83 | }; 84 | 85 | export default SCIMCard; 86 | -------------------------------------------------------------------------------- /src/components/Card/Sessions.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import { Update, Accessibility } from '@material-ui/icons'; 4 | import StatsCard from './StatsCard'; 5 | 6 | const Sessions = ({ users, devices, total }) => { 7 | if (users === '' || devices === '' || total === '') { 8 | return ( 9 | 18 | ); 19 | } else { 20 | return ( 21 | 29 | ); 30 | } 31 | }; 32 | 33 | Sessions.defaultProps = { 34 | iconColor: 'purple', 35 | statIconColor: 'gray', 36 | }; 37 | 38 | Sessions.propTypes = { 39 | users: PropTypes.node, 40 | devices: PropTypes.node, 41 | total: PropTypes.node, 42 | }; 43 | 44 | export default Sessions; 45 | -------------------------------------------------------------------------------- /src/components/Card/VersionCard.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import { Trans, useTranslation } from 'react-i18next'; 4 | 5 | import { 6 | InfoOutlined, 7 | Warning, 8 | Done, 9 | Update, 10 | ThumbUp, 11 | } from '@material-ui/icons'; 12 | 13 | import StatsCard from './StatsCard'; 14 | 15 | function VersionCard(props) { 16 | const { t } = useTranslation(); 17 | const { used_version, latest_version, title } = props; 18 | if (!used_version || !latest_version) { 19 | return ( 20 | 29 | ); 30 | } else if (used_version === latest_version) { 31 | return ( 32 | 40 | ); 41 | } else { 42 | return ( 43 | 55 | Version {{ latest_version }} available 56 | 57 | } 58 | /> 59 | ); 60 | } 61 | } 62 | 63 | VersionCard.defaultProps = { 64 | iconColor: 'purple', 65 | statIconColor: 'gray', 66 | }; 67 | 68 | VersionCard.propTypes = { 69 | title: PropTypes.node.isRequired, 70 | used_version: PropTypes.node.isRequired, 71 | latest_version: PropTypes.node.isRequired, 72 | }; 73 | 74 | export default VersionCard; 75 | -------------------------------------------------------------------------------- /src/components/CustomButtons/Button.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | // nodejs library that concatenates classes 3 | import classNames from 'classnames'; 4 | // nodejs library to set properties for components 5 | import PropTypes from 'prop-types'; 6 | 7 | // material-ui components 8 | import { makeStyles } from '@material-ui/core/styles'; 9 | import Button from '@material-ui/core/Button'; 10 | 11 | import styles from '../../assets/jss/material-dashboard-react/components/buttonStyle.js'; 12 | 13 | const useStyles = makeStyles(styles); 14 | 15 | export default function RegularButton(props) { 16 | const classes = useStyles(); 17 | const { 18 | color, 19 | round, 20 | children, 21 | disabled, 22 | simple, 23 | size, 24 | block, 25 | link, 26 | justIcon, 27 | className, 28 | muiClasses, 29 | ...rest 30 | } = props; 31 | const btnClasses = classNames({ 32 | [classes.button]: true, 33 | [classes[size]]: size, 34 | [classes[color]]: color, 35 | [classes.round]: round, 36 | [classes.disabled]: disabled, 37 | [classes.simple]: simple, 38 | [classes.block]: block, 39 | [classes.link]: link, 40 | [classes.justIcon]: justIcon, 41 | [className]: className 42 | }); 43 | return ( 44 | 47 | ); 48 | } 49 | 50 | RegularButton.propTypes = { 51 | color: PropTypes.oneOf([ 52 | 'primary', 53 | 'info', 54 | 'success', 55 | 'warning', 56 | 'danger', 57 | 'rose', 58 | 'white', 59 | 'transparent' 60 | ]), 61 | size: PropTypes.oneOf(['sm', 'lg']), 62 | simple: PropTypes.bool, 63 | round: PropTypes.bool, 64 | disabled: PropTypes.bool, 65 | block: PropTypes.bool, 66 | link: PropTypes.bool, 67 | justIcon: PropTypes.bool, 68 | className: PropTypes.string, 69 | // use this to pass the classes props from Material-UI 70 | muiClasses: PropTypes.object, 71 | children: PropTypes.node 72 | }; 73 | -------------------------------------------------------------------------------- /src/components/CustomButtons/IconButton.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { withStyles, IconButton } from '@material-ui/core'; 3 | import PropTypes from 'prop-types'; 4 | 5 | import iconButtonStyle from '../../assets/jss/material-dashboard-react/iconButtonStyle'; 6 | 7 | const IconCustomButton = ({ 8 | classes, 9 | color, 10 | children, 11 | customClass, 12 | ...rest 13 | }) => { 14 | return ( 15 | 23 | {children} 24 | 25 | ); 26 | }; 27 | 28 | IconCustomButton.propTypes = { 29 | classes: PropTypes.object.isRequired, 30 | color: PropTypes.oneOf([ 31 | 'primary', 32 | 'info', 33 | 'success', 34 | 'warning', 35 | 'danger', 36 | 'rose', 37 | 'white', 38 | 'simple', 39 | ]), 40 | customClass: PropTypes.string, 41 | disabled: PropTypes.bool, 42 | children: PropTypes.node.isRequired, 43 | }; 44 | 45 | export default withStyles(iconButtonStyle)(IconCustomButton); 46 | -------------------------------------------------------------------------------- /src/components/CustomInput/CustomInput.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { 3 | withStyles, 4 | FormControl, 5 | InputLabel, 6 | Input, 7 | FormHelperText, 8 | } from '@material-ui/core'; 9 | import { Clear, Check } from '@material-ui/icons'; 10 | import PropTypes from 'prop-types'; 11 | 12 | import customInputStyle from '../../assets/jss/material-dashboard-react/customInputStyle'; 13 | 14 | const CustomInput = ({ 15 | classes, 16 | formControlProps, 17 | helperText, 18 | labelText, 19 | id, 20 | labelProps, 21 | inputProps, 22 | error, 23 | success, 24 | }) => { 25 | return ( 26 | 31 | {labelText !== undefined ? ( 32 | 45 | {labelText} 46 | 47 | ) : null} 48 | 62 | {helperText && {helperText}} 63 | {error ? ( 64 | 67 | ) : success ? ( 68 | 71 | ) : null} 72 | 73 | ); 74 | }; 75 | 76 | CustomInput.propTypes = { 77 | classes: PropTypes.object.isRequired, 78 | helperText: PropTypes.node, 79 | labelText: PropTypes.node, 80 | labelProps: PropTypes.object, 81 | id: PropTypes.string, 82 | inputProps: PropTypes.object, 83 | formControlProps: PropTypes.object, 84 | error: PropTypes.bool, 85 | success: PropTypes.bool, 86 | }; 87 | 88 | export default withStyles(customInputStyle)(CustomInput); 89 | -------------------------------------------------------------------------------- /src/components/Dialog/DeleteConfirmDialog.js: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react'; 2 | import { useTranslation } from 'react-i18next'; 3 | import Button from '@material-ui/core/Button'; 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 DialogContentText from '@material-ui/core/DialogContentText'; 8 | import DialogTitle from '@material-ui/core/DialogTitle'; 9 | import PropTypes from 'prop-types'; 10 | 11 | const DeleteConfirmDialog = ({ title, children, onConfirm, onAbort }) => { 12 | const { t } = useTranslation(); 13 | const [open, setOpen] = useState(true); 14 | 15 | const handleAbort = () => { 16 | setOpen(false); 17 | onAbort(); 18 | }; 19 | 20 | const handleConfirm = () => { 21 | setOpen(false); 22 | onConfirm(); 23 | }; 24 | 25 | return ( 26 | 32 | {title} 33 | 34 | 35 | {children} 36 | 37 | 38 | 39 | 42 | 45 | 46 | 47 | ); 48 | }; 49 | 50 | DeleteConfirmDialog.propTypes = { 51 | title: PropTypes.string, 52 | onConfirm: PropTypes.func.isRequired, 53 | onAbort: PropTypes.func.isRequired, 54 | children: PropTypes.node, 55 | }; 56 | 57 | export default DeleteConfirmDialog; 58 | -------------------------------------------------------------------------------- /src/components/Footer/Footer.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import { List, ListItem, withStyles } from '@material-ui/core'; 4 | 5 | import footerStyle from '../../assets/jss/material-dashboard-react/footerStyle'; 6 | 7 | const Footer = ({ classes }) => { 8 | return ( 9 | 50 | ); 51 | }; 52 | 53 | Footer.propTypes = { 54 | classes: PropTypes.object.isRequired, 55 | }; 56 | 57 | export default withStyles(footerStyle)(Footer); 58 | -------------------------------------------------------------------------------- /src/components/Grid/GridItem.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { withStyles, Grid } from '@material-ui/core'; 3 | 4 | const style = { 5 | grid: { 6 | padding: '0 15px !important', 7 | }, 8 | }; 9 | 10 | const GridItem = ({ classes, children, ...rest }) => { 11 | return ( 12 | 13 | {children} 14 | 15 | ); 16 | }; 17 | 18 | export default withStyles(style)(GridItem); 19 | -------------------------------------------------------------------------------- /src/components/Header/Header.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { matchPath, useLocation } from 'react-router-dom'; 3 | import PropTypes from 'prop-types'; 4 | import { Menu } from '@material-ui/icons'; 5 | import { 6 | withStyles, 7 | AppBar, 8 | Toolbar, 9 | IconButton, 10 | Hidden, 11 | Button, 12 | } from '@material-ui/core'; 13 | import { withTranslation } from 'react-i18next'; 14 | import { compose } from 'redux'; 15 | 16 | import headerStyle from '../../assets/jss/material-dashboard-react/headerStyle'; 17 | import user from '../../services/user'; 18 | 19 | import HeaderLinks from './HeaderLinks'; 20 | 21 | const Header = (props) => { 22 | let location = useLocation(); 23 | const makeBrand = () => { 24 | for (let i = 0; i < props.routes.length; i++) { 25 | if (matchPath(location.pathname, props.routes[i].path) !== null) { 26 | return props.t(props.routes[i].navbarName); 27 | } 28 | } 29 | return null; 30 | }; 31 | 32 | const { classes, color, ...rest } = props; 33 | 34 | return ( 35 | 41 | 42 |
43 | {/* Here we create navbar brand, based on route name */} 44 | 47 |
48 | 49 | 50 | 51 | 52 | 58 | 59 | 60 | 61 | 62 | 63 | ); 64 | }; 65 | 66 | Header.propTypes = { 67 | classes: PropTypes.object.isRequired, 68 | theme: PropTypes.object.isRequired, 69 | color: PropTypes.oneOf(['primary', 'info', 'success', 'warning', 'danger']), 70 | }; 71 | 72 | export default compose( 73 | withTranslation(), 74 | withStyles(headerStyle, { withTheme: true }) 75 | )(Header); 76 | -------------------------------------------------------------------------------- /src/components/Notification/Notification.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import AddAlert from '@material-ui/icons/AddAlert'; 3 | import { useSelector } from 'react-redux'; 4 | 5 | import notification from '../../services/notification'; 6 | 7 | import { Snackbar } from '../index'; 8 | import { useTranslation } from 'react-i18next'; 9 | 10 | export default function Notification(props) { 11 | const { t } = useTranslation(); 12 | 13 | const messages = useSelector(state => state.notification.messages); 14 | 15 | return messages.map((message, index) => { 16 | let icon; 17 | let color; 18 | if (message.type === 'info') { 19 | icon = AddAlert; 20 | color = 'info'; 21 | } else if (message.type === 'danger') { 22 | icon = AddAlert; 23 | color = 'danger'; 24 | } else { 25 | console.log(message); 26 | // TODO add other notification types 27 | } 28 | 29 | return ( 30 | { 38 | const new_messages = messages; 39 | new_messages.splice(index, 1); 40 | notification.set(new_messages); 41 | }} 42 | close 43 | /> 44 | ); 45 | }, this); 46 | } 47 | -------------------------------------------------------------------------------- /src/components/Snackbar/Snackbar.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { withStyles, Snackbar as Snack, IconButton } from '@material-ui/core'; 3 | import { Close } from '@material-ui/icons'; 4 | import PropTypes from 'prop-types'; 5 | 6 | import snackbarContentStyle from '../../assets/jss/material-dashboard-react/snackbarContentStyle'; 7 | 8 | const Snackbar = ({ 9 | classes, 10 | message, 11 | color, 12 | close, 13 | icon: Icon, 14 | place, 15 | open, 16 | closeNotification, 17 | }) => { 18 | const action = close 19 | ? [ 20 | 27 | 28 | , 29 | ] 30 | : []; 31 | 32 | return ( 33 | 46 | {Icon ? : null} 47 | 48 | {message} 49 | 50 | 51 | } 52 | action={action} 53 | ContentProps={{ 54 | classes: { 55 | root: `${classes.root} ${classes[color]}`, 56 | message: classes.message, 57 | }, 58 | }} 59 | /> 60 | ); 61 | }; 62 | 63 | Snackbar.propTypes = { 64 | classes: PropTypes.object.isRequired, 65 | message: PropTypes.node.isRequired, 66 | color: PropTypes.oneOf([ 67 | 'warning', 68 | 'success', 69 | 'danger', 70 | 'info', 71 | 'primary', 72 | 'rose', 73 | ]), 74 | close: PropTypes.bool, 75 | icon: PropTypes.elementType, 76 | place: PropTypes.oneOf(['tl', 'tr', 'tc', 'br', 'bl', 'bc']), 77 | open: PropTypes.bool, 78 | closeNotification: PropTypes.func, 79 | }; 80 | 81 | export default withStyles(snackbarContentStyle)(Snackbar); 82 | -------------------------------------------------------------------------------- /src/components/Snackbar/SnackbarContent.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { 3 | withStyles, 4 | SnackbarContent as Snack, 5 | IconButton, 6 | } from '@material-ui/core'; 7 | import { Close } from '@material-ui/icons'; 8 | import PropTypes from 'prop-types'; 9 | 10 | import snackbarContentStyle from '../../assets/jss/material-dashboard-react/snackbarContentStyle'; 11 | 12 | const SnackbarContent = ({ classes, message, color, close, icon: Icon }) => { 13 | const action = []; 14 | if (close !== undefined) { 15 | action.push( 16 | 22 | 23 | 24 | ); 25 | } 26 | 27 | return ( 28 | 31 | {Icon !== undefined ? ( 32 | 33 | ) : null} 34 | 39 | {message} 40 | 41 | 42 | } 43 | classes={{ 44 | root: `${classes.root} ${classes[color]}`, 45 | message: classes.message, 46 | }} 47 | action={action} 48 | /> 49 | ); 50 | }; 51 | 52 | SnackbarContent.propTypes = { 53 | classes: PropTypes.object.isRequired, 54 | message: PropTypes.node.isRequired, 55 | color: PropTypes.oneOf(['info', 'success', 'warning', 'danger', 'primary']), 56 | close: PropTypes.bool, 57 | icon: PropTypes.elementType, 58 | }; 59 | 60 | export default withStyles(snackbarContentStyle)(SnackbarContent); 61 | -------------------------------------------------------------------------------- /src/components/Table/Table.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { 3 | withStyles, 4 | Table, 5 | TableHead, 6 | TableRow, 7 | TableBody, 8 | TableCell, 9 | } from '@material-ui/core'; 10 | import PropTypes from 'prop-types'; 11 | 12 | import tableStyle from '../../assets/jss/material-dashboard-react/tableStyle'; 13 | 14 | const CustomTable = ({ classes, tableHead, tableData, tableHeaderColor }) => { 15 | return ( 16 |
17 | 18 | {tableHead !== undefined ? ( 19 | 22 | 23 | {tableHead.map((prop, key) => ( 24 | 28 | {prop} 29 | 30 | ))} 31 | 32 | 33 | ) : null} 34 | 35 | {tableData.map((row, key) => ( 36 | 37 | {row.map((cell, cellKey) => ( 38 | 42 | {cell} 43 | 44 | ))} 45 | 46 | ))} 47 | 48 |
49 |
50 | ); 51 | }; 52 | 53 | CustomTable.defaultProps = { 54 | tableHeaderColor: 'gray', 55 | }; 56 | 57 | CustomTable.propTypes = { 58 | classes: PropTypes.object.isRequired, 59 | tableHeaderColor: PropTypes.oneOf([ 60 | 'warning', 61 | 'primary', 62 | 'danger', 63 | 'success', 64 | 'info', 65 | 'rose', 66 | 'gray', 67 | ]), 68 | tableHead: PropTypes.arrayOf(PropTypes.string), 69 | tableData: PropTypes.arrayOf(PropTypes.arrayOf(PropTypes.string)), 70 | }; 71 | 72 | export default withStyles(tableStyle)(CustomTable); 73 | -------------------------------------------------------------------------------- /src/components/Typography/A.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { withStyles } from '@material-ui/core'; 3 | import PropTypes from 'prop-types'; 4 | 5 | import typographyStyle from '../../assets/jss/material-dashboard-react/typographyStyle'; 6 | 7 | const A = ({ classes, children, ...rest }) => { 8 | return ( 9 | 13 | {children} 14 | 15 | ); 16 | }; 17 | 18 | A.propTypes = { 19 | classes: PropTypes.object.isRequired, 20 | children: PropTypes.node.isRequired, 21 | }; 22 | 23 | export default withStyles(typographyStyle)(A); 24 | -------------------------------------------------------------------------------- /src/components/Typography/Danger.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import { withStyles } from '@material-ui/core'; 4 | 5 | import typographyStyle from '../../assets/jss/material-dashboard-react/typographyStyle'; 6 | 7 | const Danger = ({ classes, children }) => { 8 | return ( 9 |
10 | {children} 11 |
12 | ); 13 | }; 14 | 15 | Danger.propTypes = { 16 | classes: PropTypes.object.isRequired, 17 | children: PropTypes.node.isRequired, 18 | }; 19 | 20 | export default withStyles(typographyStyle)(Danger); 21 | -------------------------------------------------------------------------------- /src/components/Typography/Info.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import { withStyles } from '@material-ui/core'; 4 | 5 | import typographyStyle from '../../assets/jss/material-dashboard-react/typographyStyle'; 6 | 7 | const Info = ({ classes, children }) => { 8 | return ( 9 |
10 | {children} 11 |
12 | ); 13 | }; 14 | 15 | Info.propTypes = { 16 | classes: PropTypes.object.isRequired, 17 | children: PropTypes.node.isRequired, 18 | }; 19 | 20 | export default withStyles(typographyStyle)(Info); 21 | -------------------------------------------------------------------------------- /src/components/Typography/Muted.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import { withStyles } from '@material-ui/core'; 4 | 5 | import typographyStyle from '../../assets/jss/material-dashboard-react/typographyStyle'; 6 | 7 | const Muted = ({ classes, children }) => { 8 | return ( 9 |
10 | {children} 11 |
12 | ); 13 | }; 14 | 15 | Muted.propTypes = { 16 | classes: PropTypes.object.isRequired, 17 | children: PropTypes.node.isRequired, 18 | }; 19 | 20 | export default withStyles(typographyStyle)(Muted); 21 | -------------------------------------------------------------------------------- /src/components/Typography/P.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { withStyles } from '@material-ui/core'; 3 | import PropTypes from 'prop-types'; 4 | 5 | import typographyStyle from '../../assets/jss/material-dashboard-react/typographyStyle'; 6 | 7 | const P = ({ classes, children }) => { 8 | return ( 9 |

10 | {children} 11 |

12 | ); 13 | }; 14 | 15 | P.propTypes = { 16 | classes: PropTypes.object.isRequired, 17 | children: PropTypes.node.isRequired, 18 | }; 19 | 20 | export default withStyles(typographyStyle)(P); 21 | -------------------------------------------------------------------------------- /src/components/Typography/Primary.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import { withStyles } from '@material-ui/core'; 4 | 5 | import typographyStyle from '../../assets/jss/material-dashboard-react/typographyStyle'; 6 | 7 | const Primary = ({ classes, children }) => { 8 | return ( 9 |
10 | {children} 11 |
12 | ); 13 | }; 14 | 15 | Primary.propTypes = { 16 | classes: PropTypes.object.isRequired, 17 | children: PropTypes.node.isRequired, 18 | }; 19 | 20 | export default withStyles(typographyStyle)(Primary); 21 | -------------------------------------------------------------------------------- /src/components/Typography/Quote.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { withStyles } from '@material-ui/core'; 3 | import PropTypes from 'prop-types'; 4 | 5 | import typographyStyle from '../../assets/jss/material-dashboard-react/typographyStyle'; 6 | 7 | const Quote = ({ classes, text, author }) => { 8 | return ( 9 |
10 |

{text}

11 | {author} 12 |
13 | ); 14 | }; 15 | 16 | Quote.propTypes = { 17 | classes: PropTypes.object.isRequired, 18 | text: PropTypes.node, 19 | author: PropTypes.node, 20 | }; 21 | 22 | export default withStyles(typographyStyle)(Quote); 23 | -------------------------------------------------------------------------------- /src/components/Typography/Small.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import { withStyles } from '@material-ui/core'; 4 | 5 | import typographyStyle from '../../assets/jss/material-dashboard-react/typographyStyle'; 6 | 7 | const Small = ({ classes, children }) => { 8 | return ( 9 |
10 | {children} 11 |
12 | ); 13 | }; 14 | 15 | Small.propTypes = { 16 | classes: PropTypes.object.isRequired, 17 | children: PropTypes.node.isRequired, 18 | }; 19 | 20 | export default withStyles(typographyStyle)(Small); 21 | -------------------------------------------------------------------------------- /src/components/Typography/Success.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import { withStyles } from '@material-ui/core'; 4 | 5 | import typographyStyle from '../../assets/jss/material-dashboard-react/typographyStyle'; 6 | 7 | const Success = ({ classes, children }) => { 8 | return ( 9 |
10 | {children} 11 |
12 | ); 13 | }; 14 | 15 | Success.propTypes = { 16 | classes: PropTypes.object.isRequired, 17 | children: PropTypes.node.isRequired, 18 | }; 19 | 20 | export default withStyles(typographyStyle)(Success); 21 | -------------------------------------------------------------------------------- /src/components/Typography/Warning.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import { withStyles } from '@material-ui/core'; 4 | 5 | import typographyStyle from '../../assets/jss/material-dashboard-react/typographyStyle'; 6 | 7 | const Warning = ({ classes, children }) => { 8 | return ( 9 |
10 | {children} 11 |
12 | ); 13 | }; 14 | 15 | Warning.propTypes = { 16 | classes: PropTypes.object.isRequired, 17 | children: PropTypes.node.isRequired, 18 | }; 19 | 20 | export default withStyles(typographyStyle)(Warning); 21 | -------------------------------------------------------------------------------- /src/containers/App.js: -------------------------------------------------------------------------------- 1 | import React, { Suspense } from 'react'; 2 | import { persistStore } from 'redux-persist'; 3 | import { PersistGate } from 'redux-persist/integration/react'; 4 | import { HashLoader } from 'react-spinners'; 5 | import { createBrowserHistory } from 'history'; 6 | import { withStyles } from '@material-ui/core'; 7 | import { BrowserRouter as Router, Route, Switch } from 'react-router-dom'; 8 | 9 | import store from '../services/store'; 10 | 11 | import 'chartist/dist/chartist.min.css'; 12 | import 'chartist/dist/chartist.min.js'; 13 | import 'chartist-plugin-axistitle/dist/chartist-plugin-axistitle.min.js'; 14 | import 'font-awesome/css/font-awesome.min.css'; 15 | 16 | import 'clientjs/dist/client.min.js'; 17 | import '../assets/css/material-dashboard-react.css'; 18 | import Index from '../containers/Index/Index'; 19 | import Login from '../containers/Login/Login'; 20 | import logo from '../assets/img/logo.png'; 21 | import image from '../assets/img/background.jpg'; 22 | 23 | const hist = createBrowserHistory({ 24 | basename: '/portal', 25 | }); 26 | 27 | let persistor = persistStore(store); 28 | 29 | const style = { 30 | wrapper: { 31 | position: 'relative', 32 | top: '0', 33 | height: '100vh', 34 | backgroundImage: `url(${image})`, 35 | backgroundSize: 'cover', 36 | display: 'flex', 37 | justifyContent: 'center', 38 | alignItems: 'center', 39 | }, 40 | content: { 41 | width: '100%', 42 | height: '100%', 43 | zIndex: '3', 44 | content: '', 45 | opacity: '.8', 46 | position: 'absolute', 47 | background: '#000', 48 | }, 49 | }; 50 | 51 | const Loader = ({ classes }) => ( 52 |
53 |
54 | logo 55 |
loading...
56 |
57 | ); 58 | 59 | const StyledLoader = withStyles(style)(Loader); 60 | 61 | const App = () => ( 62 | }> 63 | } persistor={persistor}> 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | ); 83 | 84 | export default App; 85 | -------------------------------------------------------------------------------- /src/containers/Index/SwitchRoutes.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { 3 | Switch, 4 | Route, 5 | Redirect, 6 | matchPath, 7 | useLocation, 8 | } from 'react-router-dom'; 9 | import sidebarRoutes from '../../routes/sidebar'; 10 | import eeRoutes from '../../routes/ee'; 11 | import ldapRoutes from '../../routes/ldap'; 12 | import samlRoutes from '../../routes/saml'; 13 | import oidcRoutes from '../../routes/oidc'; 14 | import otherRoutes from '../../routes/other'; 15 | 16 | const SwitchRoutes = (props) => { 17 | let location = useLocation(); 18 | const { actions, state, store, ...rest } = props; 19 | 20 | let variableLinks = []; 21 | if (state.server.type === 'EE') { 22 | eeRoutes.forEach(function (route) { 23 | variableLinks.push(route); 24 | }); 25 | } 26 | if ( 27 | state.server.type === 'EE' && 28 | state.server.authentication_methods.indexOf('LDAP') !== -1 29 | ) { 30 | ldapRoutes.forEach(function (route) { 31 | variableLinks.push(route); 32 | }); 33 | } 34 | if ( 35 | state.server.type === 'EE' && 36 | state.server.authentication_methods.indexOf('SAML') !== -1 37 | ) { 38 | samlRoutes.forEach(function (route) { 39 | variableLinks.push(route); 40 | }); 41 | } 42 | if ( 43 | state.server.type === 'EE' && 44 | state.server.authentication_methods.indexOf('OIDC') !== -1 45 | ) { 46 | oidcRoutes.forEach(function (route) { 47 | variableLinks.push(route); 48 | }); 49 | } 50 | 51 | const routes = otherRoutes.concat(variableLinks, sidebarRoutes); 52 | 53 | let match = null; 54 | for (let i = 0; i < routes.length; i++) { 55 | match = matchPath(location.pathname, routes[i].path); 56 | if (match !== null) { 57 | break; 58 | } 59 | } 60 | 61 | return ( 62 | 63 | {routes.map((prop, key) => { 64 | if (prop.redirect) 65 | return ( 66 | 72 | ); 73 | return ( 74 | ( 77 | 83 | )} 84 | key={key} 85 | {...rest} 86 | /> 87 | ); 88 | })} 89 | 90 | ); 91 | }; 92 | 93 | export default SwitchRoutes; 94 | -------------------------------------------------------------------------------- /src/containers/Login/Login.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { connect } from 'react-redux'; 3 | import { bindActionCreators, compose } from 'redux'; 4 | import { withStyles } from '@material-ui/core'; 5 | import PropTypes from 'prop-types'; 6 | 7 | import { LoginForm, Notification } from '../../components'; 8 | import actionCreators from '../../actions/actionCreators'; 9 | import user from '../../services/user'; 10 | import host from '../../services/host'; 11 | import browserClient from '../../services/browser-client'; 12 | 13 | import image from '../../assets/img/background.jpg'; 14 | import store from '../../services/store'; 15 | import { Redirect } from 'react-router-dom'; 16 | 17 | const style = { 18 | wrapper: { 19 | position: 'relative', 20 | top: '0', 21 | height: '100vh', 22 | backgroundImage: `url(${image})`, 23 | backgroundSize: 'cover', 24 | display: 'flex', 25 | justifyContent: 'center', 26 | alignItems: 'center', 27 | }, 28 | content: { 29 | width: '100%', 30 | height: '100%', 31 | zIndex: '3', 32 | content: '', 33 | opacity: '.8', 34 | position: 'absolute', 35 | background: '#000', 36 | }, 37 | }; 38 | 39 | const Login = ({ classes, ...rest }) => { 40 | if (store.getState().user.isLoggedIn) { 41 | return ; 42 | } 43 | 44 | return ( 45 |
46 |
47 | 48 | 67 |
68 | ); 69 | }; 70 | 71 | Login.propTypes = { 72 | classes: PropTypes.object.isRequired, 73 | }; 74 | 75 | function mapStateToProps(state) { 76 | return { state: state }; 77 | } 78 | 79 | function mapDispatchToProps(dispatch) { 80 | return { actions: bindActionCreators(actionCreators, dispatch) }; 81 | } 82 | 83 | export default compose( 84 | withStyles(style), 85 | connect(mapStateToProps, mapDispatchToProps) 86 | )(Login); 87 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import { I18nextProvider } from 'react-i18next'; 4 | import { Provider } from 'react-redux'; 5 | import CssBaseline from '@material-ui/core/CssBaseline'; 6 | import 'moment-timezone'; 7 | 8 | import i18n from './i18n'; 9 | 10 | import App from './containers/App'; 11 | import worker from './services/worker'; 12 | import store from './services/store'; 13 | 14 | /** 15 | * @typedef {Object} PublicPrivateKeyPair 16 | * @property {string} public_key The public key (hex encoded) 17 | * @property {string} private_key The private key (hex encoded) 18 | * 19 | * @typedef {Object} EncryptedValue 20 | * @property {string} text The public key (hex encoded) 21 | * @property {string} nonce The private key (hex encoded) 22 | * 23 | * @typedef {string} uuid 24 | * 25 | * @typedef {Object} SplittedUrl 26 | * @property {string} scheme The scheme e.g. 'http' or 'ftps' 27 | * @property {string} authority The scheme e.g. 'test.example.com:6000' 28 | * @property {string} full_domain The full domain e.g. 'test.example.com' 29 | * @property {string} top_domain The top level domain e.g. 'example.com' 30 | * @property {string} port The port e.g. '6000' 31 | * @property {string} port The path e.g. '/url-part/' 32 | * @property {string} port The query, evething after '?' e.g. 'myFunnyParameter=test' 33 | * @property {string} port The query, evething after '#' e.g. 'anotherParameter=test' 34 | * 35 | * @typedef {Object} TreeObject 36 | * @property {uuid} [datastore_id] The datastore id if its the top 37 | * @property {uuid} [parent_datastore_id] The parent datastore id 38 | * @property {uuid} [parent_share_id] The parent share id 39 | * @property {object} [share_rights] All the share rights in an object 40 | * @property {boolean} [expanded] Is the folder expanded or not 41 | * @property {Array} [items] The items in the tree object 42 | * @property {Array} [folders] The folders in the tree object containing other TreeObject 43 | * @property {Object} [share_index] The share index 44 | * 45 | * @typedef {Object} RightObject 46 | * @property {boolean} read The read rights 47 | * @property {boolean} write The write rights 48 | * @property {boolean} grant The grant rights 49 | * @property {boolean} [delete] The delete rights 50 | * 51 | */ 52 | 53 | import './assets/css/material-dashboard-react.css'; 54 | 55 | ReactDOM.render( 56 | 57 | 58 | 59 | 60 | 61 | , 62 | document.getElementById('root') 63 | ); 64 | worker.register(); 65 | -------------------------------------------------------------------------------- /src/reducers/admin_client.js: -------------------------------------------------------------------------------- 1 | import { LOGOUT, SET_ADMIN_CLIENT_CONFIG } from '../actions/actionTypes'; 2 | 3 | const default_config = {}; 4 | 5 | function server( 6 | state = { 7 | config: default_config 8 | }, 9 | action 10 | ) { 11 | switch (action.type) { 12 | case LOGOUT: 13 | return Object.assign({}, state, { 14 | config: default_config 15 | }); 16 | case SET_ADMIN_CLIENT_CONFIG: 17 | return Object.assign({}, state, { 18 | config: action.config 19 | }); 20 | default: 21 | return state; 22 | } 23 | } 24 | 25 | export default server; 26 | -------------------------------------------------------------------------------- /src/reducers/client.js: -------------------------------------------------------------------------------- 1 | import { LOGOUT, SET_CLIENT_URL } from '../actions/actionTypes'; 2 | 3 | const default_url = ''; 4 | 5 | function server( 6 | state = { 7 | url: default_url, 8 | }, 9 | action 10 | ) { 11 | switch (action.type) { 12 | case LOGOUT: 13 | return Object.assign({}, state, { 14 | url: default_url.toLowerCase(), 15 | }); 16 | case SET_CLIENT_URL: 17 | return Object.assign({}, state, { 18 | url: action.url.toLowerCase(), 19 | }); 20 | default: 21 | return state; 22 | } 23 | } 24 | 25 | export default server; 26 | -------------------------------------------------------------------------------- /src/reducers/index.js: -------------------------------------------------------------------------------- 1 | import { combineReducers } from 'redux'; 2 | import persistent from './persistent'; 3 | import admin_client from './admin_client'; 4 | import user from './user'; 5 | import server from './server'; 6 | import client from './client'; 7 | import notification from './notification'; 8 | 9 | const rootReducer = combineReducers({ 10 | persistent, 11 | admin_client, 12 | user, 13 | server, 14 | client, 15 | notification, 16 | }); 17 | 18 | export default rootReducer; 19 | -------------------------------------------------------------------------------- /src/reducers/notification.js: -------------------------------------------------------------------------------- 1 | import { NOTIFICATION_SEND, NOTIFICATION_SET } from '../actions/actionTypes'; 2 | 3 | function notification( 4 | state = { 5 | messages: [], 6 | }, 7 | action 8 | ) { 9 | switch (action.type) { 10 | case NOTIFICATION_SEND: 11 | const new_messages = state.messages; 12 | new_messages.push({ 13 | text: action.message, 14 | type: action.message_type, 15 | }); 16 | 17 | return Object.assign({}, state, { 18 | messages: new_messages, 19 | }); 20 | case NOTIFICATION_SET: 21 | return Object.assign({}, state, { 22 | messages: action.messages, 23 | }); 24 | default: 25 | return state; 26 | } 27 | } 28 | 29 | export default notification; 30 | -------------------------------------------------------------------------------- /src/reducers/persistent.js: -------------------------------------------------------------------------------- 1 | import { SET_KNOWN_HOSTS } from '../actions/actionTypes'; 2 | 3 | const default_known_hosts = [ 4 | { 5 | url: 'https://www.psono.pw/server', 6 | verify_key: 7 | 'a16301bd25e3a445a83b279e7091ea91d085901933f310fdb1b137db9676de59', 8 | }, 9 | ]; 10 | 11 | function persistent( 12 | state = { 13 | known_hosts: default_known_hosts, 14 | }, 15 | action 16 | ) { 17 | switch (action.type) { 18 | case SET_KNOWN_HOSTS: 19 | return Object.assign({}, state, { 20 | known_hosts: action.known_hosts, 21 | }); 22 | default: 23 | return state; 24 | } 25 | } 26 | 27 | export default persistent; 28 | -------------------------------------------------------------------------------- /src/reducers/user.js: -------------------------------------------------------------------------------- 1 | import { 2 | SET_USER_USERNAME, 3 | SET_USER_INFO_1, 4 | SET_USER_INFO_2, 5 | SET_USER_INFO_3, 6 | SET_SERVER_SECRET_EXISTS, 7 | LOGOUT, 8 | } from '../actions/actionTypes'; 9 | 10 | const default_username = ''; 11 | const default_remember_me = false; 12 | const default_trust_device = false; 13 | 14 | function user( 15 | state = { 16 | isLoggedIn: false, 17 | username: default_username, 18 | remember_me: default_remember_me, 19 | trust_device: default_trust_device, 20 | authentication: '', 21 | user_secret_key: '', 22 | serverSecretExists: false, 23 | user_private_key: '', 24 | user_public_key: '', 25 | session_secret_key: '', 26 | token: '', 27 | user_sauce: '', 28 | user_email: '', 29 | user_id: '', 30 | }, 31 | action 32 | ) { 33 | switch (action.type) { 34 | case SET_USER_USERNAME: 35 | return Object.assign({}, state, { 36 | username: action.username, 37 | }); 38 | case SET_USER_INFO_1: 39 | return Object.assign({}, state, { 40 | remember_me: action.remember_me, 41 | trust_device: action.trust_device, 42 | authentication: action.authentication, 43 | }); 44 | case SET_USER_INFO_2: 45 | return Object.assign({}, state, { 46 | user_private_key: action.user_private_key, 47 | user_public_key: action.user_public_key, 48 | session_secret_key: action.session_secret_key, 49 | token: action.token, 50 | user_sauce: action.user_sauce, 51 | authentication: action.authentication, 52 | }); 53 | case SET_USER_INFO_3: 54 | return Object.assign({}, state, { 55 | isLoggedIn: true, 56 | user_id: action.user_id, 57 | user_email: action.user_email, 58 | user_secret_key: action.user_secret_key, 59 | serverSecretExists: action.serverSecretExists, 60 | }); 61 | case SET_SERVER_SECRET_EXISTS: 62 | return Object.assign({}, state, { 63 | serverSecretExists: action.serverSecretExists, 64 | }); 65 | case LOGOUT: 66 | return Object.assign({}, state, { 67 | isLoggedIn: false, 68 | username: state.remember_me ? state.username : default_username, 69 | remember_me: state.remember_me 70 | ? state.remember_me 71 | : default_remember_me, 72 | trust_device: state.remember_me 73 | ? state.trust_device 74 | : default_trust_device, 75 | authentication: '', 76 | user_secret_key: '', 77 | serverSecretExists: false, 78 | user_private_key: '', 79 | user_email: '', 80 | user_id: '', 81 | user_public_key: '', 82 | session_secret_key: '', 83 | token: '', 84 | user_sauce: '', 85 | }); 86 | default: 87 | return state; 88 | } 89 | } 90 | 91 | export default user; 92 | -------------------------------------------------------------------------------- /src/routes/ee.js: -------------------------------------------------------------------------------- 1 | import { Policy } from '@material-ui/icons'; 2 | 3 | import Policies from '../views/Policies/Index'; 4 | 5 | let routes = [ 6 | { 7 | path: '/policies', 8 | sidebarName: 'POLICIES', 9 | navbarName: 'POLICIES', 10 | icon: Policy, 11 | component: Policies, 12 | }, 13 | ]; 14 | 15 | export default routes; 16 | -------------------------------------------------------------------------------- /src/routes/ldap.js: -------------------------------------------------------------------------------- 1 | import LDAP from '../views/LDAP/Index'; 2 | 3 | import { Business } from '@material-ui/icons'; 4 | 5 | let routes = [ 6 | { 7 | path: '/ldap', 8 | sidebarName: 'LDAP', 9 | navbarName: 'LDAP', 10 | icon: Business, 11 | component: LDAP, 12 | }, 13 | ]; 14 | 15 | export default routes; 16 | -------------------------------------------------------------------------------- /src/routes/oidc.js: -------------------------------------------------------------------------------- 1 | import OIDC from '../views/OIDC/Index'; 2 | 3 | import { Business } from '@material-ui/icons'; 4 | 5 | let routes = [ 6 | { 7 | path: '/oidc', 8 | sidebarName: 'OIDC', 9 | navbarName: 'OIDC', 10 | icon: Business, 11 | component: OIDC, 12 | }, 13 | ]; 14 | 15 | export default routes; 16 | -------------------------------------------------------------------------------- /src/routes/other.js: -------------------------------------------------------------------------------- 1 | import SecurityReportEdit from '../views/SecurityReport/Edit'; 2 | import UserEdit from '../views/User/Edit'; 3 | import GroupEdit from '../views/Group/Edit'; 4 | import GroupCreate from '../views/Group/Create'; 5 | import GroupShareRightCreate from '../views/Group/ShareRightCreate'; 6 | import PolicyCreate from '../views/Policies/Create'; 7 | import PolicyEdit from '../views/Policies/Edit'; 8 | import UserCreate from '../views/User/Create'; 9 | 10 | import { Person, Group, Policy, Timeline } from '@material-ui/icons'; 11 | 12 | let routes = [ 13 | { 14 | path: '/user/:user_id', 15 | sidebarName: 'USER', 16 | navbarName: 'USER', 17 | icon: Person, 18 | component: UserEdit, 19 | }, 20 | { 21 | path: '/security-report/:security_report_id', 22 | sidebarName: 'SECURITY_REPORT', 23 | navbarName: 'SECURITY_REPORT', 24 | icon: Timeline, 25 | component: SecurityReportEdit, 26 | }, 27 | { 28 | path: '/policy/:policy_id', 29 | sidebarName: 'POLICY', 30 | navbarName: 'POLICY', 31 | icon: Policy, 32 | component: PolicyEdit, 33 | }, 34 | { 35 | path: '/group/:group_id/create-share-right', 36 | sidebarName: 'CREATE_SHARE_RIGHT', 37 | navbarName: 'CREATE_SHARE_RIGHT', 38 | icon: Group, 39 | component: GroupShareRightCreate, 40 | }, 41 | { 42 | path: '/group/:group_id', 43 | sidebarName: 'GROUP', 44 | navbarName: 'GROUP', 45 | icon: Group, 46 | component: GroupEdit, 47 | }, 48 | { 49 | path: '/policies/create', 50 | sidebarName: 'CREATE_POLICY', 51 | navbarName: 'CREATE_POLICY', 52 | icon: Policy, 53 | component: PolicyCreate, 54 | }, 55 | { 56 | path: '/groups/create', 57 | sidebarName: 'CREATE_GROUP', 58 | navbarName: 'CREATE_GROUP', 59 | icon: Group, 60 | component: GroupCreate, 61 | }, 62 | { 63 | path: '/users/create', 64 | sidebarName: 'CREATE_USER', 65 | navbarName: 'CREATE_USER', 66 | icon: Person, 67 | component: UserCreate, 68 | }, 69 | ]; 70 | 71 | export default routes; 72 | -------------------------------------------------------------------------------- /src/routes/saml.js: -------------------------------------------------------------------------------- 1 | import SAML from '../views/SAML/Index'; 2 | import SCIM from '../views/SCIM/Index'; 3 | 4 | import { Business } from '@material-ui/icons'; 5 | 6 | let routes = [ 7 | { 8 | path: '/saml', 9 | sidebarName: 'SAML', 10 | navbarName: 'SAML', 11 | icon: Business, 12 | component: SAML, 13 | }, 14 | { 15 | path: '/scim', 16 | sidebarName: 'SCIM', 17 | navbarName: 'SCIM', 18 | icon: Business, 19 | component: SCIM, 20 | }, 21 | ]; 22 | 23 | export default routes; 24 | -------------------------------------------------------------------------------- /src/routes/sidebar.js: -------------------------------------------------------------------------------- 1 | import DashboardPage from '../views/Dashboard/Index'; 2 | import Users from '../views/Users/Index'; 3 | import Groups from '../views/Groups/Index'; 4 | import SecurityReports from '../views/SecurityReports/Index'; 5 | 6 | import { Dashboard, Person, Timeline, Group } from '@material-ui/icons'; 7 | 8 | let routes = [ 9 | { 10 | path: '/dashboard', 11 | sidebarName: 'DASHBOARD', 12 | navbarName: 'DASHBOARD', 13 | icon: Dashboard, 14 | component: DashboardPage, 15 | }, 16 | { 17 | path: '/users', 18 | sidebarName: 'USERS', 19 | navbarName: 'USERS', 20 | icon: Person, 21 | component: Users, 22 | }, 23 | { 24 | path: '/groups', 25 | sidebarName: 'GROUPS', 26 | navbarName: 'GROUPS', 27 | icon: Group, 28 | component: Groups, 29 | }, 30 | { 31 | path: '/security-reports', 32 | sidebarName: 'SECURITY_REPORTS', 33 | navbarName: 'SECURITY_REPORTS', 34 | icon: Timeline, 35 | component: SecurityReports, 36 | }, 37 | ]; 38 | 39 | if (!process.env.NODE_ENV || process.env.NODE_ENV === 'development') { 40 | //dev 41 | // preserve 42 | // routes = routes.concat([ 43 | // { 44 | // path: '/profile', 45 | // sidebarName: 'Orig. User Profile', 46 | // navbarName: 'Original: Profile', 47 | // icon: Person, 48 | // component: UserProfile 49 | // }, 50 | // { 51 | // path: '/table', 52 | // sidebarName: 'Orig. Table List', 53 | // navbarName: 'Original: Table List', 54 | // icon: ContentPaste, 55 | // component: TableList 56 | // }, 57 | // { 58 | // path: '/typography', 59 | // sidebarName: 'Orig. Typography', 60 | // navbarName: 'Original: Typography', 61 | // icon: LibraryBooks, 62 | // component: Typography 63 | // }, 64 | // { 65 | // path: '/icons', 66 | // sidebarName: 'Orig.I cons', 67 | // navbarName: 'Original: Icons', 68 | // icon: BubbleChart, 69 | // component: Icons 70 | // }, 71 | // { 72 | // path: '/maps', 73 | // sidebarName: 'Orig. Maps', 74 | // navbarName: 'Original: Map', 75 | // icon: LocationOn, 76 | // component: Maps 77 | // }, 78 | // { 79 | // path: '/notifications', 80 | // sidebarName: 'Orig. Notifications', 81 | // navbarName: 'Original: Notifications', 82 | // icon: Notifications, 83 | // component: NotificationsPage 84 | // } 85 | // ]); 86 | } else { 87 | // prod 88 | } 89 | 90 | routes = routes.concat([ 91 | { redirect: true, path: '/', to: '/dashboard', navbarName: 'Redirect' }, 92 | ]); 93 | 94 | export default routes; 95 | -------------------------------------------------------------------------------- /src/services/api-client.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Client service for the psono web client 3 | */ 4 | 5 | import store from './store'; 6 | 7 | async function getVersion() { 8 | const client_url = store.getState().server.web_client; 9 | const response = await fetch( 10 | client_url + '/VERSION.txt?t=' + new Date().getTime() 11 | ); 12 | return await response.text(); 13 | } 14 | 15 | const service = { 16 | getVersion, 17 | }; 18 | 19 | export default service; 20 | -------------------------------------------------------------------------------- /src/services/api-static.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Gitlab service, that implements the Gitlab API 3 | */ 4 | 5 | const BASE_URL = 'https://static.psono.com'; 6 | 7 | /** 8 | * 9 | * Ajax GET request to download a ressource from static.psono.com 10 | * 11 | * @param {string} ressource The "url" part to return 12 | * 13 | * @returns {Promise} promise 14 | */ 15 | async function get(ressource) { 16 | const response = await fetch(BASE_URL + ressource); 17 | return await response.json(); 18 | } 19 | 20 | const service = { 21 | get, 22 | }; 23 | 24 | export default service; 25 | -------------------------------------------------------------------------------- /src/services/clientjs.js: -------------------------------------------------------------------------------- 1 | /** 2 | * clientjs service, a small wrapper for ClientJS 3 | */ 4 | 5 | const service = new window.ClientJS(); 6 | export default service; 7 | -------------------------------------------------------------------------------- /src/services/device.js: -------------------------------------------------------------------------------- 1 | import client_js from './clientjs'; 2 | 3 | let fingerprint; 4 | 5 | activate(); 6 | function activate() { 7 | getDeviceFingerprintAsync().then(function (local_fingerprint) { 8 | fingerprint = local_fingerprint; 9 | }); 10 | } 11 | 12 | /** 13 | * Returns the device fingerprint 14 | * 15 | * @returns Promise> Returns promise with the device fingerprint 16 | */ 17 | function getDeviceFingerprintAsync() { 18 | return new Promise((resolve, reject) => { 19 | resolve(client_js.getFingerprint()); 20 | }); 21 | } 22 | 23 | /** 24 | * Returns the device fingerprint 25 | * 26 | * @returns {string} Fingerprint of the device 27 | */ 28 | function getDeviceFingerprint() { 29 | if (fingerprint) { 30 | return fingerprint; 31 | } 32 | fingerprint = client_js.getFingerprint(); 33 | return fingerprint; 34 | } 35 | 36 | /** 37 | * Returns weather we have an IE or not 38 | * 39 | * @returns {boolean} Is this an IE user 40 | */ 41 | function is_ie() { 42 | return client_js.isIE(); 43 | } 44 | 45 | /** 46 | * Returns weather we have a Chrome or not 47 | * 48 | * @returns {boolean} Is this an Chrome user 49 | */ 50 | function is_chrome() { 51 | return client_js.isChrome(); 52 | } 53 | 54 | /** 55 | * Returns weather we have a Firefox or not 56 | * 57 | * @returns {boolean} Is this an Firefox user 58 | */ 59 | function is_firefox() { 60 | return client_js.isFirefox(); 61 | } 62 | 63 | /** 64 | * Returns weather we have a Safari or not 65 | * 66 | * @returns {boolean} Is this an Safari user 67 | */ 68 | function is_safari() { 69 | return client_js.isSafari(); 70 | } 71 | 72 | /** 73 | * Returns weather we have a Opera or not 74 | * 75 | * @returns {boolean} Is this an Opera user 76 | */ 77 | function is_opera() { 78 | return client_js.isOpera(); 79 | } 80 | 81 | /** 82 | * Generates the Device description out of the Vendor, OS, Version and others 83 | * 84 | * @returns {string} Returns the device's description 85 | */ 86 | function getDeviceDescription() { 87 | let description = ''; 88 | if (typeof client_js.getDeviceVendor() !== 'undefined') { 89 | description = description + client_js.getDeviceVendor() + ' '; 90 | } 91 | if (typeof client_js.getDevice() !== 'undefined') { 92 | description = description + client_js.getDevice() + ' '; 93 | } 94 | if (typeof client_js.getOS() !== 'undefined') { 95 | description = description + client_js.getOS() + ' '; 96 | } 97 | if (typeof client_js.getOSVersion() !== 'undefined') { 98 | description = description + client_js.getOSVersion() + ' '; 99 | } 100 | if (typeof client_js.getBrowser() !== 'undefined') { 101 | description = description + client_js.getBrowser() + ' '; 102 | } 103 | if (typeof client_js.getBrowserVersion() !== 'undefined') { 104 | description = description + client_js.getBrowserVersion() + ' '; 105 | } 106 | return description; 107 | } 108 | 109 | const service = { 110 | getDeviceFingerprint: getDeviceFingerprint, 111 | is_ie: is_ie, 112 | is_chrome: is_chrome, 113 | is_firefox: is_firefox, 114 | is_safari: is_safari, 115 | is_opera: is_opera, 116 | getDeviceDescription: getDeviceDescription, 117 | }; 118 | 119 | export default service; 120 | -------------------------------------------------------------------------------- /src/services/ivalt.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Ivalt and all the functions to create / edit / delete it ... 3 | */ 4 | 5 | import psono_server from './api-server'; 6 | import store from './store'; 7 | 8 | function sendTwoFactorNotification() { 9 | const token = store.getState().user.token; 10 | const sessionSecretKey = store.getState().user.session_secret_key; 11 | const onSuccess = function () { 12 | return true; 13 | }; 14 | const onError = function () { 15 | return false; 16 | }; 17 | return psono_server 18 | .ivaltVerify(token, sessionSecretKey, 'notification') 19 | .then(onSuccess, onError); 20 | } 21 | 22 | function validateIvaltTwoFactor() { 23 | const token = store.getState().user.token; 24 | const sessionSecretKey = store.getState().user.session_secret_key; 25 | const onSuccess = function (res) { 26 | return res; 27 | }; 28 | const onError = function (res) { 29 | return res; 30 | }; 31 | return psono_server 32 | .ivaltVerify(token, sessionSecretKey, 'verification') 33 | .then(onSuccess, onError); 34 | } 35 | 36 | const ivaltService = { 37 | sendTwoFactorNotification, 38 | validateIvaltTwoFactor, 39 | }; 40 | 41 | export default ivaltService; 42 | -------------------------------------------------------------------------------- /src/services/notification.js: -------------------------------------------------------------------------------- 1 | import action from '../actions/boundActionCreators'; 2 | 3 | /** 4 | * Sends an info message 5 | * 6 | * @param {array} message The message to send 7 | */ 8 | function infoSend(message) { 9 | action.sendNotification(message, 'info'); 10 | } 11 | 12 | /** 13 | * Sends an info message 14 | * 15 | * @param {array} message The message to send 16 | */ 17 | function errorSend(message) { 18 | action.sendNotification(message, 'danger'); 19 | } 20 | 21 | /** 22 | * Resets messages 23 | */ 24 | function reset() { 25 | action.setNotifications([]); 26 | } 27 | 28 | /** 29 | * Resets messages 30 | */ 31 | function set(messages) { 32 | action.setNotifications(messages); 33 | } 34 | 35 | const service = { 36 | infoSend, 37 | errorSend, 38 | reset, 39 | set, 40 | }; 41 | 42 | export default service; 43 | -------------------------------------------------------------------------------- /src/services/store.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Store service 3 | */ 4 | 5 | import { createStore, applyMiddleware } from 'redux'; 6 | import thunkMiddleware from 'redux-thunk'; 7 | import { createLogger } from 'redux-logger'; 8 | import { persistReducer } from 'redux-persist'; 9 | import storage from 'redux-persist/lib/storage'; 10 | 11 | import rootReducer from '../reducers'; 12 | 13 | const middlewares = [thunkMiddleware]; 14 | 15 | if (!process.env.NODE_ENV || process.env.NODE_ENV === 'development') { 16 | const loggerMiddleware = createLogger(); 17 | middlewares.push(loggerMiddleware); 18 | } 19 | 20 | const persistConfig = { 21 | key: 'root', 22 | blacklist: ['transient', 'notification'], 23 | storage, 24 | }; 25 | 26 | const persistedReducer = persistReducer(persistConfig, rootReducer); 27 | 28 | let service = createStore(persistedReducer, applyMiddleware(...middlewares)); 29 | 30 | export default service; 31 | -------------------------------------------------------------------------------- /src/services/webauthn.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Fido / Webauthn and all the functions to create / edit / delete it ... 3 | */ 4 | 5 | import psonoServer from './api-server'; 6 | import store from './store'; 7 | import helperService from './helper'; 8 | 9 | /** 10 | * Returns the current origin 11 | * 12 | * @returns {string} Returns the current origin 13 | */ 14 | function getOrigin() { 15 | const parsedUrl = helperService.parse_url(window.location.href); 16 | return parsedUrl.base_url; 17 | } 18 | 19 | /** 20 | * Initiate the second factor authentication with webauthn 21 | * 22 | * @returns {Promise} Returns a promise with the user information 23 | */ 24 | function verifyWebauthnInit() { 25 | const token = store.getState().user.token; 26 | const sessionSecretKey = store.getState().user.session_secret_key; 27 | 28 | const onSuccess = function (request) { 29 | return request.data; 30 | }; 31 | const onError = function (request) { 32 | return Promise.reject(request.data); 33 | }; 34 | return psonoServer 35 | .webauthnVerifyInit(token, sessionSecretKey, getOrigin()) 36 | .then(onSuccess, onError); 37 | } 38 | 39 | /** 40 | * Solve the second factor webauthn authentication 41 | * 42 | * @param {string} credential The credentials passed by the browser 43 | * 44 | * @returns {Promise} Returns a promise with the user information 45 | */ 46 | function verifyWebauthn(credential) { 47 | const token = store.getState().user.token; 48 | const sessionSecretKey = store.getState().user.session_secret_key; 49 | 50 | const onSuccess = function (request) { 51 | return request.data; 52 | }; 53 | const onError = function (request) { 54 | return Promise.reject(request.data); 55 | }; 56 | return psonoServer 57 | .webauthnVerify(token, sessionSecretKey, credential) 58 | .then(onSuccess, onError); 59 | } 60 | 61 | const webauthnService = { 62 | verifyWebauthnInit, 63 | verifyWebauthn, 64 | }; 65 | 66 | export default webauthnService; 67 | -------------------------------------------------------------------------------- /src/setupTests.js: -------------------------------------------------------------------------------- 1 | const localStorageMock = { 2 | getItem: jest.fn(), 3 | setItem: jest.fn(), 4 | clear: jest.fn(), 5 | }; 6 | global.localStorage = localStorageMock; 7 | 8 | jest.mock('./services/clientjs', () => { 9 | return { 10 | getFingerprint: () => { 11 | return 'dummy_fingerprint'; 12 | }, 13 | }; 14 | }); 15 | -------------------------------------------------------------------------------- /src/variables/general.jsx: -------------------------------------------------------------------------------- 1 | // ############################## 2 | // // // Tasks for TasksCard - see Dashboard view 3 | // ############################# 4 | 5 | export const bugs = [ 6 | 'Sign contract for "What are conference organizers afraid of?"', 7 | 'Lines From Great Russian Literature? Or E-mails From My Boss?', 8 | 'Flooded: One year later, assessing what was lost and what was found when a ravaging rain swept through metro Detroit', 9 | 'Create 4 Invisible User Experiences you Never Knew About' 10 | ]; 11 | export const website = [ 12 | 'Flooded: One year later, assessing what was lost and what was found when a ravaging rain swept through metro Detroit', 13 | 'Sign contract for "What are conference organizers afraid of?"' 14 | ]; 15 | export const server = [ 16 | 'Lines From Great Russian Literature? Or E-mails From My Boss?', 17 | 'Flooded: One year later, assessing what was lost and what was found when a ravaging rain swept through metro Detroit', 18 | 'Sign contract for "What are conference organizers afraid of?"' 19 | ]; 20 | -------------------------------------------------------------------------------- /src/views/Dashboard/HealthCheck.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Grid } from '@material-ui/core'; 3 | import { useTranslation } from 'react-i18next'; 4 | import { HealthcheckCard } from '../../components'; 5 | import { GridItem } from '../../components'; 6 | import psono_server from '../../services/api-server'; 7 | 8 | const HealthCheck = (props) => { 9 | const { t } = useTranslation(); 10 | const [healthcheck, setHealthcheck] = React.useState({ 11 | db_read: {}, 12 | db_sync: {}, 13 | time_sync: {}, 14 | }); 15 | 16 | React.useEffect(() => { 17 | psono_server.healthcheck().then( 18 | (response) => { 19 | //healthy is reported as 200 20 | setHealthcheck(response.data); 21 | }, 22 | (response) => { 23 | if (response.hasOwnProperty('data')) { 24 | setHealthcheck(response.data); 25 | } else { 26 | console.log(response); 27 | } 28 | } 29 | ); 30 | // eslint-disable-next-line react-hooks/exhaustive-deps 31 | }, []); 32 | 33 | return ( 34 | 35 | 36 | 42 | 43 | 44 | 50 | 51 | 52 | 58 | 59 | 60 | ); 61 | }; 62 | 63 | export default HealthCheck; 64 | -------------------------------------------------------------------------------- /src/views/Icons/Icons.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { withStyles, Grid, Hidden } from '@material-ui/core'; 3 | import PropTypes from 'prop-types'; 4 | 5 | import { RegularCard, P, A, GridItem } from '../../components'; 6 | import iconsStyle from '../../assets/jss/material-dashboard-react/iconsStyle'; 7 | 8 | const Icons = ({ classes }) => { 9 | return ( 10 | 11 | 12 | 17 | Handcrafted by our friends from{' '} 18 | 23 | Google 24 | 25 |

26 | } 27 | content={ 28 |
29 | 30 | 39 | 40 | 41 | 42 |
43 | The icons are visible on Desktop mode 44 | inside an iframe. Since the iframe is 45 | not working on Mobile and Tablets please 46 | visit the icons on their original page 47 | on Google. Check the{' '} 48 | 53 | Material Icons 54 | 55 |
56 |
57 |
58 |
59 | } 60 | /> 61 |
62 |
63 | ); 64 | }; 65 | 66 | Icons.propTypes = { 67 | classes: PropTypes.object.isRequired, 68 | }; 69 | 70 | export default withStyles(iconsStyle)(Icons); 71 | -------------------------------------------------------------------------------- /src/views/OIDC/Index.jsx: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from 'react'; 2 | import { Grid } from '@material-ui/core'; 3 | 4 | import { OIDCCard, GridItem } from '../../components'; 5 | import psono_server from '../../services/api-server'; 6 | import store from '../../services/store'; 7 | 8 | const Users = () => { 9 | const [oidcGroups, setOidcGroups] = useState([]); 10 | 11 | const createGroupsNode = (oidc_group) => { 12 | oidc_group.groups = ( 13 |
14 | {oidc_group.groups.map((group, key) => ( 15 | <> 16 | {key !== 0 ? ', ' : ''} 17 | 18 | {group.name} 19 | 20 | 21 | ))} 22 |
23 | ); 24 | }; 25 | 26 | const loadOidcGroups = () => { 27 | psono_server 28 | .admin_oidc_group( 29 | store.getState().user.token, 30 | store.getState().user.session_secret_key 31 | ) 32 | .then((response) => { 33 | const { oidc_groups } = response.data; 34 | 35 | oidc_groups.forEach((oidc_group) => { 36 | oidc_group['name'] = 37 | oidc_group['display_name'] || oidc_group['oidc_name']; 38 | createGroupsNode(oidc_group); 39 | }); 40 | 41 | setOidcGroups(oidc_groups); 42 | }); 43 | }; 44 | 45 | const onDeleteOidcGroups = (selectedGroups) => { 46 | selectedGroups.forEach((group) => { 47 | psono_server 48 | .admin_delete_oidc_group( 49 | store.getState().user.token, 50 | store.getState().user.session_secret_key, 51 | group.id 52 | ) 53 | .then(() => { 54 | loadOidcGroups(); 55 | }); 56 | }); 57 | }; 58 | 59 | useEffect(() => { 60 | loadOidcGroups(); 61 | }, []); 62 | 63 | return ( 64 |
65 | 66 | 67 | 71 | 72 | 73 |
74 | ); 75 | }; 76 | 77 | export default Users; 78 | -------------------------------------------------------------------------------- /src/views/SAML/Index.jsx: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from 'react'; 2 | import { Grid } from '@material-ui/core'; 3 | import { Redirect } from 'react-router-dom'; 4 | 5 | import { SAMLCard, GridItem } from '../../components'; 6 | import psono_server from '../../services/api-server'; 7 | import notification from '../../services/notification'; 8 | import store from '../../services/store'; 9 | 10 | const SAML = (props) => { 11 | const [redirectTo, setRedirectTo] = useState(''); 12 | const [samlGroups, setSamlGroups] = useState([]); 13 | 14 | const createGroupsNode = (saml_group) => { 15 | saml_group.groups = ( 16 |
17 | {saml_group.groups.map((group, key) => ( 18 | <> 19 | {key !== 0 ? ', ' : ''} 20 | 21 | {group.name} 22 | 23 | 24 | ))} 25 |
26 | ); 27 | }; 28 | 29 | const loadSamlGroups = () => { 30 | psono_server 31 | .admin_saml_group( 32 | store.getState().user.token, 33 | store.getState().user.session_secret_key 34 | ) 35 | .then((response) => { 36 | const { saml_groups } = response.data; 37 | 38 | saml_groups.forEach((saml_group) => { 39 | saml_group['name'] = 40 | saml_group['display_name'] || saml_group['saml_name']; 41 | createGroupsNode(saml_group); 42 | }); 43 | 44 | setSamlGroups(saml_groups); 45 | }); 46 | }; 47 | 48 | const onSyncGroupsSaml = () => { 49 | psono_server 50 | .admin_saml_group_sync( 51 | store.getState().user.token, 52 | store.getState().user.session_secret_key 53 | ) 54 | .then( 55 | () => { 56 | loadSamlGroups(); 57 | }, 58 | (response) => { 59 | const { non_field_errors } = response.data; 60 | notification.errorSend(non_field_errors[0]); 61 | } 62 | ); 63 | }; 64 | 65 | const onDeleteSamlGroups = (selectedGroups) => { 66 | selectedGroups.forEach((group) => { 67 | psono_server 68 | .admin_delete_saml_group( 69 | store.getState().user.token, 70 | store.getState().user.session_secret_key, 71 | group.id 72 | ) 73 | .then(() => { 74 | loadSamlGroups(); 75 | }); 76 | }); 77 | }; 78 | 79 | useEffect(() => { 80 | loadSamlGroups(); 81 | }, []); 82 | 83 | if (redirectTo) { 84 | return ; 85 | } 86 | 87 | return ( 88 |
89 | 90 | 91 | 96 | 97 | 98 |
99 | ); 100 | }; 101 | 102 | export default SAML; 103 | -------------------------------------------------------------------------------- /src/views/SCIM/Index.jsx: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from 'react'; 2 | import { Grid } from '@material-ui/core'; 3 | 4 | import { SCIMCard, GridItem } from '../../components'; 5 | import psono_server from '../../services/api-server'; 6 | import store from '../../services/store'; 7 | 8 | const Users = () => { 9 | const [scimGroups, setScimGroups] = useState([]); 10 | 11 | const createGroupsNode = (scim_group) => { 12 | scim_group.groups = ( 13 |
14 | {scim_group.groups.map((group, key) => ( 15 | <> 16 | {key !== 0 ? ', ' : ''} 17 | 18 | {group.name} 19 | 20 | 21 | ))} 22 |
23 | ); 24 | }; 25 | 26 | const loadScimGroups = () => { 27 | psono_server 28 | .admin_scim_group( 29 | store.getState().user.token, 30 | store.getState().user.session_secret_key 31 | ) 32 | .then((response) => { 33 | const { scim_groups } = response.data; 34 | scim_groups.forEach(createGroupsNode); 35 | setScimGroups( 36 | scim_groups.map((scim_group) => ({ 37 | ...scim_group, 38 | name: scim_group.display_name || scim_group.scim_name, 39 | })) 40 | ); 41 | }); 42 | }; 43 | 44 | const onDeleteScimGroups = (selectedGroups) => { 45 | selectedGroups.forEach((group) => { 46 | psono_server 47 | .admin_delete_scim_group( 48 | store.getState().user.token, 49 | store.getState().user.session_secret_key, 50 | group.id 51 | ) 52 | .then(() => { 53 | loadScimGroups(); 54 | }); 55 | }); 56 | }; 57 | 58 | useEffect(() => { 59 | loadScimGroups(); 60 | }, []); 61 | 62 | return ( 63 |
64 | 65 | 66 | 70 | 71 | 72 |
73 | ); 74 | }; 75 | 76 | export default Users; 77 | -------------------------------------------------------------------------------- /var/build-ubuntu.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | apt-get update && \ 3 | apt-get install -y ca-certificates curl gnupg apt-transport-https zip && \ 4 | mkdir -p /etc/apt/keyrings && \ 5 | curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg && \ 6 | echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_22.x nodistro main" | tee /etc/apt/sources.list.d/nodesource.list && \ 7 | apt-get update && \ 8 | apt-get -y install nodejs && \ 9 | npm --version && \ 10 | npm config set registry https://psono.jfrog.io/psono/api/npm/npm/ && \ 11 | npm config set @devexpress:registry https://psono.jfrog.io/psono/api/npm/npm/ && \ 12 | npm config set @types:registry https://psono.jfrog.io/psono/api/npm/npm/ && \ 13 | npm ci && \ 14 | npm install -g karma-cli && \ 15 | INLINE_RUNTIME_CHUNK=false npm run build && \ 16 | ./var/update_version.sh && \ 17 | cp LICENSE.md build/LICENSE.md 18 | 19 | -------------------------------------------------------------------------------- /var/deploy-docker.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | apk upgrade --no-cache 3 | apk add --update curl skopeo 4 | 5 | # Deploy to Docker Hub 6 | skopeo copy --all docker://$CI_REGISTRY_IMAGE:$CI_COMMIT_REF_SLUG docker://docker.io/psono/psono-admin-client:latest 7 | 8 | export docker_version_tag=$(echo $CI_COMMIT_TAG | awk '{ string=substr($0, 2, 100); print string; }' ) 9 | skopeo copy --all docker://$CI_REGISTRY_IMAGE:$CI_COMMIT_REF_SLUG docker://docker.io/psono/psono-admin-client:$docker_version_tag 10 | 11 | echo "Trigger psono combo rebuild" 12 | curl -X POST -F token=$PSONO_COMBO_TRIGGER_TOKEN -F ref=master https://gitlab.com/api/v4/projects/16086547/trigger/pipeline 13 | curl -X POST -F token=$PSONO_COMBO_EE_TRIGGER_TOKEN -F ref=master https://gitlab.com/api/v4/projects/16127995/trigger/pipeline 14 | -------------------------------------------------------------------------------- /var/deploy-github.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | echo "Clonging gitlab.com/psono/psono-admin-client.git" 3 | git clone https://gitlab.com/psono/psono-admin-client.git 4 | cd psono-admin-client 5 | git branch --track develop origin/develop 6 | git fetch --all 7 | git pull --all 8 | 9 | echo "Empty .ssh folder" 10 | if [ -d "/root/.ssh" ]; then 11 | rm -Rf /root/.ssh; 12 | fi 13 | mkdir -p /root/.ssh 14 | 15 | echo "Fill .ssh folder" 16 | echo "$github_deploy_key" > /root/.ssh/id_rsa 17 | cat > /root/.ssh/known_hosts <<- "EOF" 18 | |1|AuV+6vt2c6yHKSBI3cGlgiQgBw0=|oReK12ycO4x62cIfNqNIvclb2Ao= ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBEmKSENjQEezOmxkZMy7opKgwFB9nkt5YRrYMjNuG5N87uRgg6CLrbo5wAdT/y6v0mKV0U2w0WZ2YB/++Tpockg= 19 | |1|rLMxkb3I+R6GmInBad4kitV0ZTk=|c7GxoZTzebOPBENzRmPEylRcgtY= ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBEmKSENjQEezOmxkZMy7opKgwFB9nkt5YRrYMjNuG5N87uRgg6CLrbo5wAdT/y6v0mKV0U2w0WZ2YB/++Tpockg= 20 | EOF 21 | chmod 600 /root/.ssh/id_rsa 22 | chmod 600 /root/.ssh/known_hosts 23 | 24 | echo "Push to github.com/psono/psono-admin-client.git" 25 | git remote set-url origin git@github.com:psono/psono-admin-client.git 26 | git push --all origin 27 | -------------------------------------------------------------------------------- /var/deploy_changelog.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | apt-get update && \ 3 | apt-get install -y curl python3 && \ 4 | curl -fSL "https://dl.google.com/dl/cloudsdk/channels/rapid/downloads/google-cloud-cli-392.0.0-linux-x86_64.tar.gz" -o google-cloud-cli.tar.gz && echo "a7e88856a07ed75cf310ebe5415c922c9b516021a6c7e66b3eb8f2859b9351bc google-cloud-cli.tar.gz" | sha256sum -c - && tar -xzvf google-cloud-cli.tar.gz && \ 5 | ./google-cloud-sdk/install.sh -q && \ 6 | echo "$GOOGLE_APPLICATION_CREDENTIALS" > "/root/key.json" && \ 7 | ./google-cloud-sdk/bin/gcloud auth activate-service-account --key-file=/root/key.json && \ 8 | curl -H "PRIVATE-TOKEN: $GITLAB_PERSONAL_ACCESS_TOKEN" "https://gitlab.com/api/v4/projects/$CI_PROJECT_ID/repository/tags" --output changelog.json && \ 9 | ./google-cloud-sdk/bin/gsutil cp changelog.json gs://static.psono.com/gitlab.com/$CI_PROJECT_PATH/changelog.json -------------------------------------------------------------------------------- /var/deploy_nightlyartifacts.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | apt-get update && \ 3 | apt-get install -y lsb-release curl gnupg && \ 4 | echo "deb [signed-by=/usr/share/keyrings/cloud.google.gpg] http://packages.cloud.google.com/apt cloud-sdk main" | tee -a /etc/apt/sources.list.d/google-cloud-sdk.list && \ 5 | curl https://packages.cloud.google.com/apt/doc/apt-key.gpg | apt-key --keyring /usr/share/keyrings/cloud.google.gpg add - && \ 6 | apt-get update -y && apt-get install google-cloud-cli -y && \ 7 | echo "$GOOGLE_APPLICATION_CREDENTIALS" > "/root/key.json" && \ 8 | gcloud auth activate-service-account --key-file=/root/key.json && \ 9 | curl -fL https://getcli.jfrog.io | sh && \ 10 | ./jfrog config add rt-server-1 --artifactory-url=https://psono.jfrog.io/psono --user=gitlab --password=$artifactory_credentials --interactive=false && \ 11 | ./jfrog rt dl psono/client/$CI_COMMIT_REF_NAME/webclient.zip --flat && \ 12 | gsutil cp webclient.zip gs://get.psono.com/$CI_PROJECT_PATH/nightly/adminclient.zip && \ 13 | gsutil cp sbom.json gs://get.psono.com/$CI_PROJECT_PATH/nightly/sbom.json 14 | -------------------------------------------------------------------------------- /var/deploy_releaseartifacts.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | apt-get update && \ 3 | apt-get install -y lsb-release curl gnupg && \ 4 | echo "deb [signed-by=/usr/share/keyrings/cloud.google.gpg] http://packages.cloud.google.com/apt cloud-sdk main" | tee -a /etc/apt/sources.list.d/google-cloud-sdk.list && \ 5 | curl https://packages.cloud.google.com/apt/doc/apt-key.gpg | apt-key --keyring /usr/share/keyrings/cloud.google.gpg add - && \ 6 | apt-get update -y && apt-get install google-cloud-cli -y && \ 7 | echo "$GOOGLE_APPLICATION_CREDENTIALS" > "/root/key.json" && \ 8 | gcloud auth activate-service-account --key-file=/root/key.json && \ 9 | curl -fL https://getcli.jfrog.io | sh && \ 10 | ./jfrog config add rt-server-1 --artifactory-url=https://psono.jfrog.io/psono --user=gitlab --password=$artifactory_credentials --interactive=false && \ 11 | ./jfrog rt dl psono/admin-client/$CI_COMMIT_REF_NAME/webclient.zip --flat && \ 12 | gsutil cp webclient.zip gs://get.psono.com/$CI_PROJECT_PATH/latest/adminclient.zip && \ 13 | gsutil cp webclient.zip gs://get.psono.com/$CI_PROJECT_PATH/$CI_COMMIT_REF_NAME/adminclient.zip && \ 14 | gsutil cp sbom.json gs://get.psono.com/$CI_PROJECT_PATH/$CI_COMMIT_REF_NAME/sbom.json && \ 15 | gsutil cp sbom.json gs://get.psono.com/$CI_PROJECT_PATH/latest/sbom.json 16 | -------------------------------------------------------------------------------- /var/download_translations_from_artifactory.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -e 3 | 4 | # poeditor language codes: https://poeditor.com/docs/languages 5 | 6 | apt-get update && \ 7 | apt-get install -y curl && \ 8 | curl -f -o public/locales/locale-da.json https://psono.jfrog.io/psono/psono/admin-client/languages/locale-da.json && \ 9 | curl -f -o public/locales/locale-ca.json https://psono.jfrog.io/psono/psono/admin-client/languages/locale-ca.json && \ 10 | curl -f -o public/locales/locale-sv.json https://psono.jfrog.io/psono/psono/admin-client/languages/locale-sv.json && \ 11 | curl -f -o public/locales/locale-no.json https://psono.jfrog.io/psono/psono/admin-client/languages/locale-no.json && \ 12 | curl -f -o public/locales/locale-he.json https://psono.jfrog.io/psono/psono/admin-client/languages/locale-he.json && \ 13 | curl -f -o public/locales/locale-ar.json https://psono.jfrog.io/psono/psono/admin-client/languages/locale-ar.json && \ 14 | curl -f -o public/locales/locale-hi.json https://psono.jfrog.io/psono/psono/admin-client/languages/locale-hi.json && \ 15 | curl -f -o public/locales/locale-hu.json https://psono.jfrog.io/psono/psono/admin-client/languages/locale-hu.json && \ 16 | curl -f -o public/locales/locale-bn.json https://psono.jfrog.io/psono/psono/admin-client/languages/locale-bn.json && \ 17 | curl -f -o public/locales/locale-cs.json https://psono.jfrog.io/psono/psono/admin-client/languages/locale-cs.json && \ 18 | curl -f -o public/locales/locale-de.json https://psono.jfrog.io/psono/psono/admin-client/languages/locale-de.json && \ 19 | curl -f -o public/locales/locale-en.json https://psono.jfrog.io/psono/psono/admin-client/languages/locale-en.json && \ 20 | curl -f -o public/locales/locale-es.json https://psono.jfrog.io/psono/psono/admin-client/languages/locale-es.json && \ 21 | curl -f -o public/locales/locale-fi.json https://psono.jfrog.io/psono/psono/admin-client/languages/locale-fi.json && \ 22 | curl -f -o public/locales/locale-fr.json https://psono.jfrog.io/psono/psono/admin-client/languages/locale-fr.json && \ 23 | curl -f -o public/locales/locale-hr.json https://psono.jfrog.io/psono/psono/admin-client/languages/locale-hr.json && \ 24 | curl -f -o public/locales/locale-it.json https://psono.jfrog.io/psono/psono/admin-client/languages/locale-it.json && \ 25 | curl -f -o public/locales/locale-ja.json https://psono.jfrog.io/psono/psono/admin-client/languages/locale-ja.json && \ 26 | curl -f -o public/locales/locale-ko.json https://psono.jfrog.io/psono/psono/admin-client/languages/locale-ko.json && \ 27 | curl -f -o public/locales/locale-nl.json https://psono.jfrog.io/psono/psono/admin-client/languages/locale-nl.json && \ 28 | curl -f -o public/locales/locale-pt.json https://psono.jfrog.io/psono/psono/admin-client/languages/locale-pt.json && \ 29 | curl -f -o public/locales/locale-pt-BR.json https://psono.jfrog.io/psono/psono/admin-client/languages/locale-pt-br.json && \ 30 | curl -f -o public/locales/locale-pl.json https://psono.jfrog.io/psono/psono/admin-client/languages/locale-pl.json && \ 31 | curl -f -o public/locales/locale-ru.json https://psono.jfrog.io/psono/psono/admin-client/languages/locale-ru.json && \ 32 | curl -f -o public/locales/locale-sk.json https://psono.jfrog.io/psono/psono/admin-client/languages/locale-sk.json && \ 33 | curl -f -o public/locales/locale-vi.json https://psono.jfrog.io/psono/psono/admin-client/languages/locale-vi.json && \ 34 | curl -f -o public/locales/locale-zh-Hant.json https://psono.jfrog.io/psono/psono/admin-client/languages/locale-zh-Hant.json && \ 35 | curl -f -o public/locales/locale-zh-Hans.json https://psono.jfrog.io/psono/psono/admin-client/languages/locale-zh-Hans.json 36 | -------------------------------------------------------------------------------- /var/package-webclient.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | cd /builds/psono/psono-admin-client/build/ 3 | zip -r /builds/psono/psono-admin-client/build/psono.webclient.zip * 4 | cd /builds/psono/psono-admin-client/ -------------------------------------------------------------------------------- /var/update_version.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | if [ -z "$CI_COMMIT_TAG" ]; then 4 | exit 0 5 | fi 6 | 7 | if [ -z "$CI_COMMIT_SHA" ]; then 8 | exit 0 9 | fi 10 | 11 | if ! echo "$CI_COMMIT_TAG" | egrep -q ^v[0-9]+\.[0-9]+\.[0-9]+$; then 12 | exit 0 13 | fi 14 | 15 | version="$(echo $CI_COMMIT_TAG | awk '{ string=substr($0, 2, 100); print string; }' ) (Build $(echo $CI_COMMIT_SHA | awk '{ string=substr($0, 1, 8); print string; }' ))" 16 | 17 | echo $version > ./build/VERSION.txt 18 | 19 | -------------------------------------------------------------------------------- /var/upload_translations.py: -------------------------------------------------------------------------------- 1 | import requests 2 | import os 3 | import json 4 | import time 5 | 6 | POEDITOR_API_KEY = os.environ['POEDITOR_API_KEY'] 7 | POEDITOR_PROJECT_ID = os.environ['POEDITOR_PROJECT_ID'] 8 | 9 | 10 | FILE_PATHS = { 11 | 'en': 'public/locales/locale-en.json', 12 | } 13 | 14 | 15 | def upload_language(lang, updating): 16 | 17 | if lang in FILE_PATHS: 18 | data = { 19 | 'id': POEDITOR_PROJECT_ID, 20 | 'api_token': POEDITOR_API_KEY, 21 | 'updating': updating, 22 | 'language': lang, 23 | 'overwrite': 1, 24 | 'sync_terms ': 1, 25 | } 26 | with open(FILE_PATHS[lang], 'rb') as file: 27 | r = requests.post('https://api.poeditor.com/v2/projects/upload', data=data, files={'file': file}) 28 | 29 | else: 30 | print("Error: upload_language " + lang + " No webhook configured for this language") 31 | # params = ( 32 | # ('api_token', POEDITOR_API_KEY), 33 | # ('id_project', POEDITOR_PROJECT_ID), 34 | # ('language', lang), 35 | # ('operation', 'import_terms_and_translations'), 36 | # ) 37 | # 38 | # r = requests.post('https://poeditor.com/api/webhooks/gitlab', params=params) 39 | if not r.ok: 40 | print("Error: upload_language " + lang) 41 | print(r.text) 42 | exit(1) 43 | content = json.loads(r.content) 44 | if "response" not in content or "status" not in content["response"] or content["response"]["status"] != 'success': 45 | print("Error: upload_language " + lang) 46 | print(r.text) 47 | exit(1) 48 | print("Success: upload_language " + lang) 49 | 50 | 51 | def main(): 52 | # Upload 53 | upload_language('en', 'terms_translations') 54 | 55 | print("Success") 56 | 57 | if __name__ == "__main__": 58 | main() 59 | -------------------------------------------------------------------------------- /webpack.common.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const HtmlWebpackPlugin = require('html-webpack-plugin'); 3 | const CopyWebpackPlugin = require('copy-webpack-plugin'); 4 | const { CleanWebpackPlugin } = require('clean-webpack-plugin'); 5 | const webpack = require('webpack'); 6 | const paths = { 7 | publicUrlOrPath: '/portal/', 8 | }; 9 | 10 | module.exports = { 11 | entry: './src/index.js', 12 | output: { 13 | filename: 'static/js/[name].[contenthash:8].js', 14 | chunkFilename: 'static/js/[name].[contenthash:8].chunk.js', 15 | path: path.resolve(__dirname, 'build'), 16 | publicPath: '/portal/', 17 | }, 18 | plugins: [ 19 | new CleanWebpackPlugin(), 20 | new CopyWebpackPlugin({ 21 | patterns: [ 22 | { 23 | from: 'public', 24 | to: '', 25 | globOptions: { 26 | ignore: ['**/index.html'], 27 | }, 28 | }, 29 | ], 30 | }), 31 | new webpack.ProvidePlugin({ 32 | process: 'process/browser', 33 | Buffer: ['buffer', 'Buffer'], 34 | }), 35 | ], 36 | module: { 37 | rules: [ 38 | { 39 | test: /\.(js|jsx)$/, 40 | exclude: /node_modules/, 41 | use: { 42 | loader: 'babel-loader', 43 | options: { 44 | presets: [ 45 | ['@babel/preset-env', { targets: { node: 'current' } }], 46 | '@babel/preset-react', 47 | ], 48 | plugins: [ 49 | '@babel/plugin-proposal-class-properties', 50 | '@babel/plugin-transform-runtime', 51 | ], 52 | }, 53 | }, 54 | }, 55 | { 56 | test: /\.css$/, 57 | use: ['style-loader', 'css-loader'], 58 | }, 59 | { 60 | test: /\.(png|svg|jpg|jpeg|gif|ico)$/i, 61 | type: 'asset/resource', 62 | generator: { 63 | filename: 'static/media/[name].[hash:8][ext]', 64 | }, 65 | }, 66 | { 67 | test: /\.(woff|woff2|eot|ttf|otf)$/i, 68 | type: 'asset/resource', 69 | generator: { 70 | filename: 'static/media/[name].[hash:8][ext]', 71 | }, 72 | }, 73 | ], 74 | }, 75 | resolve: { 76 | extensions: ['.js', '.jsx'], 77 | fallback: { 78 | "crypto": false, 79 | "buffer": require.resolve("buffer/"), 80 | "path": require.resolve("path-browserify"), 81 | "process": require.resolve("process/browser"), 82 | }, 83 | }, 84 | }; -------------------------------------------------------------------------------- /webpack.dev.js: -------------------------------------------------------------------------------- 1 | const { merge } = require('webpack-merge'); 2 | const common = require('./webpack.common.js'); 3 | const path = require('path'); 4 | const webpack = require('webpack'); 5 | const HtmlWebpackPlugin = require('html-webpack-plugin'); 6 | 7 | module.exports = merge(common, { 8 | mode: 'development', 9 | devtool: 'inline-source-map', 10 | devServer: { 11 | static: { 12 | directory: path.join(__dirname, 'public'), 13 | }, 14 | hot: true, 15 | historyApiFallback: { 16 | disableDotRule: true, 17 | index: '/portal/', 18 | }, 19 | port: 3000, 20 | client: { 21 | overlay: { 22 | errors: true, 23 | warnings: false, 24 | }, 25 | }, 26 | }, 27 | plugins: [ 28 | new webpack.DefinePlugin({ 29 | 'process.env.NODE_ENV': JSON.stringify('development'), 30 | 'process.env.PUBLIC_URL': JSON.stringify('/portal'), 31 | }), 32 | // Replace HtmlWebpackPlugin to use template processing 33 | new HtmlWebpackPlugin({ 34 | inject: true, 35 | template: path.resolve(__dirname, 'public/index.html'), 36 | templateParameters: { 37 | PUBLIC_URL: '/portal', 38 | }, 39 | }), 40 | ], 41 | }); -------------------------------------------------------------------------------- /webpack.prod.js: -------------------------------------------------------------------------------- 1 | const { merge } = require('webpack-merge'); 2 | const common = require('./webpack.common.js'); 3 | const TerserPlugin = require('terser-webpack-plugin'); 4 | const MiniCssExtractPlugin = require('mini-css-extract-plugin'); 5 | const CssMinimizerPlugin = require('css-minimizer-webpack-plugin'); 6 | const webpack = require('webpack'); 7 | const HtmlWebpackPlugin = require('html-webpack-plugin'); 8 | const path = require('path'); 9 | 10 | module.exports = merge(common, { 11 | mode: 'production', 12 | devtool: 'source-map', 13 | output: { 14 | filename: 'static/js/[name].[contenthash:8].js', 15 | chunkFilename: 'static/js/[name].[contenthash:8].chunk.js', 16 | }, 17 | optimization: { 18 | minimize: true, 19 | minimizer: [ 20 | new TerserPlugin({ 21 | terserOptions: { 22 | compress: { 23 | comparisons: false, 24 | }, 25 | mangle: { 26 | safari10: true, 27 | }, 28 | output: { 29 | comments: false, 30 | ascii_only: true, 31 | }, 32 | }, 33 | }), 34 | new CssMinimizerPlugin(), 35 | ], 36 | splitChunks: { 37 | chunks: 'all', 38 | name: false, 39 | }, 40 | runtimeChunk: { 41 | name: (entrypoint) => `runtime-${entrypoint.name}`, 42 | }, 43 | }, 44 | plugins: [ 45 | new webpack.DefinePlugin({ 46 | 'process.env.NODE_ENV': JSON.stringify('production'), 47 | 'process.env.PUBLIC_URL': JSON.stringify('/portal'), 48 | }), 49 | new MiniCssExtractPlugin({ 50 | filename: 'static/css/[name].[contenthash:8].css', 51 | chunkFilename: 'static/css/[name].[contenthash:8].chunk.css', 52 | }), 53 | // Replace HtmlWebpackPlugin to use template processing 54 | new HtmlWebpackPlugin({ 55 | inject: true, 56 | template: path.resolve(__dirname, 'public/index.html'), 57 | templateParameters: { 58 | PUBLIC_URL: '/portal', 59 | }, 60 | minify: { 61 | removeComments: true, 62 | collapseWhitespace: true, 63 | removeRedundantAttributes: true, 64 | useShortDoctype: true, 65 | removeEmptyAttributes: true, 66 | removeStyleLinkTypeAttributes: true, 67 | keepClosingSlash: true, 68 | minifyJS: true, 69 | minifyCSS: true, 70 | minifyURLs: true, 71 | }, 72 | }), 73 | ], 74 | module: { 75 | rules: [ 76 | { 77 | test: /\.css$/, 78 | use: [ 79 | MiniCssExtractPlugin.loader, 80 | 'css-loader', 81 | ], 82 | }, 83 | ], 84 | }, 85 | }); --------------------------------------------------------------------------------