chore: add slot to allow additional course app plugin (#941)
* chore: add @openedx/frontend-plugin-framework chore: move plugin page setting button to a props chore: split out app setting modal for reusability chore: add implementation of WTC plugin chore: update app setting form chore: implement the plugin form with mock chore: follow the UI design chore: remove translation plugin and move it into frontend-plugin instead * chore: add eslint ignore for env.config.jsx * chore: update package-lock.json
This commit is contained in:
@@ -1,4 +1,6 @@
|
|||||||
coverage/*
|
coverage/*
|
||||||
dist/
|
dist/
|
||||||
node_modules/
|
node_modules/
|
||||||
jest.config.js
|
jest.config.js
|
||||||
|
env.config.jsx
|
||||||
|
example.env.config.jsx
|
||||||
|
|||||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -26,3 +26,6 @@ temp/babel-plugin-react-intl
|
|||||||
|
|
||||||
# Messages .json files fetched by atlas
|
# Messages .json files fetched by atlas
|
||||||
src/i18n/messages/
|
src/i18n/messages/
|
||||||
|
|
||||||
|
# environment js config
|
||||||
|
env.config.jsx
|
||||||
|
|||||||
24
example.env.config.jsx
Normal file
24
example.env.config.jsx
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
import WholeCourseTranslation from '@edx/course-app-translation-plugin';
|
||||||
|
import { PLUGIN_OPERATIONS, DIRECT_PLUGIN } from '@openedx/frontend-plugin-framework';
|
||||||
|
|
||||||
|
// Load environment variables from .env file
|
||||||
|
const config = {
|
||||||
|
...process.env,
|
||||||
|
pluginSlots: {
|
||||||
|
additional_course_plugin: {
|
||||||
|
plugins: [
|
||||||
|
{
|
||||||
|
op: PLUGIN_OPERATIONS.Insert,
|
||||||
|
widget: {
|
||||||
|
id: 'whole-course-translation-plugin',
|
||||||
|
type: DIRECT_PLUGIN,
|
||||||
|
priority: 1,
|
||||||
|
RenderWidget: WholeCourseTranslation,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export default config;
|
||||||
267
package-lock.json
generated
267
package-lock.json
generated
@@ -37,6 +37,7 @@
|
|||||||
"@openedx-plugins/course-app-teams": "file:plugins/course-apps/teams",
|
"@openedx-plugins/course-app-teams": "file:plugins/course-apps/teams",
|
||||||
"@openedx-plugins/course-app-wiki": "file:plugins/course-apps/wiki",
|
"@openedx-plugins/course-app-wiki": "file:plugins/course-apps/wiki",
|
||||||
"@openedx-plugins/course-app-xpert_unit_summary": "file:plugins/course-apps/xpert_unit_summary",
|
"@openedx-plugins/course-app-xpert_unit_summary": "file:plugins/course-apps/xpert_unit_summary",
|
||||||
|
"@openedx/frontend-plugin-framework": "^1.1.0",
|
||||||
"@openedx/paragon": "^22.2.1",
|
"@openedx/paragon": "^22.2.1",
|
||||||
"@reduxjs/toolkit": "1.9.7",
|
"@reduxjs/toolkit": "1.9.7",
|
||||||
"@tanstack/react-query": "4.36.1",
|
"@tanstack/react-query": "4.36.1",
|
||||||
@@ -50,10 +51,11 @@
|
|||||||
"lodash": "4.17.21",
|
"lodash": "4.17.21",
|
||||||
"meilisearch": "^0.38.0",
|
"meilisearch": "^0.38.0",
|
||||||
"moment": "2.29.4",
|
"moment": "2.29.4",
|
||||||
"prop-types": "15.7.2",
|
"prop-types": "^15.8.1",
|
||||||
"react": "17.0.2",
|
"react": "17.0.2",
|
||||||
"react-datepicker": "^4.13.0",
|
"react-datepicker": "^4.13.0",
|
||||||
"react-dom": "17.0.2",
|
"react-dom": "17.0.2",
|
||||||
|
"react-error-boundary": "^4.0.13",
|
||||||
"react-helmet": "^6.1.0",
|
"react-helmet": "^6.1.0",
|
||||||
"react-redux": "7.2.9",
|
"react-redux": "7.2.9",
|
||||||
"react-responsive": "9.0.2",
|
"react-responsive": "9.0.2",
|
||||||
@@ -2377,16 +2379,6 @@
|
|||||||
"react-dom": "^16.9.0 || ^17.0.0"
|
"react-dom": "^16.9.0 || ^17.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@edx/frontend-component-ai-translations/node_modules/prop-types": {
|
|
||||||
"version": "15.8.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
|
|
||||||
"integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==",
|
|
||||||
"dependencies": {
|
|
||||||
"loose-envify": "^1.4.0",
|
|
||||||
"object-assign": "^4.1.1",
|
|
||||||
"react-is": "^16.13.1"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@edx/frontend-component-ai-translations/node_modules/react-responsive": {
|
"node_modules/@edx/frontend-component-ai-translations/node_modules/react-responsive": {
|
||||||
"version": "8.2.0",
|
"version": "8.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/react-responsive/-/react-responsive-8.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/react-responsive/-/react-responsive-8.2.0.tgz",
|
||||||
@@ -3495,16 +3487,6 @@
|
|||||||
"react": ">=16.3"
|
"react": ">=16.3"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@fortawesome/react-fontawesome/node_modules/prop-types": {
|
|
||||||
"version": "15.8.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
|
|
||||||
"integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==",
|
|
||||||
"dependencies": {
|
|
||||||
"loose-envify": "^1.4.0",
|
|
||||||
"object-assign": "^4.1.1",
|
|
||||||
"react-is": "^16.13.1"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@fullhuman/postcss-purgecss": {
|
"node_modules/@fullhuman/postcss-purgecss": {
|
||||||
"version": "5.0.0",
|
"version": "5.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/@fullhuman/postcss-purgecss/-/postcss-purgecss-5.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/@fullhuman/postcss-purgecss/-/postcss-purgecss-5.0.0.tgz",
|
||||||
@@ -4705,6 +4687,174 @@
|
|||||||
"url": "https://github.com/sponsors/sindresorhus"
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@openedx/frontend-plugin-framework": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@openedx/frontend-plugin-framework/-/frontend-plugin-framework-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-WT2Up9UR1ctQLeN43iCwEF6BOmU3zxL+s+clDeS4zp/aPue3v5ObTbzRsuJGlFg9gA6lY1C6Yh2+QQ6w2sK2aw==",
|
||||||
|
"dependencies": {
|
||||||
|
"@edx/brand": "npm:@openedx/brand-openedx@^1.2.2",
|
||||||
|
"@edx/frontend-component-footer": "13.0.3",
|
||||||
|
"@edx/frontend-component-header": "5.0.2",
|
||||||
|
"classnames": "^2.3.2",
|
||||||
|
"core-js": "3.36.0",
|
||||||
|
"react-redux": "7.2.9",
|
||||||
|
"redux": "4.2.1",
|
||||||
|
"regenerator-runtime": "0.14.1"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@edx/frontend-platform": "^7.0.0",
|
||||||
|
"@openedx/paragon": "^21.0.0 || ^22.0.0",
|
||||||
|
"prop-types": "^15.8.0",
|
||||||
|
"react": "^17.0.0",
|
||||||
|
"react-dom": "^17.0.0",
|
||||||
|
"react-error-boundary": "^4.0.11"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@openedx/frontend-plugin-framework/node_modules/@edx/frontend-component-footer": {
|
||||||
|
"version": "13.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@edx/frontend-component-footer/-/frontend-component-footer-13.0.3.tgz",
|
||||||
|
"integrity": "sha512-09vX6qC7AcDwG02qhBzKr4x58hpe9FXZrA9ui2cJnsG53pKaNL+wvOSRtDUBNexCf+y/iPg+8RgR+4alkzhZhw==",
|
||||||
|
"dependencies": {
|
||||||
|
"@fortawesome/fontawesome-svg-core": "6.5.1",
|
||||||
|
"@fortawesome/free-brands-svg-icons": "6.5.1",
|
||||||
|
"@fortawesome/free-regular-svg-icons": "6.5.1",
|
||||||
|
"@fortawesome/free-solid-svg-icons": "6.5.1",
|
||||||
|
"@fortawesome/react-fontawesome": "0.2.0",
|
||||||
|
"lodash": "^4.17.21"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@edx/frontend-platform": "^7.0.0",
|
||||||
|
"@openedx/paragon": ">= 21.11.3 < 23.0.0",
|
||||||
|
"prop-types": "^15.5.10",
|
||||||
|
"react": "^16.9.0 || ^17.0.0",
|
||||||
|
"react-dom": "^16.9.0 || ^17.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@openedx/frontend-plugin-framework/node_modules/@edx/frontend-component-header": {
|
||||||
|
"version": "5.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@edx/frontend-component-header/-/frontend-component-header-5.0.2.tgz",
|
||||||
|
"integrity": "sha512-73fNNc1X/tevb3/hw7+s22T+nPGlW1yXA7zsT9eRzdH7rBxONfp0Jz7yEdeBvTax9a96PaOht45DA6GX9eG4KA==",
|
||||||
|
"dependencies": {
|
||||||
|
"@fortawesome/fontawesome-svg-core": "6.5.1",
|
||||||
|
"@fortawesome/free-brands-svg-icons": "6.5.1",
|
||||||
|
"@fortawesome/free-regular-svg-icons": "6.5.1",
|
||||||
|
"@fortawesome/free-solid-svg-icons": "6.5.1",
|
||||||
|
"@fortawesome/react-fontawesome": "^0.2.0",
|
||||||
|
"axios-mock-adapter": "1.22.0",
|
||||||
|
"babel-polyfill": "6.26.0",
|
||||||
|
"react-responsive": "8.2.0",
|
||||||
|
"react-transition-group": "4.4.5"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@edx/frontend-platform": "^7.0.0",
|
||||||
|
"@openedx/paragon": ">= 21.5.7 < 23.0.0",
|
||||||
|
"prop-types": "^15.5.10",
|
||||||
|
"react": "^16.9.0 || ^17.0.0",
|
||||||
|
"react-dom": "^16.9.0 || ^17.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@openedx/frontend-plugin-framework/node_modules/@fortawesome/fontawesome-common-types": {
|
||||||
|
"version": "6.5.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-6.5.1.tgz",
|
||||||
|
"integrity": "sha512-GkWzv+L6d2bI5f/Vk6ikJ9xtl7dfXtoRu3YGE6nq0p/FFqA1ebMOAWg3XgRyb0I6LYyYkiAo+3/KrwuBp8xG7A==",
|
||||||
|
"hasInstallScript": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@openedx/frontend-plugin-framework/node_modules/@fortawesome/fontawesome-svg-core": {
|
||||||
|
"version": "6.5.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-6.5.1.tgz",
|
||||||
|
"integrity": "sha512-MfRCYlQPXoLlpem+egxjfkEuP9UQswTrlCOsknus/NcMoblTH2g0jPrapbcIb04KGA7E2GZxbAccGZfWoYgsrQ==",
|
||||||
|
"hasInstallScript": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@fortawesome/fontawesome-common-types": "6.5.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@openedx/frontend-plugin-framework/node_modules/@fortawesome/free-brands-svg-icons": {
|
||||||
|
"version": "6.5.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@fortawesome/free-brands-svg-icons/-/free-brands-svg-icons-6.5.1.tgz",
|
||||||
|
"integrity": "sha512-093l7DAkx0aEtBq66Sf19MgoZewv1zeY9/4C7vSKPO4qMwEsW/2VYTUTpBtLwfb9T2R73tXaRDPmE4UqLCYHfg==",
|
||||||
|
"hasInstallScript": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@fortawesome/fontawesome-common-types": "6.5.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@openedx/frontend-plugin-framework/node_modules/@fortawesome/free-regular-svg-icons": {
|
||||||
|
"version": "6.5.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@fortawesome/free-regular-svg-icons/-/free-regular-svg-icons-6.5.1.tgz",
|
||||||
|
"integrity": "sha512-m6ShXn+wvqEU69wSP84coxLbNl7sGVZb+Ca+XZq6k30SzuP3X4TfPqtycgUh9ASwlNh5OfQCd8pDIWxl+O+LlQ==",
|
||||||
|
"hasInstallScript": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@fortawesome/fontawesome-common-types": "6.5.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@openedx/frontend-plugin-framework/node_modules/@fortawesome/free-solid-svg-icons": {
|
||||||
|
"version": "6.5.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@fortawesome/free-solid-svg-icons/-/free-solid-svg-icons-6.5.1.tgz",
|
||||||
|
"integrity": "sha512-S1PPfU3mIJa59biTtXJz1oI0+KAXW6bkAb31XKhxdxtuXDiUIFsih4JR1v5BbxY7hVHsD1RKq+jRkVRaf773NQ==",
|
||||||
|
"hasInstallScript": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@fortawesome/fontawesome-common-types": "6.5.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@openedx/frontend-plugin-framework/node_modules/classnames": {
|
||||||
|
"version": "2.5.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/classnames/-/classnames-2.5.1.tgz",
|
||||||
|
"integrity": "sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow=="
|
||||||
|
},
|
||||||
|
"node_modules/@openedx/frontend-plugin-framework/node_modules/core-js": {
|
||||||
|
"version": "3.36.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/core-js/-/core-js-3.36.0.tgz",
|
||||||
|
"integrity": "sha512-mt7+TUBbTFg5+GngsAxeKBTl5/VS0guFeJacYge9OmHb+m058UwwIm41SE9T4Den7ClatV57B6TYTuJ0CX1MAw==",
|
||||||
|
"hasInstallScript": true,
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/core-js"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@openedx/frontend-plugin-framework/node_modules/react-responsive": {
|
||||||
|
"version": "8.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-responsive/-/react-responsive-8.2.0.tgz",
|
||||||
|
"integrity": "sha512-iagCqVrw4QSjhxKp3I/YK6+ODkWY6G+YPElvdYKiUUbywwh9Ds0M7r26Fj2/7dWFFbOpcGnJE6uE7aMck8j5Qg==",
|
||||||
|
"dependencies": {
|
||||||
|
"hyphenate-style-name": "^1.0.0",
|
||||||
|
"matchmediaquery": "^0.3.0",
|
||||||
|
"prop-types": "^15.6.1",
|
||||||
|
"shallow-equal": "^1.1.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.10"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": ">=16.8.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@openedx/frontend-plugin-framework/node_modules/redux": {
|
||||||
|
"version": "4.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/redux/-/redux-4.2.1.tgz",
|
||||||
|
"integrity": "sha512-LAUYz4lc+Do8/g7aeRa8JkyDErK6ekstQaqWQrNRW//MY1TvCEpMtpTWvlQ+FPbWCx+Xixu/6SHt5N0HR+SB4w==",
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/runtime": "^7.9.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@openedx/frontend-plugin-framework/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/@openedx/paragon": {
|
"node_modules/@openedx/paragon": {
|
||||||
"version": "22.2.1",
|
"version": "22.2.1",
|
||||||
"resolved": "https://registry.npmjs.org/@openedx/paragon/-/paragon-22.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/@openedx/paragon/-/paragon-22.2.1.tgz",
|
||||||
@@ -4824,16 +4974,6 @@
|
|||||||
"node": ">=10"
|
"node": ">=10"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@openedx/paragon/node_modules/prop-types": {
|
|
||||||
"version": "15.8.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
|
|
||||||
"integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==",
|
|
||||||
"dependencies": {
|
|
||||||
"loose-envify": "^1.4.0",
|
|
||||||
"object-assign": "^4.1.1",
|
|
||||||
"react-is": "^16.13.1"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@openedx/paragon/node_modules/react-responsive": {
|
"node_modules/@openedx/paragon/node_modules/react-responsive": {
|
||||||
"version": "8.2.0",
|
"version": "8.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/react-responsive/-/react-responsive-8.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/react-responsive/-/react-responsive-8.2.0.tgz",
|
||||||
@@ -5386,6 +5526,22 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@testing-library/react-hooks/node_modules/react-error-boundary": {
|
||||||
|
"version": "3.1.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-error-boundary/-/react-error-boundary-3.1.4.tgz",
|
||||||
|
"integrity": "sha512-uM9uPzZJTF6wRQORmSrvOIgt4lJ9MC1sNgEOj2XGsDTRE4kmpWxg7ENK9EWNKJRMAOY9z0MuF4yIfl6gp4sotA==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/runtime": "^7.12.5"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10",
|
||||||
|
"npm": ">=6"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": ">=16.13.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@testing-library/react/node_modules/@testing-library/dom": {
|
"node_modules/@testing-library/react/node_modules/@testing-library/dom": {
|
||||||
"version": "8.20.1",
|
"version": "8.20.1",
|
||||||
"resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-8.20.1.tgz",
|
"resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-8.20.1.tgz",
|
||||||
@@ -10062,16 +10218,6 @@
|
|||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/eslint-plugin-react/node_modules/prop-types": {
|
|
||||||
"version": "15.8.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
|
|
||||||
"integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==",
|
|
||||||
"dependencies": {
|
|
||||||
"loose-envify": "^1.4.0",
|
|
||||||
"object-assign": "^4.1.1",
|
|
||||||
"react-is": "^16.13.1"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/eslint-plugin-react/node_modules/resolve": {
|
"node_modules/eslint-plugin-react/node_modules/resolve": {
|
||||||
"version": "2.0.0-next.5",
|
"version": "2.0.0-next.5",
|
||||||
"resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.5.tgz",
|
"resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.5.tgz",
|
||||||
@@ -17422,13 +17568,13 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/prop-types": {
|
"node_modules/prop-types": {
|
||||||
"version": "15.7.2",
|
"version": "15.8.1",
|
||||||
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz",
|
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
|
||||||
"integrity": "sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==",
|
"integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"loose-envify": "^1.4.0",
|
"loose-envify": "^1.4.0",
|
||||||
"object-assign": "^4.1.1",
|
"object-assign": "^4.1.1",
|
||||||
"react-is": "^16.8.1"
|
"react-is": "^16.13.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/prop-types-extra": {
|
"node_modules/prop-types-extra": {
|
||||||
@@ -17878,28 +18024,13 @@
|
|||||||
"react": ">= 16.8 || 18.0.0"
|
"react": ">= 16.8 || 18.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/react-dropzone/node_modules/prop-types": {
|
|
||||||
"version": "15.8.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
|
|
||||||
"integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==",
|
|
||||||
"dependencies": {
|
|
||||||
"loose-envify": "^1.4.0",
|
|
||||||
"object-assign": "^4.1.1",
|
|
||||||
"react-is": "^16.13.1"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/react-error-boundary": {
|
"node_modules/react-error-boundary": {
|
||||||
"version": "3.1.4",
|
"version": "4.0.13",
|
||||||
"resolved": "https://registry.npmjs.org/react-error-boundary/-/react-error-boundary-3.1.4.tgz",
|
"resolved": "https://registry.npmjs.org/react-error-boundary/-/react-error-boundary-4.0.13.tgz",
|
||||||
"integrity": "sha512-uM9uPzZJTF6wRQORmSrvOIgt4lJ9MC1sNgEOj2XGsDTRE4kmpWxg7ENK9EWNKJRMAOY9z0MuF4yIfl6gp4sotA==",
|
"integrity": "sha512-b6PwbdSv8XeOSYvjt8LpgpKrZ0yGdtZokYwkwV2wlcZbxgopHX/hgPl5VgpnoVOWd868n1hktM8Qm4b+02MiLQ==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/runtime": "^7.12.5"
|
"@babel/runtime": "^7.12.5"
|
||||||
},
|
},
|
||||||
"engines": {
|
|
||||||
"node": ">=10",
|
|
||||||
"npm": ">=6"
|
|
||||||
},
|
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"react": ">=16.13.1"
|
"react": ">=16.13.1"
|
||||||
}
|
}
|
||||||
@@ -17996,16 +18127,6 @@
|
|||||||
"react": ">=0.14.0"
|
"react": ">=0.14.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/react-imask/node_modules/prop-types": {
|
|
||||||
"version": "15.8.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
|
|
||||||
"integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==",
|
|
||||||
"dependencies": {
|
|
||||||
"loose-envify": "^1.4.0",
|
|
||||||
"object-assign": "^4.1.1",
|
|
||||||
"react-is": "^16.13.1"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/react-intl": {
|
"node_modules/react-intl": {
|
||||||
"version": "6.6.4",
|
"version": "6.6.4",
|
||||||
"resolved": "https://registry.npmjs.org/react-intl/-/react-intl-6.6.4.tgz",
|
"resolved": "https://registry.npmjs.org/react-intl/-/react-intl-6.6.4.tgz",
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "fedx-scripts webpack",
|
"build": "fedx-scripts webpack",
|
||||||
"i18n_extract": "fedx-scripts formatjs extract",
|
"i18n_extract": "fedx-scripts formatjs extract",
|
||||||
"stylelint": "stylelint \"src/**/*.scss\" \"scss/**/*.scss\" --config .stylelintrc.json",
|
"stylelint": "stylelint \"plugins/**/*.scss\" \"src/**/*.scss\" \"scss/**/*.scss\" --config .stylelintrc.json",
|
||||||
"lint": "npm run stylelint && fedx-scripts eslint --ext .js --ext .jsx .",
|
"lint": "npm run stylelint && fedx-scripts eslint --ext .js --ext .jsx .",
|
||||||
"lint:fix": "npm run stylelint && fedx-scripts eslint --ext .js --ext .jsx . --fix",
|
"lint:fix": "npm run stylelint && fedx-scripts eslint --ext .js --ext .jsx . --fix",
|
||||||
"snapshot": "TZ=UTC fedx-scripts jest --updateSnapshot",
|
"snapshot": "TZ=UTC fedx-scripts jest --updateSnapshot",
|
||||||
@@ -64,6 +64,7 @@
|
|||||||
"@openedx-plugins/course-app-teams": "file:plugins/course-apps/teams",
|
"@openedx-plugins/course-app-teams": "file:plugins/course-apps/teams",
|
||||||
"@openedx-plugins/course-app-wiki": "file:plugins/course-apps/wiki",
|
"@openedx-plugins/course-app-wiki": "file:plugins/course-apps/wiki",
|
||||||
"@openedx-plugins/course-app-xpert_unit_summary": "file:plugins/course-apps/xpert_unit_summary",
|
"@openedx-plugins/course-app-xpert_unit_summary": "file:plugins/course-apps/xpert_unit_summary",
|
||||||
|
"@openedx/frontend-plugin-framework": "^1.1.0",
|
||||||
"@openedx/paragon": "^22.2.1",
|
"@openedx/paragon": "^22.2.1",
|
||||||
"@reduxjs/toolkit": "1.9.7",
|
"@reduxjs/toolkit": "1.9.7",
|
||||||
"@tanstack/react-query": "4.36.1",
|
"@tanstack/react-query": "4.36.1",
|
||||||
@@ -77,10 +78,11 @@
|
|||||||
"lodash": "4.17.21",
|
"lodash": "4.17.21",
|
||||||
"meilisearch": "^0.38.0",
|
"meilisearch": "^0.38.0",
|
||||||
"moment": "2.29.4",
|
"moment": "2.29.4",
|
||||||
"prop-types": "15.7.2",
|
"prop-types": "^15.8.1",
|
||||||
"react": "17.0.2",
|
"react": "17.0.2",
|
||||||
"react-datepicker": "^4.13.0",
|
"react-datepicker": "^4.13.0",
|
||||||
"react-dom": "17.0.2",
|
"react-dom": "17.0.2",
|
||||||
|
"react-error-boundary": "^4.0.13",
|
||||||
"react-helmet": "^6.1.0",
|
"react-helmet": "^6.1.0",
|
||||||
"react-redux": "7.2.9",
|
"react-redux": "7.2.9",
|
||||||
"react-responsive": "9.0.2",
|
"react-responsive": "9.0.2",
|
||||||
|
|||||||
@@ -1,11 +1,9 @@
|
|||||||
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
|
import { useIntl } from '@edx/frontend-platform/i18n';
|
||||||
import {
|
import {
|
||||||
ActionRow,
|
|
||||||
Alert,
|
Alert,
|
||||||
Badge,
|
Badge,
|
||||||
Form,
|
Form,
|
||||||
Hyperlink,
|
Hyperlink,
|
||||||
ModalDialog,
|
|
||||||
StatefulButton,
|
StatefulButton,
|
||||||
TransitionReplace,
|
TransitionReplace,
|
||||||
} from '@openedx/paragon';
|
} from '@openedx/paragon';
|
||||||
@@ -31,81 +29,10 @@ import { updateSavingStatus } from '../data/slice';
|
|||||||
import { updateAppStatus } from '../data/thunks';
|
import { updateAppStatus } from '../data/thunks';
|
||||||
import AppConfigFormDivider from '../discussions/app-config-form/apps/shared/AppConfigFormDivider';
|
import AppConfigFormDivider from '../discussions/app-config-form/apps/shared/AppConfigFormDivider';
|
||||||
import { PagesAndResourcesContext } from '../PagesAndResourcesProvider';
|
import { PagesAndResourcesContext } from '../PagesAndResourcesProvider';
|
||||||
|
import AppSettingsModalBase from './AppSettingsModalBase';
|
||||||
import messages from './messages';
|
import messages from './messages';
|
||||||
|
|
||||||
const AppSettingsForm = ({
|
|
||||||
formikProps, children, showForm,
|
|
||||||
}) => children && (
|
|
||||||
<TransitionReplace>
|
|
||||||
{showForm ? (
|
|
||||||
<React.Fragment key="app-enabled">
|
|
||||||
{children(formikProps)}
|
|
||||||
</React.Fragment>
|
|
||||||
) : (
|
|
||||||
<React.Fragment key="app-disabled" />
|
|
||||||
)}
|
|
||||||
</TransitionReplace>
|
|
||||||
);
|
|
||||||
|
|
||||||
AppSettingsForm.propTypes = {
|
|
||||||
// Ignore the warning here since we're just passing along the props as-is and the child component should validate
|
|
||||||
// eslint-disable-next-line react/forbid-prop-types
|
|
||||||
formikProps: PropTypes.object.isRequired,
|
|
||||||
showForm: PropTypes.bool.isRequired,
|
|
||||||
children: PropTypes.func,
|
|
||||||
};
|
|
||||||
|
|
||||||
AppSettingsForm.defaultProps = {
|
|
||||||
children: null,
|
|
||||||
};
|
|
||||||
|
|
||||||
const AppSettingsModalBase = ({
|
|
||||||
intl, title, onClose, variant, isMobile, children, footer,
|
|
||||||
}) => (
|
|
||||||
<ModalDialog
|
|
||||||
title={title}
|
|
||||||
isOpen
|
|
||||||
onClose={onClose}
|
|
||||||
size="lg"
|
|
||||||
variant={variant}
|
|
||||||
hasCloseButton={isMobile}
|
|
||||||
isFullscreenOnMobile
|
|
||||||
>
|
|
||||||
<ModalDialog.Header>
|
|
||||||
<ModalDialog.Title data-testid="modal-title">
|
|
||||||
{title}
|
|
||||||
</ModalDialog.Title>
|
|
||||||
</ModalDialog.Header>
|
|
||||||
<ModalDialog.Body>
|
|
||||||
{children}
|
|
||||||
</ModalDialog.Body>
|
|
||||||
<ModalDialog.Footer className="p-4">
|
|
||||||
<ActionRow>
|
|
||||||
<ModalDialog.CloseButton variant="tertiary">
|
|
||||||
{intl.formatMessage(messages.cancel)}
|
|
||||||
</ModalDialog.CloseButton>
|
|
||||||
{footer}
|
|
||||||
</ActionRow>
|
|
||||||
</ModalDialog.Footer>
|
|
||||||
</ModalDialog>
|
|
||||||
);
|
|
||||||
|
|
||||||
AppSettingsModalBase.propTypes = {
|
|
||||||
intl: intlShape.isRequired,
|
|
||||||
title: PropTypes.string.isRequired,
|
|
||||||
onClose: PropTypes.func.isRequired,
|
|
||||||
variant: PropTypes.oneOf(['default', 'dark']).isRequired,
|
|
||||||
isMobile: PropTypes.bool.isRequired,
|
|
||||||
children: PropTypes.node.isRequired,
|
|
||||||
footer: PropTypes.node,
|
|
||||||
};
|
|
||||||
|
|
||||||
AppSettingsModalBase.defaultProps = {
|
|
||||||
footer: null,
|
|
||||||
};
|
|
||||||
|
|
||||||
const AppSettingsModal = ({
|
const AppSettingsModal = ({
|
||||||
intl,
|
|
||||||
appId,
|
appId,
|
||||||
title,
|
title,
|
||||||
children,
|
children,
|
||||||
@@ -121,6 +48,7 @@ const AppSettingsModal = ({
|
|||||||
enableReinitialize,
|
enableReinitialize,
|
||||||
hideAppToggle,
|
hideAppToggle,
|
||||||
}) => {
|
}) => {
|
||||||
|
const { formatMessage } = useIntl();
|
||||||
const { courseId } = useContext(PagesAndResourcesContext);
|
const { courseId } = useContext(PagesAndResourcesContext);
|
||||||
const loadingStatus = useSelector(getLoadingStatus);
|
const loadingStatus = useSelector(getLoadingStatus);
|
||||||
const updateSettingsRequestStatus = useSelector(getSavingStatus);
|
const updateSettingsRequestStatus = useSelector(getSavingStatus);
|
||||||
@@ -199,14 +127,12 @@ const AppSettingsModal = ({
|
|||||||
onClose={onClose}
|
onClose={onClose}
|
||||||
variant={modalVariant}
|
variant={modalVariant}
|
||||||
isMobile={isMobile}
|
isMobile={isMobile}
|
||||||
isFullscreenOnMobile
|
|
||||||
intl={intl}
|
|
||||||
footer={(
|
footer={(
|
||||||
<StatefulButton
|
<StatefulButton
|
||||||
labels={{
|
labels={{
|
||||||
default: intl.formatMessage(messages.save),
|
default: formatMessage(messages.save),
|
||||||
pending: intl.formatMessage(messages.saving),
|
pending: formatMessage(messages.saving),
|
||||||
complete: intl.formatMessage(messages.saved),
|
complete: formatMessage(messages.saved),
|
||||||
}}
|
}}
|
||||||
state={submitButtonState}
|
state={submitButtonState}
|
||||||
onClick={handleFormikSubmit(formikProps)}
|
onClick={handleFormikSubmit(formikProps)}
|
||||||
@@ -216,9 +142,9 @@ const AppSettingsModal = ({
|
|||||||
{saveError && (
|
{saveError && (
|
||||||
<Alert variant="danger" icon={Info} ref={alertRef}>
|
<Alert variant="danger" icon={Info} ref={alertRef}>
|
||||||
<Alert.Heading>
|
<Alert.Heading>
|
||||||
{formikProps.errors.enabled?.title || intl.formatMessage(messages.errorSavingTitle)}
|
{formikProps.errors.enabled?.title || formatMessage(messages.errorSavingTitle)}
|
||||||
</Alert.Heading>
|
</Alert.Heading>
|
||||||
{formikProps.errors.enabled?.message || intl.formatMessage(messages.errorSavingMessage)}
|
{formikProps.errors.enabled?.message || formatMessage(messages.errorSavingMessage)}
|
||||||
</Alert>
|
</Alert>
|
||||||
)}
|
)}
|
||||||
{!hideAppToggle && (
|
{!hideAppToggle && (
|
||||||
@@ -233,7 +159,7 @@ const AppSettingsModal = ({
|
|||||||
{enableAppLabel}
|
{enableAppLabel}
|
||||||
{formikProps.values.enabled && (
|
{formikProps.values.enabled && (
|
||||||
<Badge className="ml-2" variant="success" data-testid="enable-badge">
|
<Badge className="ml-2" variant="success" data-testid="enable-badge">
|
||||||
{intl.formatMessage(messages.enabled)}
|
{formatMessage(messages.enabled)}
|
||||||
</Badge>
|
</Badge>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
@@ -249,9 +175,19 @@ const AppSettingsModal = ({
|
|||||||
{bodyChildren}
|
{bodyChildren}
|
||||||
{(formikProps.values.enabled || configureBeforeEnable) && children
|
{(formikProps.values.enabled || configureBeforeEnable) && children
|
||||||
&& <AppConfigFormDivider marginAdj={{ default: 0, sm: 0 }} />}
|
&& <AppConfigFormDivider marginAdj={{ default: 0, sm: 0 }} />}
|
||||||
<AppSettingsForm formikProps={formikProps} showForm={formikProps.values.enabled || configureBeforeEnable}>
|
{
|
||||||
{children}
|
children && (
|
||||||
</AppSettingsForm>
|
<TransitionReplace>
|
||||||
|
{formikProps.values.enabled || configureBeforeEnable ? (
|
||||||
|
<React.Fragment key="app-enabled">
|
||||||
|
{children(formikProps)}
|
||||||
|
</React.Fragment>
|
||||||
|
) : (
|
||||||
|
<React.Fragment key="app-disabled" />
|
||||||
|
)}
|
||||||
|
</TransitionReplace>
|
||||||
|
)
|
||||||
|
}
|
||||||
</AppSettingsModalBase>
|
</AppSettingsModalBase>
|
||||||
</Form>
|
</Form>
|
||||||
)}
|
)}
|
||||||
@@ -260,7 +196,6 @@ const AppSettingsModal = ({
|
|||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<AppSettingsModalBase
|
<AppSettingsModalBase
|
||||||
intl={intl}
|
|
||||||
title={title}
|
title={title}
|
||||||
isOpen
|
isOpen
|
||||||
onClose={onClose}
|
onClose={onClose}
|
||||||
@@ -277,7 +212,6 @@ const AppSettingsModal = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
AppSettingsModal.propTypes = {
|
AppSettingsModal.propTypes = {
|
||||||
intl: intlShape.isRequired,
|
|
||||||
title: PropTypes.string.isRequired,
|
title: PropTypes.string.isRequired,
|
||||||
appId: PropTypes.string.isRequired,
|
appId: PropTypes.string.isRequired,
|
||||||
children: PropTypes.func,
|
children: PropTypes.func,
|
||||||
@@ -306,4 +240,4 @@ AppSettingsModal.defaultProps = {
|
|||||||
hideAppToggle: false,
|
hideAppToggle: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default injectIntl(AppSettingsModal);
|
export default AppSettingsModal;
|
||||||
|
|||||||
@@ -0,0 +1,63 @@
|
|||||||
|
import { useIntl } from '@edx/frontend-platform/i18n';
|
||||||
|
import { ActionRow, ModalDialog } from '@openedx/paragon';
|
||||||
|
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
import messages from './messages';
|
||||||
|
|
||||||
|
const AppSettingsModalBase = ({
|
||||||
|
title,
|
||||||
|
onClose,
|
||||||
|
variant,
|
||||||
|
isMobile,
|
||||||
|
children,
|
||||||
|
footer,
|
||||||
|
isOpen,
|
||||||
|
}) => {
|
||||||
|
const { formatMessage } = useIntl();
|
||||||
|
return (
|
||||||
|
<ModalDialog
|
||||||
|
title={title}
|
||||||
|
isOpen={isOpen}
|
||||||
|
onClose={onClose}
|
||||||
|
size="lg"
|
||||||
|
variant={variant}
|
||||||
|
hasCloseButton={isMobile}
|
||||||
|
isFullscreenOnMobile
|
||||||
|
>
|
||||||
|
<ModalDialog.Header>
|
||||||
|
<ModalDialog.Title data-testid="modal-title">{title}</ModalDialog.Title>
|
||||||
|
</ModalDialog.Header>
|
||||||
|
<ModalDialog.Body>{children}</ModalDialog.Body>
|
||||||
|
<ModalDialog.Footer className="p-4">
|
||||||
|
<ActionRow>
|
||||||
|
<ModalDialog.CloseButton variant="tertiary">
|
||||||
|
{formatMessage(messages.cancel)}
|
||||||
|
</ModalDialog.CloseButton>
|
||||||
|
{footer}
|
||||||
|
</ActionRow>
|
||||||
|
</ModalDialog.Footer>
|
||||||
|
</ModalDialog>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
AppSettingsModalBase.defaultProps = {
|
||||||
|
isOpen: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
AppSettingsModalBase.propTypes = {
|
||||||
|
title: PropTypes.string.isRequired,
|
||||||
|
onClose: PropTypes.func.isRequired,
|
||||||
|
variant: PropTypes.oneOf(['default', 'dark']).isRequired,
|
||||||
|
isMobile: PropTypes.bool.isRequired,
|
||||||
|
children: PropTypes.node.isRequired,
|
||||||
|
footer: PropTypes.node,
|
||||||
|
isOpen: PropTypes.bool,
|
||||||
|
};
|
||||||
|
|
||||||
|
AppSettingsModalBase.defaultProps = {
|
||||||
|
footer: null,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default AppSettingsModalBase;
|
||||||
@@ -1,15 +1,11 @@
|
|||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
|
import { useIntl } from '@edx/frontend-platform/i18n';
|
||||||
import {
|
import { Badge, Card } from '@openedx/paragon';
|
||||||
Badge, Card, Icon, IconButton, Hyperlink,
|
|
||||||
} from '@openedx/paragon';
|
|
||||||
import { ArrowForward, Settings } from '@openedx/paragon/icons';
|
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import React, { useContext } from 'react';
|
import React from 'react';
|
||||||
import { useNavigate } from 'react-router-dom';
|
|
||||||
import messages from '../messages';
|
import messages from '../messages';
|
||||||
import { PagesAndResourcesContext } from '../PagesAndResourcesProvider';
|
|
||||||
import { useIsDesktop } from '../../utils';
|
import { useIsDesktop } from '../../utils';
|
||||||
|
import PageSettingButton from './PageSettingButton';
|
||||||
import './PageCard.scss';
|
import './PageCard.scss';
|
||||||
|
|
||||||
const CoursePageShape = PropTypes.shape({
|
const CoursePageShape = PropTypes.shape({
|
||||||
@@ -19,45 +15,21 @@ const CoursePageShape = PropTypes.shape({
|
|||||||
enabled: PropTypes.bool.isRequired,
|
enabled: PropTypes.bool.isRequired,
|
||||||
legacyLink: PropTypes.string,
|
legacyLink: PropTypes.string,
|
||||||
allowedOperations: PropTypes.shape({
|
allowedOperations: PropTypes.shape({
|
||||||
enable: PropTypes.bool.isRequired,
|
enable: PropTypes.bool,
|
||||||
configure: PropTypes.bool.isRequired,
|
configure: PropTypes.bool,
|
||||||
}).isRequired,
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
export { CoursePageShape };
|
export { CoursePageShape };
|
||||||
|
|
||||||
const PageCard = ({
|
const PageCard = ({
|
||||||
intl,
|
|
||||||
page,
|
page,
|
||||||
|
settingButton,
|
||||||
}) => {
|
}) => {
|
||||||
const { path: pagesAndResourcesPath } = useContext(PagesAndResourcesContext);
|
const { formatMessage } = useIntl();
|
||||||
const isDesktop = useIsDesktop();
|
const isDesktop = useIsDesktop();
|
||||||
const navigate = useNavigate();
|
|
||||||
|
|
||||||
// eslint-disable-next-line react/no-unstable-nested-components
|
const SettingButton = settingButton || <PageSettingButton {...page} />;
|
||||||
const SettingsButton = () => {
|
|
||||||
if (page.legacyLink) {
|
|
||||||
return (
|
|
||||||
<Hyperlink destination={page.legacyLink}>
|
|
||||||
<IconButton
|
|
||||||
src={ArrowForward}
|
|
||||||
iconAs={Icon}
|
|
||||||
size="inline"
|
|
||||||
alt={intl.formatMessage(messages.settings)}
|
|
||||||
/>
|
|
||||||
</Hyperlink>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return (page.allowedOperations.configure || page.allowedOperations.enable) && (
|
|
||||||
<IconButton
|
|
||||||
src={Settings}
|
|
||||||
iconAs={Icon}
|
|
||||||
size="inline"
|
|
||||||
alt={intl.formatMessage(messages.settings)}
|
|
||||||
onClick={() => navigate(`${pagesAndResourcesPath}/${page.id}/settings`)}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card
|
<Card
|
||||||
@@ -70,10 +42,10 @@ const PageCard = ({
|
|||||||
title={page.name}
|
title={page.name}
|
||||||
subtitle={page.enabled && (
|
subtitle={page.enabled && (
|
||||||
<Badge variant="success" className="mt-1">
|
<Badge variant="success" className="mt-1">
|
||||||
{intl.formatMessage(messages.enabled)}
|
{formatMessage(messages.enabled)}
|
||||||
</Badge>
|
</Badge>
|
||||||
)}
|
)}
|
||||||
actions={<div className="mt-1"><SettingsButton /></div>}
|
actions={<div className="mt-1">{SettingButton}</div>}
|
||||||
size="sm"
|
size="sm"
|
||||||
/>
|
/>
|
||||||
<Card.Body>
|
<Card.Body>
|
||||||
@@ -85,9 +57,13 @@ const PageCard = ({
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
PageCard.propTypes = {
|
PageCard.defaultProps = {
|
||||||
intl: intlShape.isRequired,
|
settingButton: null,
|
||||||
page: CoursePageShape.isRequired,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default injectIntl(PageCard);
|
PageCard.propTypes = {
|
||||||
|
page: CoursePageShape.isRequired,
|
||||||
|
settingButton: PropTypes.node,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default PageCard;
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import PropTypes from 'prop-types';
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { injectIntl } from '@edx/frontend-platform/i18n';
|
import { injectIntl } from '@edx/frontend-platform/i18n';
|
||||||
import { CardGrid } from '@openedx/paragon';
|
import { CardGrid } from '@openedx/paragon';
|
||||||
|
import { PluginSlot } from '@openedx/frontend-plugin-framework';
|
||||||
import PageCard, { CoursePageShape } from './PageCard';
|
import PageCard, { CoursePageShape } from './PageCard';
|
||||||
|
|
||||||
const PageGrid = ({ pages }) => (
|
const PageGrid = ({ pages }) => (
|
||||||
@@ -15,6 +16,7 @@ const PageGrid = ({ pages }) => (
|
|||||||
{pages.map((page) => (
|
{pages.map((page) => (
|
||||||
<PageCard page={page} key={page.id} />
|
<PageCard page={page} key={page.id} />
|
||||||
))}
|
))}
|
||||||
|
<PluginSlot id="additional_course_plugin" />
|
||||||
</CardGrid>
|
</CardGrid>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
59
src/pages-and-resources/pages/PageSettingButton.jsx
Normal file
59
src/pages-and-resources/pages/PageSettingButton.jsx
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
import React, { useContext } from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
|
import { useIntl } from '@edx/frontend-platform/i18n';
|
||||||
|
import { Icon, IconButton, Hyperlink } from '@openedx/paragon';
|
||||||
|
import { ArrowForward, Settings } from '@openedx/paragon/icons';
|
||||||
|
import { useNavigate } from 'react-router-dom';
|
||||||
|
import messages from '../messages';
|
||||||
|
import { PagesAndResourcesContext } from '../PagesAndResourcesProvider';
|
||||||
|
|
||||||
|
const PageSettingButton = ({
|
||||||
|
id,
|
||||||
|
legacyLink,
|
||||||
|
allowedOperations,
|
||||||
|
}) => {
|
||||||
|
const { formatMessage } = useIntl();
|
||||||
|
const { path: pagesAndResourcesPath } = useContext(PagesAndResourcesContext);
|
||||||
|
const navigate = useNavigate();
|
||||||
|
|
||||||
|
if (legacyLink) {
|
||||||
|
return (
|
||||||
|
<Hyperlink destination={legacyLink}>
|
||||||
|
<IconButton
|
||||||
|
src={ArrowForward}
|
||||||
|
iconAs={Icon}
|
||||||
|
size="inline"
|
||||||
|
alt={formatMessage(messages.settings)}
|
||||||
|
/>
|
||||||
|
</Hyperlink>
|
||||||
|
);
|
||||||
|
} if (!(allowedOperations?.configure || allowedOperations?.enable)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<IconButton
|
||||||
|
src={Settings}
|
||||||
|
iconAs={Icon}
|
||||||
|
size="inline"
|
||||||
|
alt={formatMessage(messages.settings)}
|
||||||
|
onClick={() => navigate(`${pagesAndResourcesPath}/${id}/settings`)}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
PageSettingButton.defaultProps = {
|
||||||
|
legacyLink: null,
|
||||||
|
allowedOperations: null,
|
||||||
|
};
|
||||||
|
|
||||||
|
PageSettingButton.propTypes = {
|
||||||
|
id: PropTypes.string.isRequired,
|
||||||
|
legacyLink: PropTypes.string,
|
||||||
|
allowedOperations: PropTypes.shape({
|
||||||
|
configure: PropTypes.bool,
|
||||||
|
enable: PropTypes.bool,
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
|
||||||
|
export default PageSettingButton;
|
||||||
Reference in New Issue
Block a user