diff --git a/README.md b/README.md index 7f99d59..ea12b42 100644 --- a/README.md +++ b/README.md @@ -12,21 +12,24 @@ This plugin is still work in progress and not yet ready for production use. ## Development -Start up a Mattermost development server -```bash -make start-server -``` -go to http://localhost:8065 and do the initial setup. Create a [personal access token](https://docs.mattermost.com/developer/personal-access-tokens.html) -and set the environment variable `MM_ADMIN_TOKEN` to the token value. Run -``` -export MM_ADMIN_TOKEN= -make watch -``` -to start the plugin in development mode. This will watch for webapp changes and automatically rebuild the plugin. - -Configure the plugin in mattermost, go to System Console -> Plugins -> Parabol and enter -- Parabol URL: http://host.docker.internal:3001 -- Parabol API Token: get this from MATTERMOST_SECRET environment of your Parabol instance +- Create a `.env` file in the root of the project: + ```bash + cp .env.example .env + ``` +- Start up a Mattermost development server + ```bash + make start-server + ``` + go to http://localhost:8065 and do the initial setup. Create a [personal access token](https://docs.mattermost.com/developer/personal-access-tokens.html) + and set the environment variable `MM_ADMIN_TOKEN` to the token value. +- Run + ``` + make watch + ``` + to start the plugin in development mode. This will watch for webapp changes and automatically rebuild the plugin. +- Configure the plugin in mattermost, go to System Console -> Plugins -> Parabol and enter + - Parabol URL: http://host.docker.internal:3001 + - Parabol API Token: get this from MATTERMOST_SECRET environment of your Parabol instance ### Releasing new versions diff --git a/webapp/package-lock.json b/webapp/package-lock.json index 3068957..3640215 100644 --- a/webapp/package-lock.json +++ b/webapp/package-lock.json @@ -5,6 +5,10 @@ "packages": { "": { "dependencies": { + "@mattermost/compass-icons": "0.1.47", + "@mui/base": "5.0.0-alpha.127", + "@mui/material": "5.11.16", + "@mui/styled-engine-sc": "5.11.11", "@reduxjs/toolkit": "1.9.7", "core-js": "3.33.3", "mattermost-redux": "5.33.1", @@ -14,6 +18,7 @@ "react-redux": "8.1.3", "react-select": "5.8.2", "redux": "4.2.1", + "styled-components": "5.3.6", "typescript": "4.6.4" }, "devDependencies": { @@ -46,6 +51,7 @@ "babel-eslint": "10.1.0", "babel-loader": "9.2.1", "babel-plugin-formatjs": "10.3.7", + "babel-plugin-styled-components": "2.1.1", "babel-plugin-typescript-to-proptypes": "2.0.0", "css-loader": "6.5.1", "enzyme": "3.11.0", @@ -2329,6 +2335,14 @@ "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.2.tgz", "integrity": "sha512-MyqliTZGuOm3+5ZRSaaBGP3USLw6+EGykkwZns2EPC5g8jJ4z9OrdZY9apkl3+UP9+sdz76YYkwCKP5gh8iY3g==" }, + "node_modules/@emotion/is-prop-valid": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.3.1.tgz", + "integrity": "sha512-/ACwoqx7XQi9knQs/G0qKvv5teDMhD7bXYns9N/wM8ah8iNb8jZ2uNO0YOgiq2o2poIvVtJS2YALasQuMSQ7Kw==", + "dependencies": { + "@emotion/memoize": "^0.9.0" + } + }, "node_modules/@emotion/memoize": { "version": "0.9.0", "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.9.0.tgz", @@ -2390,6 +2404,11 @@ "resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.4.0.tgz", "integrity": "sha512-fTBW9/8r2w3dXWYM4HCB1Rdp8NLibOw2+XELH5m5+AkWiL/KqYX6dc0kKYlaYyKjrQ6ds33MCdMPEwgs2z1rqg==" }, + "node_modules/@emotion/stylis": { + "version": "0.8.5", + "resolved": "https://registry.npmjs.org/@emotion/stylis/-/stylis-0.8.5.tgz", + "integrity": "sha512-h6KtPihKFn3T9fuIrwvXXUOwlx3rfUvfZIcP5a6rh8Y7zjE3O06hT5Ss4S/YI1AYhuZ1kjaE/5EaOOI2NqSylQ==" + }, "node_modules/@emotion/unitless": { "version": "0.10.0", "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.10.0.tgz", @@ -3870,6 +3889,11 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@mattermost/compass-icons": { + "version": "0.1.47", + "resolved": "https://registry.npmjs.org/@mattermost/compass-icons/-/compass-icons-0.1.47.tgz", + "integrity": "sha512-vI4j1m/B9qQu51g5YnMQBft8gDh/ZWp3JHBgZXJwb522uY2o6WjwOpCbFwdJxWnD2uq/N2D9wDNmdutJNddf+w==" + }, "node_modules/@mattermost/types": { "version": "6.7.0-0", "resolved": "https://registry.npmjs.org/@mattermost/types/-/types-6.7.0-0.tgz", @@ -3884,6 +3908,461 @@ } } }, + "node_modules/@mui/base": { + "version": "5.0.0-alpha.127", + "resolved": "https://registry.npmjs.org/@mui/base/-/base-5.0.0-alpha.127.tgz", + "integrity": "sha512-FoRQd0IOH9MnfyL5yXssyQRnC4vXI+1bwkU1idr+wNkP1ZfxE+JsThHcfl1dy5azLssVUGTtQFD9edQLdbyJog==", + "dependencies": { + "@babel/runtime": "^7.21.0", + "@emotion/is-prop-valid": "^1.2.0", + "@mui/types": "^7.2.4", + "@mui/utils": "^5.12.0", + "@popperjs/core": "^2.11.7", + "clsx": "^1.2.1", + "prop-types": "^15.8.1", + "react-is": "^18.2.0" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui" + }, + "peerDependencies": { + "@types/react": "^17.0.0 || ^18.0.0", + "react": "^17.0.0 || ^18.0.0", + "react-dom": "^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/base/node_modules/@babel/runtime": { + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.0.tgz", + "integrity": "sha512-FDSOghenHTiToteC/QRlv2q3DhPZ/oOXTBoirfWNx1Cx3TMVcGWQtMMmQcSvb/JjpNeGzx8Pq/b4fKEJuWm1sw==", + "dependencies": { + "regenerator-runtime": "^0.14.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@mui/base/node_modules/react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==" + }, + "node_modules/@mui/base/node_modules/regenerator-runtime": { + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", + "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==" + }, + "node_modules/@mui/core-downloads-tracker": { + "version": "5.16.7", + "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-5.16.7.tgz", + "integrity": "sha512-RtsCt4Geed2/v74sbihWzzRs+HsIQCfclHeORh5Ynu2fS4icIKozcSubwuG7vtzq2uW3fOR1zITSP84TNt2GoQ==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + } + }, + "node_modules/@mui/material": { + "version": "5.11.16", + "resolved": "https://registry.npmjs.org/@mui/material/-/material-5.11.16.tgz", + "integrity": "sha512-++glQqbZ3rMzOWB77yOvqRG+k8+scYTUKVWZpWff+GWsf6L10g9L2wgRhhAS8bDLuxCbXZlPNbSZowXDDw6z6Q==", + "dependencies": { + "@babel/runtime": "^7.21.0", + "@mui/base": "5.0.0-alpha.124", + "@mui/core-downloads-tracker": "^5.11.16", + "@mui/system": "^5.11.16", + "@mui/types": "^7.2.3", + "@mui/utils": "^5.11.13", + "@types/react-transition-group": "^4.4.5", + "clsx": "^1.2.1", + "csstype": "^3.1.2", + "prop-types": "^15.8.1", + "react-is": "^18.2.0", + "react-transition-group": "^4.4.5" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui" + }, + "peerDependencies": { + "@emotion/react": "^11.5.0", + "@emotion/styled": "^11.3.0", + "@types/react": "^17.0.0 || ^18.0.0", + "react": "^17.0.0 || ^18.0.0", + "react-dom": "^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@emotion/react": { + "optional": true + }, + "@emotion/styled": { + "optional": true + }, + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/material/node_modules/@babel/runtime": { + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.0.tgz", + "integrity": "sha512-FDSOghenHTiToteC/QRlv2q3DhPZ/oOXTBoirfWNx1Cx3TMVcGWQtMMmQcSvb/JjpNeGzx8Pq/b4fKEJuWm1sw==", + "dependencies": { + "regenerator-runtime": "^0.14.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@mui/material/node_modules/@mui/base": { + "version": "5.0.0-alpha.124", + "resolved": "https://registry.npmjs.org/@mui/base/-/base-5.0.0-alpha.124.tgz", + "integrity": "sha512-I6M+FrjRCybQCr8I8JTu6L2MkUobSQFgNIpOJyDNKL5zq/73LvZIQXvsKumAzthVGvI1PYaarM9vGDrDYbumKA==", + "dependencies": { + "@babel/runtime": "^7.21.0", + "@emotion/is-prop-valid": "^1.2.0", + "@mui/types": "^7.2.3", + "@mui/utils": "^5.11.13", + "@popperjs/core": "^2.11.7", + "clsx": "^1.2.1", + "prop-types": "^15.8.1", + "react-is": "^18.2.0" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui" + }, + "peerDependencies": { + "@types/react": "^17.0.0 || ^18.0.0", + "react": "^17.0.0 || ^18.0.0", + "react-dom": "^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/material/node_modules/@types/react-transition-group": { + "version": "4.4.11", + "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.11.tgz", + "integrity": "sha512-RM05tAniPZ5DZPzzNFP+DmrcOdD0efDUxMy3145oljWSl3x9ZV5vhme98gTxFrj2lhXvmGNnUiuDyJgY9IKkNA==", + "dependencies": { + "@types/react": "*" + } + }, + "node_modules/@mui/material/node_modules/dom-helpers": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz", + "integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==", + "dependencies": { + "@babel/runtime": "^7.8.7", + "csstype": "^3.0.2" + } + }, + "node_modules/@mui/material/node_modules/react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==" + }, + "node_modules/@mui/material/node_modules/react-transition-group": { + "version": "4.4.5", + "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz", + "integrity": "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==", + "dependencies": { + "@babel/runtime": "^7.5.5", + "dom-helpers": "^5.0.1", + "loose-envify": "^1.4.0", + "prop-types": "^15.6.2" + }, + "peerDependencies": { + "react": ">=16.6.0", + "react-dom": ">=16.6.0" + } + }, + "node_modules/@mui/material/node_modules/regenerator-runtime": { + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", + "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==" + }, + "node_modules/@mui/private-theming": { + "version": "5.16.6", + "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-5.16.6.tgz", + "integrity": "sha512-rAk+Rh8Clg7Cd7shZhyt2HGTTE5wYKNSJ5sspf28Fqm/PZ69Er9o6KX25g03/FG2dfpg5GCwZh/xOojiTfm3hw==", + "dependencies": { + "@babel/runtime": "^7.23.9", + "@mui/utils": "^5.16.6", + "prop-types": "^15.8.1" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@types/react": "^17.0.0 || ^18.0.0", + "react": "^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/private-theming/node_modules/@babel/runtime": { + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.0.tgz", + "integrity": "sha512-FDSOghenHTiToteC/QRlv2q3DhPZ/oOXTBoirfWNx1Cx3TMVcGWQtMMmQcSvb/JjpNeGzx8Pq/b4fKEJuWm1sw==", + "dependencies": { + "regenerator-runtime": "^0.14.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@mui/private-theming/node_modules/regenerator-runtime": { + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", + "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==" + }, + "node_modules/@mui/styled-engine": { + "version": "5.16.6", + "resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-5.16.6.tgz", + "integrity": "sha512-zaThmS67ZmtHSWToTiHslbI8jwrmITcN93LQaR2lKArbvS7Z3iLkwRoiikNWutx9MBs8Q6okKvbZq1RQYB3v7g==", + "dependencies": { + "@babel/runtime": "^7.23.9", + "@emotion/cache": "^11.11.0", + "csstype": "^3.1.3", + "prop-types": "^15.8.1" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@emotion/react": "^11.4.1", + "@emotion/styled": "^11.3.0", + "react": "^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@emotion/react": { + "optional": true + }, + "@emotion/styled": { + "optional": true + } + } + }, + "node_modules/@mui/styled-engine-sc": { + "version": "5.11.11", + "resolved": "https://registry.npmjs.org/@mui/styled-engine-sc/-/styled-engine-sc-5.11.11.tgz", + "integrity": "sha512-6+HsfcKHlhjQklDoEup7Itl+Xgn+BCsqEpIdIIhlxED4YlOZ38xghxIKrx78XFZznTorbhAspUgDDKIaB5vDMg==", + "dependencies": { + "@babel/runtime": "^7.21.0", + "prop-types": "^15.8.1" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui" + }, + "peerDependencies": { + "@types/styled-components": "^5.1.14", + "styled-components": "^5.3.1" + }, + "peerDependenciesMeta": { + "@types/styled-components": { + "optional": true + } + } + }, + "node_modules/@mui/styled-engine-sc/node_modules/@babel/runtime": { + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.0.tgz", + "integrity": "sha512-FDSOghenHTiToteC/QRlv2q3DhPZ/oOXTBoirfWNx1Cx3TMVcGWQtMMmQcSvb/JjpNeGzx8Pq/b4fKEJuWm1sw==", + "dependencies": { + "regenerator-runtime": "^0.14.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@mui/styled-engine-sc/node_modules/regenerator-runtime": { + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", + "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==" + }, + "node_modules/@mui/styled-engine/node_modules/@babel/runtime": { + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.0.tgz", + "integrity": "sha512-FDSOghenHTiToteC/QRlv2q3DhPZ/oOXTBoirfWNx1Cx3TMVcGWQtMMmQcSvb/JjpNeGzx8Pq/b4fKEJuWm1sw==", + "dependencies": { + "regenerator-runtime": "^0.14.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@mui/styled-engine/node_modules/regenerator-runtime": { + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", + "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==" + }, + "node_modules/@mui/system": { + "version": "5.16.7", + "resolved": "https://registry.npmjs.org/@mui/system/-/system-5.16.7.tgz", + "integrity": "sha512-Jncvs/r/d/itkxh7O7opOunTqbbSSzMTHzZkNLM+FjAOg+cYAZHrPDlYe1ZGKUYORwwb2XexlWnpZp0kZ4AHuA==", + "dependencies": { + "@babel/runtime": "^7.23.9", + "@mui/private-theming": "^5.16.6", + "@mui/styled-engine": "^5.16.6", + "@mui/types": "^7.2.15", + "@mui/utils": "^5.16.6", + "clsx": "^2.1.0", + "csstype": "^3.1.3", + "prop-types": "^15.8.1" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@emotion/react": "^11.5.0", + "@emotion/styled": "^11.3.0", + "@types/react": "^17.0.0 || ^18.0.0", + "react": "^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@emotion/react": { + "optional": true + }, + "@emotion/styled": { + "optional": true + }, + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/system/node_modules/@babel/runtime": { + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.0.tgz", + "integrity": "sha512-FDSOghenHTiToteC/QRlv2q3DhPZ/oOXTBoirfWNx1Cx3TMVcGWQtMMmQcSvb/JjpNeGzx8Pq/b4fKEJuWm1sw==", + "dependencies": { + "regenerator-runtime": "^0.14.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@mui/system/node_modules/clsx": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", + "engines": { + "node": ">=6" + } + }, + "node_modules/@mui/system/node_modules/regenerator-runtime": { + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", + "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==" + }, + "node_modules/@mui/types": { + "version": "7.2.19", + "resolved": "https://registry.npmjs.org/@mui/types/-/types-7.2.19.tgz", + "integrity": "sha512-6XpZEM/Q3epK9RN8ENoXuygnqUQxE+siN/6rGRi2iwJPgBUR25mphYQ9ZI87plGh58YoZ5pp40bFvKYOCDJ3tA==", + "peerDependencies": { + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/utils": { + "version": "5.16.6", + "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-5.16.6.tgz", + "integrity": "sha512-tWiQqlhxAt3KENNiSRL+DIn9H5xNVK6Jjf70x3PnfQPz1MPBdh7yyIcAyVBT9xiw7hP3SomRhPR7hzBMBCjqEA==", + "dependencies": { + "@babel/runtime": "^7.23.9", + "@mui/types": "^7.2.15", + "@types/prop-types": "^15.7.12", + "clsx": "^2.1.1", + "prop-types": "^15.8.1", + "react-is": "^18.3.1" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@types/react": "^17.0.0 || ^18.0.0", + "react": "^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/utils/node_modules/@babel/runtime": { + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.0.tgz", + "integrity": "sha512-FDSOghenHTiToteC/QRlv2q3DhPZ/oOXTBoirfWNx1Cx3TMVcGWQtMMmQcSvb/JjpNeGzx8Pq/b4fKEJuWm1sw==", + "dependencies": { + "regenerator-runtime": "^0.14.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@mui/utils/node_modules/clsx": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", + "engines": { + "node": ">=6" + } + }, + "node_modules/@mui/utils/node_modules/react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==" + }, + "node_modules/@mui/utils/node_modules/regenerator-runtime": { + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", + "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==" + }, "node_modules/@nicolo-ribaudo/chokidar-2": { "version": "2.1.8-no-fsevents.3", "resolved": "https://registry.npmjs.org/@nicolo-ribaudo/chokidar-2/-/chokidar-2-2.1.8-no-fsevents.3.tgz", @@ -3926,6 +4405,15 @@ "node": ">= 8" } }, + "node_modules/@popperjs/core": { + "version": "2.11.8", + "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz", + "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/popperjs" + } + }, "node_modules/@react-native-community/cli": { "version": "12.1.1", "resolved": "https://registry.npmjs.org/@react-native-community/cli/-/cli-12.1.1.tgz", @@ -5413,9 +5901,9 @@ "dev": true }, "node_modules/@types/prop-types": { - "version": "15.7.11", - "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.11.tgz", - "integrity": "sha512-ga8y9v9uyeiLdpKddhxYQkxNDrfvuPrlFb0N1qnZZByvcElJaXthF1UhvCh9TLWJBEHeNtdnbysW7Y6Uq8CVng==" + "version": "15.7.13", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.13.tgz", + "integrity": "sha512-hCZTSvwbzWGvhqxp/RqVqwU999pBf2vp7hzIjiYOsl8wqOmUxkQ6ddw1cV3l8811+kdUFus/q4d1Y3E3SyEifA==" }, "node_modules/@types/react": { "version": "17.0.83", @@ -7036,6 +7524,26 @@ "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" } }, + "node_modules/babel-plugin-styled-components": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/babel-plugin-styled-components/-/babel-plugin-styled-components-2.1.1.tgz", + "integrity": "sha512-c8lJlszObVQPguHkI+akXv8+Jgb9Ccujx0EetL7oIvwU100LxO6XAGe45qry37wUL40a5U9f23SYrivro2XKhA==", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.16.0", + "@babel/helper-module-imports": "^7.16.0", + "babel-plugin-syntax-jsx": "^6.18.0", + "lodash": "^4.17.21", + "picomatch": "^2.3.0" + }, + "peerDependencies": { + "styled-components": ">= 2" + } + }, + "node_modules/babel-plugin-syntax-jsx": { + "version": "6.18.0", + "resolved": "https://registry.npmjs.org/babel-plugin-syntax-jsx/-/babel-plugin-syntax-jsx-6.18.0.tgz", + "integrity": "sha512-qrPaCSo9c8RHNRHIotaufGbuOBN8rtdC4QrrFFc43vyWCCz7Kl7GL1PGaXtMGQZUXrkCjNEgxDfmAuAabr/rlw==" + }, "node_modules/babel-plugin-syntax-trailing-function-commas": { "version": "7.0.0-beta.0", "resolved": "https://registry.npmjs.org/babel-plugin-syntax-trailing-function-commas/-/babel-plugin-syntax-trailing-function-commas-7.0.0-beta.0.tgz", @@ -7400,6 +7908,14 @@ "node": ">=6" } }, + "node_modules/camelize": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/camelize/-/camelize-1.0.1.tgz", + "integrity": "sha512-dU+Tx2fsypxTgtLoE36npi3UqcjSSMNYfkqgmoEhtZrraP5VWq0K7FkWVTYa8eMPtnU/G2txVsfdCJTn9uzpuQ==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/caniuse-lite": { "version": "1.0.30001680", "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001680.tgz", @@ -7664,6 +8180,14 @@ "node": ">=6" } }, + "node_modules/clsx": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-1.2.1.tgz", + "integrity": "sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==", + "engines": { + "node": ">=6" + } + }, "node_modules/co": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", @@ -7937,6 +8461,14 @@ "source-map-resolve": "^0.6.0" } }, + "node_modules/css-color-keywords": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/css-color-keywords/-/css-color-keywords-1.0.0.tgz", + "integrity": "sha512-FyyrDHZKEjXDpNJYvVsV960FiqQyXc/LlYmsxl2BcdMb2WPx0OGRVgTg55rPSyLSNMqP52R9r8geSp7apN3Ofg==", + "engines": { + "node": ">=4" + } + }, "node_modules/css-loader": { "version": "6.5.1", "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-6.5.1.tgz", @@ -7991,6 +8523,16 @@ "url": "https://github.com/sponsors/fb55" } }, + "node_modules/css-to-react-native": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/css-to-react-native/-/css-to-react-native-3.2.0.tgz", + "integrity": "sha512-e8RKaLXMOFii+02mOlqwjbD00KSEKqblnpO9e++1aXS1fPQOpS1YoqdVHBqPjHNoxeF2mimzVqawm2KCbEdtHQ==", + "dependencies": { + "camelize": "^1.0.0", + "css-color-keywords": "^1.0.0", + "postcss-value-parser": "^4.0.2" + } + }, "node_modules/css-what": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz", @@ -16984,8 +17526,7 @@ "node_modules/postcss-value-parser": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", - "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", - "dev": true + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" }, "node_modules/postcss/node_modules/nanoid": { "version": "3.3.7", @@ -18527,6 +19068,11 @@ "resolved": "https://registry.npmjs.org/shallow-equals/-/shallow-equals-1.0.0.tgz", "integrity": "sha512-xd/FKcdmfmMbyYCca3QTVEJtqUOGuajNzvAX6nt8dXILwjAIEkfHc4hI8/JMGApAmb7VeULO0Q30NTxnbH/15g==" }, + "node_modules/shallowequal": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/shallowequal/-/shallowequal-1.1.0.tgz", + "integrity": "sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ==" + }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -18920,6 +19466,60 @@ "webpack": "^5.0.0" } }, + "node_modules/styled-components": { + "version": "5.3.6", + "resolved": "https://registry.npmjs.org/styled-components/-/styled-components-5.3.6.tgz", + "integrity": "sha512-hGTZquGAaTqhGWldX7hhfzjnIYBZ0IXQXkCYdvF1Sq3DsUaLx6+NTHC5Jj1ooM2F68sBiVz3lvhfwQs/S3l6qg==", + "hasInstallScript": true, + "dependencies": { + "@babel/helper-module-imports": "^7.0.0", + "@babel/traverse": "^7.4.5", + "@emotion/is-prop-valid": "^1.1.0", + "@emotion/stylis": "^0.8.4", + "@emotion/unitless": "^0.7.4", + "babel-plugin-styled-components": ">= 1.12.0", + "css-to-react-native": "^3.0.0", + "hoist-non-react-statics": "^3.0.0", + "shallowequal": "^1.1.0", + "supports-color": "^5.5.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/styled-components" + }, + "peerDependencies": { + "react": ">= 16.8.0", + "react-dom": ">= 16.8.0", + "react-is": ">= 16.8.0" + } + }, + "node_modules/styled-components/node_modules/@emotion/unitless": { + "version": "0.7.5", + "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.7.5.tgz", + "integrity": "sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg==" + }, + "node_modules/styled-components/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "engines": { + "node": ">=4" + } + }, + "node_modules/styled-components/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/stylis": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.2.0.tgz", diff --git a/webapp/package.json b/webapp/package.json index b3aa64d..6a1cd5f 100644 --- a/webapp/package.json +++ b/webapp/package.json @@ -44,6 +44,7 @@ "@typescript-eslint/typescript-estree": "5.52.0", "babel-eslint": "10.1.0", "babel-loader": "9.2.1", + "babel-plugin-styled-components": "2.1.1", "babel-plugin-formatjs": "10.3.7", "babel-plugin-typescript-to-proptypes": "2.0.0", "css-loader": "6.5.1", @@ -76,17 +77,40 @@ "webpack-cli": "5.0.1" }, "dependencies": { + "@mattermost/compass-icons": "0.1.47", + "@mui/base": "5.0.0-alpha.127", + "@mui/material": "5.11.16", + "@mui/styled-engine-sc": "5.11.11", "@reduxjs/toolkit": "1.9.7", "core-js": "3.33.3", "mattermost-redux": "5.33.1", "react": "17.0.2", - "react-dom": "17.0.2", "react-bootstrap": "github:mattermost/react-bootstrap#d821e2b1db1059bd36112d7587fd1b0912b27626", + "react-dom": "17.0.2", "react-redux": "8.1.3", "react-select": "5.8.2", "redux": "4.2.1", + "styled-components": "5.3.6", "typescript": "4.6.4" }, + "overrides": { + "@mui/base": { + "react": "17.0.2", + "react-dom": "17.0.2" + }, + "enzyme-adapter-utils": { + "react": "17.0.2", + "react-dom": "17.0.2" + }, + "react-bootstrap": { + "react": "17.0.2", + "react-dom": "17.0.2" + }, + "react-select": { + "react": "17.0.2", + "react-dom": "17.0.2" + } + }, "jest": { "snapshotSerializers": [ "/node_modules/enzyme-to-json/serializer" diff --git a/webapp/src/api.ts b/webapp/src/api.tsx similarity index 79% rename from webapp/src/api.ts rename to webapp/src/api.tsx index c07e320..d334d16 100644 --- a/webapp/src/api.ts +++ b/webapp/src/api.tsx @@ -7,7 +7,7 @@ import {getPluginServerRoute} from './selectors' const {id} = manifest -type Template = { +type MeetingTemplate = { id: string name: string type: string @@ -24,18 +24,16 @@ export type MeetingSettings = { disableAnonymity?: boolean } +type TeamMember = { + id: string + email: string +} + type Team = { id: string name: string orgId: string - retroSettings: MeetingSettings - pokerSettings: MeetingSettings - actionSettings: MeetingSettings -} - -type MeetingTemplatesResponse = { - availableTemplates: Template[] - teams: Team[] + teamMembers: TeamMember[] } type ReflectPrompt = { @@ -91,15 +89,14 @@ FetchBaseQueryError export const api = createApi({ reducerPath: `plugins-${id}`, baseQuery, - tagTypes: ['MeetingTemplates', 'MeetingSettings'], + tagTypes: ['Teams', 'MeetingTemplates', 'MeetingSettings', 'Meetings'], endpoints: (builder) => ({ - getTemplates: builder.query({ + templates: builder.query({ query: () => ({ url: '/query/meetingTemplates', method: 'POST', }), }), - // teamId and meetingType are required for convenient cache updates setMeetingSettings: builder.mutation({ query: (variables) => ({ @@ -107,21 +104,15 @@ export const api = createApi({ method: 'POST', body: variables, }), - invalidatesTags: () => { - console.log('invalidating tags') - return ['MeetingSettings'] - }, + invalidatesTags: ['MeetingSettings'] }), - getMeetingSettings: builder.query({ + meetingSettings: builder.query({ query: (variables) => ({ - url: '/query/getMeetingSettings', + url: '/query/meetingSettings', method: 'POST', body: variables, }), - providesTags: () => { - console.log('providing tags') - return ['MeetingSettings'] - }, + providesTags: ['MeetingSettings'] }), startRetrospective: builder.mutation({ query: (variables) => ({ @@ -129,6 +120,7 @@ export const api = createApi({ method: 'POST', body: variables, }), + invalidatesTags: ['Meetings'] }), startCheckIn: builder.mutation({ query: (variables) => ({ @@ -136,6 +128,7 @@ export const api = createApi({ method: 'POST', body: variables, }), + invalidatesTags: ['Meetings'] }), startSprintPoker: builder.mutation({ query: (variables) => ({ @@ -143,6 +136,7 @@ export const api = createApi({ method: 'POST', body: variables, }), + invalidatesTags: ['Meetings'] }), startTeamPrompt: builder.mutation({ query: (variables) => ({ @@ -150,12 +144,14 @@ export const api = createApi({ method: 'POST', body: variables, }), + invalidatesTags: ['Meetings'] }), - getActiveMeetings: builder.query({ + activeMeetings: builder.query({ query: () => ({ - url: '/query/getActiveMeetings', + url: '/query/activeMeetings', method: 'POST', }), + providesTags: ['Meetings'] }), createReflection: builder.mutation({ query: (variables) => ({ @@ -164,25 +160,35 @@ export const api = createApi({ body: variables, }), }), + teams: builder.query({ + query: () => ({ + url: '/query/teams', + method: 'POST', + }), + providesTags: ['Teams'], + }), linkedTeams: builder.query({ query: ({channelId}) => ({ url: `/linkedTeams/${channelId}`, method: 'GET', }), + providesTags: ['Teams'], }), linkTeam: builder.mutation({ query: ({channelId, teamId}) => ({ url: `/linkTeam/${channelId}/${teamId}`, method: 'POST', }), + invalidatesTags: ['Teams'], }), unlinkTeam: builder.mutation({ query: ({channelId, teamId}) => ({ url: `/unlinkTeam/${channelId}/${teamId}`, method: 'POST', }), + invalidatesTags: ['Teams'], }), - getConfig: builder.query<{parabolURL: string}, void>({ + config: builder.query<{parabolURL: string}, void>({ query: () => ({ url: '/config', method: 'GET', @@ -196,17 +202,18 @@ export const isError = (result: any): result is {error: Error} => { } export const { - useGetTemplatesQuery, + useTemplatesQuery, useSetMeetingSettingsMutation, - useGetMeetingSettingsQuery, + useMeetingSettingsQuery, useStartRetrospectiveMutation, useStartTeamPromptMutation, useStartCheckInMutation, useStartSprintPokerMutation, - useGetActiveMeetingsQuery, + useActiveMeetingsQuery, useCreateReflectionMutation, useLinkedTeamsQuery, + useTeamsQuery, useLinkTeamMutation, useUnlinkTeamMutation, - useGetConfigQuery, + useConfigQuery, } = api diff --git a/webapp/src/components/link_team_modal/link_team_modal.tsx b/webapp/src/components/link_team_modal/link_team_modal.tsx index e89f9c6..ab25e9a 100644 --- a/webapp/src/components/link_team_modal/link_team_modal.tsx +++ b/webapp/src/components/link_team_modal/link_team_modal.tsx @@ -2,34 +2,27 @@ import React, {useEffect, useMemo} from 'react' import {Modal} from 'react-bootstrap' import {useDispatch, useSelector} from 'react-redux' -import {getCurrentChannelId} from 'mattermost-redux/selectors/entities/common' +import {getCurrentChannel} from 'mattermost-redux/selectors/entities/channels' -import {isError, useGetConfigQuery, useGetTemplatesQuery, useLinkedTeamsQuery, useLinkTeamMutation} from '../../api' +import {isError, useConfigQuery, useLinkTeamMutation} from '../../api' import {closeLinkTeamModal} from '../../reducers' import {getAssetsUrl, isLinkTeamModalVisible} from '../../selectors' import Select from '../select' +import useLinkedTeams from '../../hooks/use_linked_teams' const LinkTeamModal = () => { const isVisible = useSelector(isLinkTeamModalVisible) - const channelId = useSelector(getCurrentChannelId) - const {data: teamData, refetch: refetchTeams} = useGetTemplatesQuery() - const {data: linkedTeamIds, refetch: refetchLinkedTeams} = useLinkedTeamsQuery({channelId}) - const {data: config} = useGetConfigQuery() + const {unlinkedTeams, refetch: refetchUnlinkedTeams} = useLinkedTeams() + const channel = useSelector(getCurrentChannel) + const {id: channelId, display_name: channelName} = channel + const {data: config} = useConfigQuery() useEffect(() => { if (isVisible) { - refetchTeams() - refetchLinkedTeams() + refetchUnlinkedTeams() } - }, [isVisible, refetchTeams, refetchLinkedTeams]) + }, [isVisible, refetchUnlinkedTeams]) - const unlinkedTeams = useMemo(() => { - if (!teamData || !linkedTeamIds) { - return null - } - const {teams} = teamData - return teams.filter((team) => !linkedTeamIds.includes(team.id)) - }, [teamData, linkedTeamIds]) const [selectedTeam, setSelectedTeam] = React.useState[number] | null>(null) const [linkTeam] = useLinkTeamMutation() @@ -81,7 +74,7 @@ const LinkTeamModal = () => { height={36} src={`${assetsPath}/parabol.png`} /> - {'Link a Parabol Team to this Channel'} + {` Link a Parabol Team to ${channelName}`} diff --git a/webapp/src/components/menu/index.tsx b/webapp/src/components/menu/index.tsx new file mode 100644 index 0000000..de29f0c --- /dev/null +++ b/webapp/src/components/menu/index.tsx @@ -0,0 +1,56 @@ +import {IconButton, Menu, MenuItem} from "@mui/material" +import { + DotsVerticalIcon, +} from '@mattermost/compass-icons/components' +import React from "react" + +type MenuOption = { + label: string + onClick: () => void +} + +type Props = { + options: MenuOption[] +} + +const MoreMenu = ({options}: Props) => { + const [anchorEl, setAnchorEl] = React.useState(null) + const open = Boolean(anchorEl) + const handleOpen= (event: React.MouseEvent) => { + setAnchorEl(event.currentTarget) + } + const handleClose = () => { + setAnchorEl(null) + } + return ( +
+ + + + + {options.map(({label, onClick}) => ( + {onClick(); handleClose()}}> + {label} + + ))} + +
+ ) +} + +export default MoreMenu diff --git a/webapp/src/components/push_reflection/push_reflection_modal.tsx b/webapp/src/components/push_reflection/push_reflection_modal.tsx index 0650eaa..3d765be 100644 --- a/webapp/src/components/push_reflection/push_reflection_modal.tsx +++ b/webapp/src/components/push_reflection/push_reflection_modal.tsx @@ -6,7 +6,7 @@ import {getPost} from 'mattermost-redux/selectors/entities/posts' import {GlobalState} from 'mattermost-redux/types/store' -import {useCreateReflectionMutation, useGetActiveMeetingsQuery} from '../../api' +import {useCreateReflectionMutation, useActiveMeetingsQuery} from '../../api' import {closePushPostAsReflection} from '../../reducers' import {getAssetsUrl, getPostURL, pushPostAsReflection} from '../../selectors' import Select from '../select' @@ -19,7 +19,7 @@ const PushReflectionModal = () => { const post = useSelector((state: GlobalState) => getPost(state, postId!)) const postUrl = useSelector((state: GlobalState) => getPostURL(state, postId!)) - const {data, isLoading, refetch} = useGetActiveMeetingsQuery() + const {data, isLoading, refetch} = useActiveMeetingsQuery() useEffect(() => { if (postId) { refetch() @@ -101,7 +101,7 @@ const PushReflectionModal = () => { height={36} src={`${assetsPath}/parabol.png`} /> - {'Start a Parabol Activity'} + {' Add Comment to Parabol Activity'} @@ -143,7 +143,7 @@ const PushReflectionModal = () => { )} {data && (<> { value={selectedTeam} onChange={setSelectedTeam} /> + } + {isTemplatesLoading && } + {templatesError &&
Loading templates failed, try refreshing the page
} + {selectedTeam && availableTemplates && (<>