├── server
├── api
│ ├── .gitkeep
│ ├── email
│ │ └── config
│ │ │ └── routes.json
│ ├── day
│ │ ├── services
│ │ │ └── day.js
│ │ ├── models
│ │ │ ├── day.js
│ │ │ └── day.settings.json
│ │ └── config
│ │ │ └── routes.json
│ ├── grade
│ │ ├── models
│ │ │ ├── grade.js
│ │ │ └── grade.settings.json
│ │ ├── services
│ │ │ └── grade.js
│ │ ├── controllers
│ │ │ └── grade.js
│ │ └── config
│ │ │ └── routes.json
│ ├── save
│ │ ├── models
│ │ │ ├── save.js
│ │ │ └── save.settings.json
│ │ ├── services
│ │ │ └── save.js
│ │ └── config
│ │ │ └── routes.json
│ ├── unit
│ │ ├── models
│ │ │ ├── unit.js
│ │ │ └── unit.settings.json
│ │ ├── services
│ │ │ └── unit.js
│ │ ├── config
│ │ │ └── routes.json
│ │ └── controllers
│ │ │ └── unit.js
│ ├── mentor
│ │ ├── models
│ │ │ ├── mentor.js
│ │ │ └── mentor.settings.json
│ │ ├── services
│ │ │ └── mentor.js
│ │ ├── config
│ │ │ └── routes.json
│ │ └── controllers
│ │ │ └── mentor.js
│ ├── school
│ │ ├── models
│ │ │ ├── school.js
│ │ │ └── school.settings.json
│ │ ├── services
│ │ │ └── school.js
│ │ ├── controllers
│ │ │ └── school.js
│ │ └── config
│ │ │ └── routes.json
│ ├── session
│ │ ├── models
│ │ │ ├── session.js
│ │ │ └── session.settings.json
│ │ ├── services
│ │ │ └── session.js
│ │ ├── controllers
│ │ │ └── session.js
│ │ └── config
│ │ │ └── routes.json
│ ├── student
│ │ ├── models
│ │ │ ├── student.js
│ │ │ └── student.settings.json
│ │ ├── services
│ │ │ └── student.js
│ │ └── config
│ │ │ └── routes.json
│ ├── classroom
│ │ ├── models
│ │ │ ├── classroom.js
│ │ │ └── classroom.settings.json
│ │ └── services
│ │ │ └── classroom.js
│ ├── selection
│ │ ├── models
│ │ │ ├── selection.js
│ │ │ └── selection.settings.json
│ │ ├── services
│ │ │ └── selection.js
│ │ └── config
│ │ │ └── routes.json
│ ├── submission
│ │ ├── models
│ │ │ ├── submission.js
│ │ │ └── submission.settings.json
│ │ └── config
│ │ │ └── routes.json
│ ├── block
│ │ ├── controllers
│ │ │ └── block.js
│ │ ├── models
│ │ │ ├── block.settings.json
│ │ │ └── block.js
│ │ ├── config
│ │ │ └── routes.json
│ │ └── services
│ │ │ └── block.js
│ ├── learning-standard
│ │ ├── models
│ │ │ ├── learning-standard.js
│ │ │ └── learning-standard.settings.json
│ │ ├── services
│ │ │ └── learning-standard.js
│ │ ├── controllers
│ │ │ └── learning-standard.js
│ │ └── config
│ │ │ └── routes.json
│ ├── blocks-category
│ │ ├── services
│ │ │ └── blocks-category.js
│ │ ├── controllers
│ │ │ └── blocks-category.js
│ │ ├── models
│ │ │ ├── blocks-category.settings.json
│ │ │ └── blocks-category.js
│ │ └── config
│ │ │ └── routes.json
│ ├── cc-workspace
│ │ ├── models
│ │ │ ├── cc-workspace.js
│ │ │ └── cc-workspace.settings.json
│ │ ├── services
│ │ │ └── cc-workspace.js
│ │ └── config
│ │ │ ├── policies
│ │ │ └── isContentCreatorOrHasClassroom.js
│ │ │ └── routes.json
│ ├── learning-components
│ │ ├── models
│ │ │ ├── learning-components.js
│ │ │ └── learning-components.settings.json
│ │ ├── services
│ │ │ └── learning-components.js
│ │ ├── controllers
│ │ │ └── learning-components.js
│ │ └── config
│ │ │ └── routes.json
│ ├── learning-component-types
│ │ ├── models
│ │ │ ├── learning-component-types.js
│ │ │ └── learning-component-types.settings.json
│ │ ├── services
│ │ │ └── learning-component-types.js
│ │ ├── controllers
│ │ │ └── learning-component-types.js
│ │ └── config
│ │ │ └── routes.json
│ ├── strapiusers
│ │ ├── config
│ │ │ └── routes.json
│ │ ├── controllers
│ │ │ └── strapiusers.js
│ │ └── documentation
│ │ │ └── 1.0.0
│ │ │ ├── admin.json
│ │ │ └── strapiusers.json
│ ├── classroom-manager
│ │ ├── config
│ │ │ └── routes.json
│ │ ├── controllers
│ │ │ └── classroom-manager.js
│ │ ├── services
│ │ │ └── classroom-manager.js
│ │ └── documentation
│ │ │ └── 1.0.0
│ │ │ └── classroom-manager.json
│ ├── validator
│ │ └── services
│ │ │ └── validator.js
│ └── sandbox
│ │ └── config
│ │ └── routes.json
├── extensions
│ ├── .gitkeep
│ └── users-permissions
│ │ └── config
│ │ └── jwt.js
├── public
│ ├── client
│ │ └── .gitkeep
│ └── uploads
│ │ ├── .gitkeep
│ │ ├── 0f298ba9ad194735911aecc1839c6097.PNG
│ │ ├── 2c987c12f08042b8878b36d5c32556f7.PNG
│ │ ├── 2d7ee65ba79e404ea0daa9a3c08bd0bd.jpeg
│ │ ├── 3b6e19b63f0d479e970cb2f13c0c7bbb.jpeg
│ │ ├── 8434130ebc364acc86de7b6f3ddf7e51.jpeg
│ │ └── b5d64109ebc14297bbe269c955fe6d52.PNG
├── config
│ ├── locales
│ │ ├── ja_jp.json
│ │ ├── cs_cz.json
│ │ ├── en_us.json
│ │ ├── fr_fr.json
│ │ ├── it_it.json
│ │ ├── tr_tr.json
│ │ ├── de_de.json
│ │ ├── es_es.json
│ │ └── ru_ru.json
│ ├── compile_queue.js
│ ├── functions
│ │ ├── responses
│ │ │ └── 404.js
│ │ ├── cron.js
│ │ └── bootstrap.js
│ ├── policies
│ │ ├── isStudent.js
│ │ ├── isContentCreator.js
│ │ ├── isClassroomManager.js
│ │ ├── hasClassroom.js
│ │ └── hasStudentsClassroom.js
│ ├── server.js
│ ├── middleware.js
│ ├── plugins.js
│ └── database.js
├── favicon.ico
├── er_diagram.PNG
├── ._.strapi-updater.json
├── admin
│ └── src
│ │ ├── favicon.png
│ │ ├── assets
│ │ └── images
│ │ │ ├── logo_slack.png
│ │ │ ├── social_gh.png
│ │ │ ├── logo-strapi.png
│ │ │ ├── logo_github.png
│ │ │ ├── logo_strapi.png
│ │ │ ├── social_slack.png
│ │ │ ├── banner_t-shirt.png
│ │ │ ├── bg_hp_tee_shirt.png
│ │ │ ├── social_medium.png
│ │ │ ├── social_reddit.png
│ │ │ ├── social_twitter.png
│ │ │ ├── logo_stack_overflow.png
│ │ │ ├── background_welcome_homepage.png
│ │ │ └── logo-t-shirt.svg
│ │ ├── containers
│ │ └── AuthPage
│ │ │ └── components
│ │ │ └── Logo
│ │ │ └── img.js
│ │ ├── themes
│ │ ├── sizes.js
│ │ └── colors.js
│ │ ├── index.html
│ │ └── components
│ │ └── LeftMenu
│ │ └── LeftMenuHeader
│ │ └── Wrapper.js
├── package.json
└── middlewares
│ └── proxy
│ └── index.js
├── test
├── functional
│ ├── .gitkeep
│ └── contentcreatorMocks.test.js
├── performance
│ ├── .gitkeep
│ └── load.test.js
├── jest.config.js
├── babel.config.cjs
├── integration
│ └── request.js
├── package.json
└── README.md
├── client
├── .gitignore
├── public
│ ├── favicon.ico
│ ├── logo192.png
│ ├── logo512.png
│ ├── robots.txt
│ ├── images
│ │ ├── text.png
│ │ ├── io_tone.png
│ │ ├── io_highlow.png
│ │ ├── io_notone.png
│ │ ├── io_pulsein.png
│ │ ├── logic_null.png
│ │ ├── math_round.png
│ │ ├── math_trig.png
│ │ ├── servo_read.png
│ │ ├── spi_setup.png
│ │ ├── text_join.png
│ │ ├── text_print.png
│ │ ├── time_delay.png
│ │ ├── controls_for.png
│ │ ├── controls_if.png
│ │ ├── logic_negate.png
│ │ ├── math_change.png
│ │ ├── math_modulo.png
│ │ ├── math_number.png
│ │ ├── math_on_list.png
│ │ ├── math_single.png
│ │ ├── serial_print.png
│ │ ├── serial_setup.png
│ │ ├── servo_write.png
│ │ ├── spi_transfer.png
│ │ ├── stepper_step.png
│ │ ├── text_append.png
│ │ ├── text_char_At.png
│ │ ├── text_length.png
│ │ ├── time_micros.png
│ │ ├── time_millis.png
│ │ ├── block_comment.png
│ │ ├── controls_repeat.png
│ │ ├── infinite_loop.png
│ │ ├── insert_comment.png
│ │ ├── io_analogread.png
│ │ ├── io_analogwrite.png
│ │ ├── io_builtin_led.png
│ │ ├── io_digitalread.png
│ │ ├── io_digitalwrite.png
│ │ ├── io_pulsetimeout.png
│ │ ├── logic_boolean.png
│ │ ├── logic_compare.png
│ │ ├── logic_operation.png
│ │ ├── logic_ternary.png
│ │ ├── math_arithmetic.png
│ │ ├── math_constant.png
│ │ ├── math_constrain.png
│ │ ├── math_random_int.png
│ │ ├── stepper_config.png
│ │ ├── text_is_Empty.png
│ │ ├── text_prompt_ext.png
│ │ ├── variables_get.png
│ │ ├── variables_set.png
│ │ ├── arduino_functions.png
│ │ ├── controls_for_Each.png
│ │ ├── math_random_float.png
│ │ ├── time_delaymicros.png
│ │ ├── controls_while_Until.png
│ │ ├── math_number_property.png
│ │ ├── procedures_defreturn.png
│ │ ├── procedures_ifreturn.png
│ │ ├── sensor_set_dht_pin.png
│ │ ├── spi_transfer_return.png
│ │ ├── text_get_Substring.png
│ │ ├── variables_set_type.png
│ │ ├── procedures_callreturn.png
│ │ ├── procedures_defnoreturn.png
│ │ ├── small_io_pulsetimeout.png
│ │ ├── controls_flow_statements.png
│ │ ├── procedures_callnoreturn.png
│ │ ├── sensor_read_dht_humidity.png
│ │ ├── small_text_get_Substring.png
│ │ └── sensor_read_dht_temperature.png
│ ├── lib
│ │ ├── depreciated.js
│ │ └── readme.md
│ └── manifest.json
├── src
│ ├── assets
│ │ ├── maker.png
│ │ ├── arduino.png
│ │ ├── nsf_logo.png
│ │ ├── science.png
│ │ ├── uf_logo.png
│ │ ├── casmm_logo.png
│ │ ├── tamu_logo.png
│ │ ├── style.css
│ │ └── style.less
│ ├── Utils
│ │ ├── hosts.js
│ │ ├── PrivateRoute.jsx
│ │ ├── userState.js
│ │ └── AuthRequests.js
│ ├── components
│ │ ├── Message.jsx
│ │ ├── RouteButton
│ │ │ ├── RouteButton.less
│ │ │ └── RouteButton.jsx
│ │ ├── DayPanels
│ │ │ └── BlocklyCanvasPanel
│ │ │ │ ├── Icons
│ │ │ │ └── textIcon.json
│ │ │ │ ├── BlocklyCanvasPanel.jsx
│ │ │ │ └── modals
│ │ │ │ └── CodeModal.jsx
│ │ ├── NavBar
│ │ │ ├── NavBarConfig.json
│ │ │ └── NavBar.less
│ │ └── MentorSubHeader
│ │ │ ├── MentorSubHeader.less
│ │ │ └── MentorSubHeader.jsx
│ ├── index.css
│ ├── views
│ │ ├── NotFound.jsx
│ │ ├── UploadBlocks
│ │ │ └── UpbloadBlocks.less
│ │ ├── NotChrome.jsx
│ │ ├── Home
│ │ │ ├── Home.jsx
│ │ │ └── HomeJoin.jsx
│ │ ├── ContentCreator
│ │ │ ├── ContentCreator.less
│ │ │ ├── UnitEditor
│ │ │ │ └── UnitEditor.less
│ │ │ ├── UnitCreator
│ │ │ │ └── UnitCreator.less
│ │ │ └── LearningStandardCreator
│ │ │ │ └── LearningStandardCreator.less
│ │ ├── Mentor
│ │ │ ├── Classroom
│ │ │ │ ├── Classroom.less
│ │ │ │ ├── Home
│ │ │ │ │ ├── DisplayCodeModal.jsx
│ │ │ │ │ ├── DisplayFormModal.jsx
│ │ │ │ │ └── LearningStandardSelect
│ │ │ │ │ │ └── CheckUnits.jsx
│ │ │ │ ├── Roster
│ │ │ │ │ ├── AddStudents
│ │ │ │ │ │ ├── AddStudents.less
│ │ │ │ │ │ └── AddStudentsModal.jsx
│ │ │ │ │ ├── StudentModal.jsx
│ │ │ │ │ └── CardView.jsx
│ │ │ │ └── Classroom.jsx
│ │ │ └── Dashboard
│ │ │ │ └── DashboardDisplayCodeModal.jsx
│ │ ├── Researcher
│ │ │ ├── GroupReport.less
│ │ │ ├── GroupReport.jsx
│ │ │ └── Report.jsx
│ │ ├── Student
│ │ │ ├── form.less
│ │ │ ├── form.jsx
│ │ │ └── Student.less
│ │ ├── TeacherLogin
│ │ │ ├── Sorry.less
│ │ │ ├── ConfirmEmail.jsx
│ │ │ └── Sorry.jsx
│ │ ├── About
│ │ │ └── About.less
│ │ ├── BugReport
│ │ │ └── BugReport.less
│ │ ├── Replay
│ │ │ └── Replay.less
│ │ └── Workspace
│ │ │ └── Workspace.jsx
│ ├── index.jsx
│ └── index.less
├── package.json
└── vite.config.js
├── .dockerignore
├── .gitattributes
├── compile
├── diagram.png
├── .dockerignore
├── src
│ ├── utils
│ │ └── base.js
│ ├── cluster.js
│ ├── controllers
│ │ └── job.js
│ └── handlers
│ │ └── worker.js
├── package.json
└── Dockerfile
├── package.json
├── .github
├── workflows
│ ├── tag-master.yml
│ ├── end-review.yml
│ ├── deploy-staging.yml
│ ├── update-review.yml
│ ├── deploy-production.yml
│ └── start-review.yml
└── ISSUE_TEMPLATE
│ ├── feature_request.md
│ └── bug_report.md
├── .gitignore
├── scripts
└── init_db.sh
├── Dockerfile
├── docker-compose.yml
└── docker-compose.test.yml
/server/api/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/server/extensions/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/test/functional/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/test/performance/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/server/public/client/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/server/public/uploads/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/client/.gitignore:
--------------------------------------------------------------------------------
1 | package-lock.json
--------------------------------------------------------------------------------
/.dockerignore:
--------------------------------------------------------------------------------
1 | */node_modules
2 | */build
3 | */.cache
4 | */.tmp
5 |
--------------------------------------------------------------------------------
/server/config/locales/ja_jp.json:
--------------------------------------------------------------------------------
1 | {
2 | "welcome": "ようこそ"
3 | }
4 |
--------------------------------------------------------------------------------
/server/config/locales/cs_cz.json:
--------------------------------------------------------------------------------
1 | {
2 | "welcome": "Vítejte"
3 | }
4 |
--------------------------------------------------------------------------------
/server/config/locales/en_us.json:
--------------------------------------------------------------------------------
1 | {
2 | "welcome": "Welcome"
3 | }
4 |
--------------------------------------------------------------------------------
/server/config/locales/fr_fr.json:
--------------------------------------------------------------------------------
1 | {
2 | "welcome": "Bienvenue"
3 | }
4 |
--------------------------------------------------------------------------------
/server/config/locales/it_it.json:
--------------------------------------------------------------------------------
1 | {
2 | "welcome": "Benvenuto"
3 | }
4 |
--------------------------------------------------------------------------------
/server/config/locales/tr_tr.json:
--------------------------------------------------------------------------------
1 | {
2 | "welcome": "Hoşgeldin"
3 | }
4 |
--------------------------------------------------------------------------------
/test/jest.config.js:
--------------------------------------------------------------------------------
1 | export default {
2 | testTimeout: 20000
3 | }
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | # set all bash files to keep lf endings
2 | *.sh text eol=lf
--------------------------------------------------------------------------------
/server/config/locales/de_de.json:
--------------------------------------------------------------------------------
1 | {
2 | "welcome": "Willkommen"
3 | }
4 |
--------------------------------------------------------------------------------
/server/config/locales/es_es.json:
--------------------------------------------------------------------------------
1 | {
2 | "welcome": "Bienvenido"
3 | }
4 |
--------------------------------------------------------------------------------
/compile/diagram.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/STEM-C/CaSMM/HEAD/compile/diagram.png
--------------------------------------------------------------------------------
/server/config/locales/ru_ru.json:
--------------------------------------------------------------------------------
1 | {
2 | "welcome": "Добро пожаловать"
3 | }
4 |
--------------------------------------------------------------------------------
/server/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/STEM-C/CaSMM/HEAD/server/favicon.ico
--------------------------------------------------------------------------------
/server/er_diagram.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/STEM-C/CaSMM/HEAD/server/er_diagram.PNG
--------------------------------------------------------------------------------
/client/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/STEM-C/CaSMM/HEAD/client/public/favicon.ico
--------------------------------------------------------------------------------
/client/public/logo192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/STEM-C/CaSMM/HEAD/client/public/logo192.png
--------------------------------------------------------------------------------
/client/public/logo512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/STEM-C/CaSMM/HEAD/client/public/logo512.png
--------------------------------------------------------------------------------
/compile/.dockerignore:
--------------------------------------------------------------------------------
1 | # Ignore these when building the image
2 | node_modules
3 | npm-debug.log
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "dependencies": {
3 | "react-media-recorder": "^1.7.1"
4 | }
5 | }
6 |
--------------------------------------------------------------------------------
/client/public/robots.txt:
--------------------------------------------------------------------------------
1 | # https://www.robotstxt.org/robotstxt.html
2 | User-agent: *
3 | Disallow:
4 |
--------------------------------------------------------------------------------
/client/src/assets/maker.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/STEM-C/CaSMM/HEAD/client/src/assets/maker.png
--------------------------------------------------------------------------------
/client/public/images/text.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/STEM-C/CaSMM/HEAD/client/public/images/text.png
--------------------------------------------------------------------------------
/client/src/assets/arduino.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/STEM-C/CaSMM/HEAD/client/src/assets/arduino.png
--------------------------------------------------------------------------------
/client/src/assets/nsf_logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/STEM-C/CaSMM/HEAD/client/src/assets/nsf_logo.png
--------------------------------------------------------------------------------
/client/src/assets/science.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/STEM-C/CaSMM/HEAD/client/src/assets/science.png
--------------------------------------------------------------------------------
/client/src/assets/uf_logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/STEM-C/CaSMM/HEAD/client/src/assets/uf_logo.png
--------------------------------------------------------------------------------
/server/._.strapi-updater.json:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/STEM-C/CaSMM/HEAD/server/._.strapi-updater.json
--------------------------------------------------------------------------------
/server/admin/src/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/STEM-C/CaSMM/HEAD/server/admin/src/favicon.png
--------------------------------------------------------------------------------
/client/public/images/io_tone.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/STEM-C/CaSMM/HEAD/client/public/images/io_tone.png
--------------------------------------------------------------------------------
/client/src/assets/casmm_logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/STEM-C/CaSMM/HEAD/client/src/assets/casmm_logo.png
--------------------------------------------------------------------------------
/client/src/assets/tamu_logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/STEM-C/CaSMM/HEAD/client/src/assets/tamu_logo.png
--------------------------------------------------------------------------------
/client/public/images/io_highlow.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/STEM-C/CaSMM/HEAD/client/public/images/io_highlow.png
--------------------------------------------------------------------------------
/client/public/images/io_notone.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/STEM-C/CaSMM/HEAD/client/public/images/io_notone.png
--------------------------------------------------------------------------------
/client/public/images/io_pulsein.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/STEM-C/CaSMM/HEAD/client/public/images/io_pulsein.png
--------------------------------------------------------------------------------
/client/public/images/logic_null.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/STEM-C/CaSMM/HEAD/client/public/images/logic_null.png
--------------------------------------------------------------------------------
/client/public/images/math_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/STEM-C/CaSMM/HEAD/client/public/images/math_round.png
--------------------------------------------------------------------------------
/client/public/images/math_trig.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/STEM-C/CaSMM/HEAD/client/public/images/math_trig.png
--------------------------------------------------------------------------------
/client/public/images/servo_read.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/STEM-C/CaSMM/HEAD/client/public/images/servo_read.png
--------------------------------------------------------------------------------
/client/public/images/spi_setup.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/STEM-C/CaSMM/HEAD/client/public/images/spi_setup.png
--------------------------------------------------------------------------------
/client/public/images/text_join.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/STEM-C/CaSMM/HEAD/client/public/images/text_join.png
--------------------------------------------------------------------------------
/client/public/images/text_print.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/STEM-C/CaSMM/HEAD/client/public/images/text_print.png
--------------------------------------------------------------------------------
/client/public/images/time_delay.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/STEM-C/CaSMM/HEAD/client/public/images/time_delay.png
--------------------------------------------------------------------------------
/client/public/images/controls_for.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/STEM-C/CaSMM/HEAD/client/public/images/controls_for.png
--------------------------------------------------------------------------------
/client/public/images/controls_if.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/STEM-C/CaSMM/HEAD/client/public/images/controls_if.png
--------------------------------------------------------------------------------
/client/public/images/logic_negate.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/STEM-C/CaSMM/HEAD/client/public/images/logic_negate.png
--------------------------------------------------------------------------------
/client/public/images/math_change.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/STEM-C/CaSMM/HEAD/client/public/images/math_change.png
--------------------------------------------------------------------------------
/client/public/images/math_modulo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/STEM-C/CaSMM/HEAD/client/public/images/math_modulo.png
--------------------------------------------------------------------------------
/client/public/images/math_number.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/STEM-C/CaSMM/HEAD/client/public/images/math_number.png
--------------------------------------------------------------------------------
/client/public/images/math_on_list.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/STEM-C/CaSMM/HEAD/client/public/images/math_on_list.png
--------------------------------------------------------------------------------
/client/public/images/math_single.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/STEM-C/CaSMM/HEAD/client/public/images/math_single.png
--------------------------------------------------------------------------------
/client/public/images/serial_print.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/STEM-C/CaSMM/HEAD/client/public/images/serial_print.png
--------------------------------------------------------------------------------
/client/public/images/serial_setup.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/STEM-C/CaSMM/HEAD/client/public/images/serial_setup.png
--------------------------------------------------------------------------------
/client/public/images/servo_write.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/STEM-C/CaSMM/HEAD/client/public/images/servo_write.png
--------------------------------------------------------------------------------
/client/public/images/spi_transfer.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/STEM-C/CaSMM/HEAD/client/public/images/spi_transfer.png
--------------------------------------------------------------------------------
/client/public/images/stepper_step.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/STEM-C/CaSMM/HEAD/client/public/images/stepper_step.png
--------------------------------------------------------------------------------
/client/public/images/text_append.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/STEM-C/CaSMM/HEAD/client/public/images/text_append.png
--------------------------------------------------------------------------------
/client/public/images/text_char_At.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/STEM-C/CaSMM/HEAD/client/public/images/text_char_At.png
--------------------------------------------------------------------------------
/client/public/images/text_length.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/STEM-C/CaSMM/HEAD/client/public/images/text_length.png
--------------------------------------------------------------------------------
/client/public/images/time_micros.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/STEM-C/CaSMM/HEAD/client/public/images/time_micros.png
--------------------------------------------------------------------------------
/client/public/images/time_millis.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/STEM-C/CaSMM/HEAD/client/public/images/time_millis.png
--------------------------------------------------------------------------------
/client/public/images/block_comment.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/STEM-C/CaSMM/HEAD/client/public/images/block_comment.png
--------------------------------------------------------------------------------
/client/public/images/controls_repeat.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/STEM-C/CaSMM/HEAD/client/public/images/controls_repeat.png
--------------------------------------------------------------------------------
/client/public/images/infinite_loop.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/STEM-C/CaSMM/HEAD/client/public/images/infinite_loop.png
--------------------------------------------------------------------------------
/client/public/images/insert_comment.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/STEM-C/CaSMM/HEAD/client/public/images/insert_comment.png
--------------------------------------------------------------------------------
/client/public/images/io_analogread.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/STEM-C/CaSMM/HEAD/client/public/images/io_analogread.png
--------------------------------------------------------------------------------
/client/public/images/io_analogwrite.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/STEM-C/CaSMM/HEAD/client/public/images/io_analogwrite.png
--------------------------------------------------------------------------------
/client/public/images/io_builtin_led.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/STEM-C/CaSMM/HEAD/client/public/images/io_builtin_led.png
--------------------------------------------------------------------------------
/client/public/images/io_digitalread.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/STEM-C/CaSMM/HEAD/client/public/images/io_digitalread.png
--------------------------------------------------------------------------------
/client/public/images/io_digitalwrite.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/STEM-C/CaSMM/HEAD/client/public/images/io_digitalwrite.png
--------------------------------------------------------------------------------
/client/public/images/io_pulsetimeout.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/STEM-C/CaSMM/HEAD/client/public/images/io_pulsetimeout.png
--------------------------------------------------------------------------------
/client/public/images/logic_boolean.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/STEM-C/CaSMM/HEAD/client/public/images/logic_boolean.png
--------------------------------------------------------------------------------
/client/public/images/logic_compare.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/STEM-C/CaSMM/HEAD/client/public/images/logic_compare.png
--------------------------------------------------------------------------------
/client/public/images/logic_operation.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/STEM-C/CaSMM/HEAD/client/public/images/logic_operation.png
--------------------------------------------------------------------------------
/client/public/images/logic_ternary.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/STEM-C/CaSMM/HEAD/client/public/images/logic_ternary.png
--------------------------------------------------------------------------------
/client/public/images/math_arithmetic.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/STEM-C/CaSMM/HEAD/client/public/images/math_arithmetic.png
--------------------------------------------------------------------------------
/client/public/images/math_constant.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/STEM-C/CaSMM/HEAD/client/public/images/math_constant.png
--------------------------------------------------------------------------------
/client/public/images/math_constrain.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/STEM-C/CaSMM/HEAD/client/public/images/math_constrain.png
--------------------------------------------------------------------------------
/client/public/images/math_random_int.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/STEM-C/CaSMM/HEAD/client/public/images/math_random_int.png
--------------------------------------------------------------------------------
/client/public/images/stepper_config.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/STEM-C/CaSMM/HEAD/client/public/images/stepper_config.png
--------------------------------------------------------------------------------
/client/public/images/text_is_Empty.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/STEM-C/CaSMM/HEAD/client/public/images/text_is_Empty.png
--------------------------------------------------------------------------------
/client/public/images/text_prompt_ext.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/STEM-C/CaSMM/HEAD/client/public/images/text_prompt_ext.png
--------------------------------------------------------------------------------
/client/public/images/variables_get.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/STEM-C/CaSMM/HEAD/client/public/images/variables_get.png
--------------------------------------------------------------------------------
/client/public/images/variables_set.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/STEM-C/CaSMM/HEAD/client/public/images/variables_set.png
--------------------------------------------------------------------------------
/client/public/images/arduino_functions.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/STEM-C/CaSMM/HEAD/client/public/images/arduino_functions.png
--------------------------------------------------------------------------------
/client/public/images/controls_for_Each.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/STEM-C/CaSMM/HEAD/client/public/images/controls_for_Each.png
--------------------------------------------------------------------------------
/client/public/images/math_random_float.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/STEM-C/CaSMM/HEAD/client/public/images/math_random_float.png
--------------------------------------------------------------------------------
/client/public/images/time_delaymicros.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/STEM-C/CaSMM/HEAD/client/public/images/time_delaymicros.png
--------------------------------------------------------------------------------
/server/config/compile_queue.js:
--------------------------------------------------------------------------------
1 | module.exports = ({ env }) => ({
2 | url: env('REDIS_URL', 'redis://compile_queue:6379')
3 | })
--------------------------------------------------------------------------------
/client/public/images/controls_while_Until.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/STEM-C/CaSMM/HEAD/client/public/images/controls_while_Until.png
--------------------------------------------------------------------------------
/client/public/images/math_number_property.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/STEM-C/CaSMM/HEAD/client/public/images/math_number_property.png
--------------------------------------------------------------------------------
/client/public/images/procedures_defreturn.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/STEM-C/CaSMM/HEAD/client/public/images/procedures_defreturn.png
--------------------------------------------------------------------------------
/client/public/images/procedures_ifreturn.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/STEM-C/CaSMM/HEAD/client/public/images/procedures_ifreturn.png
--------------------------------------------------------------------------------
/client/public/images/sensor_set_dht_pin.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/STEM-C/CaSMM/HEAD/client/public/images/sensor_set_dht_pin.png
--------------------------------------------------------------------------------
/client/public/images/spi_transfer_return.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/STEM-C/CaSMM/HEAD/client/public/images/spi_transfer_return.png
--------------------------------------------------------------------------------
/client/public/images/text_get_Substring.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/STEM-C/CaSMM/HEAD/client/public/images/text_get_Substring.png
--------------------------------------------------------------------------------
/client/public/images/variables_set_type.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/STEM-C/CaSMM/HEAD/client/public/images/variables_set_type.png
--------------------------------------------------------------------------------
/server/admin/src/assets/images/logo_slack.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/STEM-C/CaSMM/HEAD/server/admin/src/assets/images/logo_slack.png
--------------------------------------------------------------------------------
/server/admin/src/assets/images/social_gh.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/STEM-C/CaSMM/HEAD/server/admin/src/assets/images/social_gh.png
--------------------------------------------------------------------------------
/client/public/images/procedures_callreturn.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/STEM-C/CaSMM/HEAD/client/public/images/procedures_callreturn.png
--------------------------------------------------------------------------------
/client/public/images/procedures_defnoreturn.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/STEM-C/CaSMM/HEAD/client/public/images/procedures_defnoreturn.png
--------------------------------------------------------------------------------
/client/public/images/small_io_pulsetimeout.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/STEM-C/CaSMM/HEAD/client/public/images/small_io_pulsetimeout.png
--------------------------------------------------------------------------------
/server/admin/src/assets/images/logo-strapi.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/STEM-C/CaSMM/HEAD/server/admin/src/assets/images/logo-strapi.png
--------------------------------------------------------------------------------
/server/admin/src/assets/images/logo_github.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/STEM-C/CaSMM/HEAD/server/admin/src/assets/images/logo_github.png
--------------------------------------------------------------------------------
/server/admin/src/assets/images/logo_strapi.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/STEM-C/CaSMM/HEAD/server/admin/src/assets/images/logo_strapi.png
--------------------------------------------------------------------------------
/server/admin/src/assets/images/social_slack.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/STEM-C/CaSMM/HEAD/server/admin/src/assets/images/social_slack.png
--------------------------------------------------------------------------------
/client/public/images/controls_flow_statements.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/STEM-C/CaSMM/HEAD/client/public/images/controls_flow_statements.png
--------------------------------------------------------------------------------
/client/public/images/procedures_callnoreturn.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/STEM-C/CaSMM/HEAD/client/public/images/procedures_callnoreturn.png
--------------------------------------------------------------------------------
/client/public/images/sensor_read_dht_humidity.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/STEM-C/CaSMM/HEAD/client/public/images/sensor_read_dht_humidity.png
--------------------------------------------------------------------------------
/client/public/images/small_text_get_Substring.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/STEM-C/CaSMM/HEAD/client/public/images/small_text_get_Substring.png
--------------------------------------------------------------------------------
/server/admin/src/assets/images/banner_t-shirt.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/STEM-C/CaSMM/HEAD/server/admin/src/assets/images/banner_t-shirt.png
--------------------------------------------------------------------------------
/server/admin/src/assets/images/bg_hp_tee_shirt.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/STEM-C/CaSMM/HEAD/server/admin/src/assets/images/bg_hp_tee_shirt.png
--------------------------------------------------------------------------------
/server/admin/src/assets/images/social_medium.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/STEM-C/CaSMM/HEAD/server/admin/src/assets/images/social_medium.png
--------------------------------------------------------------------------------
/server/admin/src/assets/images/social_reddit.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/STEM-C/CaSMM/HEAD/server/admin/src/assets/images/social_reddit.png
--------------------------------------------------------------------------------
/server/admin/src/assets/images/social_twitter.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/STEM-C/CaSMM/HEAD/server/admin/src/assets/images/social_twitter.png
--------------------------------------------------------------------------------
/client/public/images/sensor_read_dht_temperature.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/STEM-C/CaSMM/HEAD/client/public/images/sensor_read_dht_temperature.png
--------------------------------------------------------------------------------
/server/admin/src/assets/images/logo_stack_overflow.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/STEM-C/CaSMM/HEAD/server/admin/src/assets/images/logo_stack_overflow.png
--------------------------------------------------------------------------------
/server/extensions/users-permissions/config/jwt.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | jwtSecret: process.env.JWT_SECRET || '58cb969b-bb0e-4492-9d8f-1306100e1f90'
3 | };
--------------------------------------------------------------------------------
/server/public/uploads/0f298ba9ad194735911aecc1839c6097.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/STEM-C/CaSMM/HEAD/server/public/uploads/0f298ba9ad194735911aecc1839c6097.PNG
--------------------------------------------------------------------------------
/server/public/uploads/2c987c12f08042b8878b36d5c32556f7.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/STEM-C/CaSMM/HEAD/server/public/uploads/2c987c12f08042b8878b36d5c32556f7.PNG
--------------------------------------------------------------------------------
/server/public/uploads/2d7ee65ba79e404ea0daa9a3c08bd0bd.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/STEM-C/CaSMM/HEAD/server/public/uploads/2d7ee65ba79e404ea0daa9a3c08bd0bd.jpeg
--------------------------------------------------------------------------------
/server/public/uploads/3b6e19b63f0d479e970cb2f13c0c7bbb.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/STEM-C/CaSMM/HEAD/server/public/uploads/3b6e19b63f0d479e970cb2f13c0c7bbb.jpeg
--------------------------------------------------------------------------------
/server/public/uploads/8434130ebc364acc86de7b6f3ddf7e51.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/STEM-C/CaSMM/HEAD/server/public/uploads/8434130ebc364acc86de7b6f3ddf7e51.jpeg
--------------------------------------------------------------------------------
/server/public/uploads/b5d64109ebc14297bbe269c955fe6d52.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/STEM-C/CaSMM/HEAD/server/public/uploads/b5d64109ebc14297bbe269c955fe6d52.PNG
--------------------------------------------------------------------------------
/server/admin/src/assets/images/background_welcome_homepage.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/STEM-C/CaSMM/HEAD/server/admin/src/assets/images/background_welcome_homepage.png
--------------------------------------------------------------------------------
/server/config/functions/responses/404.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | module.exports = async (/* ctx */) => {
4 | // return ctx.notFound('My custom message 404');
5 | };
6 |
--------------------------------------------------------------------------------
/client/src/Utils/hosts.js:
--------------------------------------------------------------------------------
1 | // get the hostname
2 | const { hostname } = window.location
3 |
4 | // export the server host
5 | export const server = hostname.includes('localhost') ? 'http://localhost:1337/api' : '/api'
--------------------------------------------------------------------------------
/server/api/email/config/routes.json:
--------------------------------------------------------------------------------
1 | {
2 | "routes": [
3 | {
4 | "method": "POST",
5 | "path": "/bug-report",
6 | "handler": "Email.send",
7 | "config": {}
8 | }
9 | ]
10 | }
11 |
--------------------------------------------------------------------------------
/server/api/day/services/day.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | /**
4 | * Read the documentation (https://strapi.io/documentation/v3.x/concepts/services.html#core-services)
5 | * to customize this service
6 | */
7 |
8 | module.exports = {};
9 |
--------------------------------------------------------------------------------
/server/api/day/models/day.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | /**
4 | * Read the documentation (https://strapi.io/documentation/v3.x/concepts/models.html#life-cycle-callbacks)
5 | * to customize this model
6 | */
7 |
8 | module.exports = {};
9 |
--------------------------------------------------------------------------------
/server/api/grade/models/grade.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | /**
4 | * Read the documentation (https://strapi.io/documentation/v3.x/concepts/models.html#life-cycle-callbacks)
5 | * to customize this model
6 | */
7 |
8 | module.exports = {};
9 |
--------------------------------------------------------------------------------
/server/api/grade/services/grade.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | /**
4 | * Read the documentation (https://strapi.io/documentation/v3.x/concepts/services.html#core-services)
5 | * to customize this service
6 | */
7 |
8 | module.exports = {};
9 |
--------------------------------------------------------------------------------
/server/api/save/models/save.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | /**
4 | * Read the documentation (https://strapi.io/documentation/v3.x/concepts/models.html#life-cycle-callbacks)
5 | * to customize this model
6 | */
7 |
8 | module.exports = {};
9 |
--------------------------------------------------------------------------------
/server/api/save/services/save.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | /**
4 | * Read the documentation (https://strapi.io/documentation/v3.x/concepts/services.html#core-services)
5 | * to customize this service
6 | */
7 |
8 | module.exports = {};
9 |
--------------------------------------------------------------------------------
/server/api/unit/models/unit.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | /**
4 | * Read the documentation (https://strapi.io/documentation/v3.x/concepts/models.html#life-cycle-callbacks)
5 | * to customize this model
6 | */
7 |
8 | module.exports = {};
9 |
--------------------------------------------------------------------------------
/server/api/unit/services/unit.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | /**
4 | * Read the documentation (https://strapi.io/documentation/v3.x/concepts/services.html#core-services)
5 | * to customize this service
6 | */
7 |
8 | module.exports = {};
9 |
--------------------------------------------------------------------------------
/server/api/mentor/models/mentor.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | /**
4 | * Read the documentation (https://strapi.io/documentation/v3.x/concepts/models.html#life-cycle-callbacks)
5 | * to customize this model
6 | */
7 |
8 | module.exports = {};
9 |
--------------------------------------------------------------------------------
/server/api/mentor/services/mentor.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | /**
4 | * Read the documentation (https://strapi.io/documentation/v3.x/concepts/services.html#core-services)
5 | * to customize this service
6 | */
7 |
8 | module.exports = {};
9 |
--------------------------------------------------------------------------------
/server/api/school/models/school.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | /**
4 | * Read the documentation (https://strapi.io/documentation/v3.x/concepts/models.html#life-cycle-callbacks)
5 | * to customize this model
6 | */
7 |
8 | module.exports = {};
9 |
--------------------------------------------------------------------------------
/server/api/school/services/school.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | /**
4 | * Read the documentation (https://strapi.io/documentation/v3.x/concepts/services.html#core-services)
5 | * to customize this service
6 | */
7 |
8 | module.exports = {};
9 |
--------------------------------------------------------------------------------
/server/api/session/models/session.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | /**
4 | * Read the documentation (https://strapi.io/documentation/v3.x/concepts/models.html#life-cycle-callbacks)
5 | * to customize this model
6 | */
7 |
8 | module.exports = {};
9 |
--------------------------------------------------------------------------------
/server/api/student/models/student.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | /**
4 | * Read the documentation (https://strapi.io/documentation/v3.x/concepts/models.html#life-cycle-callbacks)
5 | * to customize this model
6 | */
7 |
8 | module.exports = {};
9 |
--------------------------------------------------------------------------------
/server/api/student/services/student.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | /**
4 | * Read the documentation (https://strapi.io/documentation/v3.x/concepts/services.html#core-services)
5 | * to customize this service
6 | */
7 |
8 | module.exports = {};
9 |
--------------------------------------------------------------------------------
/test/babel.config.cjs:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | presets: [
3 | [
4 | '@babel/preset-env',
5 | {
6 | targets: {
7 | node: 'current'
8 | }
9 | }
10 | ]
11 | ]
12 | }
--------------------------------------------------------------------------------
/server/api/classroom/models/classroom.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | /**
4 | * Read the documentation (https://strapi.io/documentation/v3.x/concepts/models.html#life-cycle-callbacks)
5 | * to customize this model
6 | */
7 |
8 | module.exports = {};
9 |
--------------------------------------------------------------------------------
/server/api/selection/models/selection.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | /**
4 | * Read the documentation (https://strapi.io/documentation/v3.x/concepts/models.html#life-cycle-callbacks)
5 | * to customize this model
6 | */
7 |
8 | module.exports = {};
9 |
--------------------------------------------------------------------------------
/server/api/submission/models/submission.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | /**
4 | * Read the documentation (https://strapi.io/documentation/v3.x/concepts/models.html#life-cycle-callbacks)
5 | * to customize this model
6 | */
7 |
8 | module.exports = {};
9 |
--------------------------------------------------------------------------------
/compile/src/utils/base.js:
--------------------------------------------------------------------------------
1 | module.exports.compileLog = (message) => {
2 |
3 | // construct log
4 | const logPrefix = `[${(new Date()).toISOString()}]`
5 | const log = `${logPrefix} ${message}`
6 |
7 | // output
8 | console.log(log)
9 | }
--------------------------------------------------------------------------------
/server/api/grade/controllers/grade.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | /**
4 | * Read the documentation (https://strapi.io/documentation/v3.x/concepts/controllers.html#core-controllers)
5 | * to customize this controller
6 | */
7 |
8 | module.exports = {};
9 |
--------------------------------------------------------------------------------
/server/api/school/controllers/school.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | /**
4 | * Read the documentation (https://strapi.io/documentation/v3.x/concepts/controllers.html#core-controllers)
5 | * to customize this controller
6 | */
7 |
8 | module.exports = {};
9 |
--------------------------------------------------------------------------------
/server/api/block/controllers/block.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | /**
4 | * Read the documentation (https://strapi.io/documentation/3.0.0-beta.x/concepts/controllers.html#core-controllers)
5 | * to customize this controller
6 | */
7 |
8 | module.exports = {};
9 |
--------------------------------------------------------------------------------
/server/api/learning-standard/models/learning-standard.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | /**
4 | * Read the documentation (https://strapi.io/documentation/v3.x/concepts/models.html#life-cycle-callbacks)
5 | * to customize this model
6 | */
7 |
8 | module.exports = {};
9 |
--------------------------------------------------------------------------------
/server/api/learning-standard/services/learning-standard.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | /**
4 | * Read the documentation (https://strapi.io/documentation/v3.x/concepts/services.html#core-services)
5 | * to customize this service
6 | */
7 |
8 | module.exports = {};
9 |
--------------------------------------------------------------------------------
/server/api/session/services/session.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | /**
4 | * Read the documentation (https://strapi.io/documentation/v3.x/concepts/services.html#core-services)
5 | * to customize this service
6 | */
7 |
8 | module.exports = {};
9 |
10 |
11 |
--------------------------------------------------------------------------------
/server/api/blocks-category/services/blocks-category.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | /**
4 | * Read the documentation (https://strapi.io/documentation/3.0.0-beta.x/concepts/services.html#core-services)
5 | * to customize this service
6 | */
7 |
8 | module.exports = {};
9 |
--------------------------------------------------------------------------------
/server/admin/src/containers/AuthPage/components/Logo/img.js:
--------------------------------------------------------------------------------
1 | import styled from 'styled-components';
2 |
3 | const Img = styled.img`
4 | height: 100px;
5 | `;
6 | //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
7 | //Changed height to: 100px
8 |
9 | export default Img;
10 |
--------------------------------------------------------------------------------
/server/api/blocks-category/controllers/blocks-category.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | /**
4 | * Read the documentation (https://strapi.io/documentation/3.0.0-beta.x/concepts/controllers.html#core-controllers)
5 | * to customize this controller
6 | */
7 |
8 | module.exports = {};
9 |
--------------------------------------------------------------------------------
/server/api/cc-workspace/models/cc-workspace.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | /**
4 | * Read the documentation (https://strapi.io/documentation/developer-docs/latest/development/backend-customization.html#lifecycle-hooks)
5 | * to customize this model
6 | */
7 |
8 | module.exports = {};
9 |
--------------------------------------------------------------------------------
/server/api/cc-workspace/services/cc-workspace.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | /**
4 | * Read the documentation (https://strapi.io/documentation/developer-docs/latest/development/backend-customization.html#core-services)
5 | * to customize this service
6 | */
7 |
8 | module.exports = {};
9 |
--------------------------------------------------------------------------------
/server/admin/src/assets/images/logo-t-shirt.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/server/api/learning-components/models/learning-components.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | /**
4 | * Read the documentation (https://strapi.io/documentation/developer-docs/latest/development/backend-customization.html#lifecycle-hooks)
5 | * to customize this model
6 | */
7 |
8 | module.exports = {};
9 |
--------------------------------------------------------------------------------
/server/api/learning-components/services/learning-components.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | /**
4 | * Read the documentation (https://strapi.io/documentation/developer-docs/latest/development/backend-customization.html#core-services)
5 | * to customize this service
6 | */
7 |
8 | module.exports = {};
9 |
--------------------------------------------------------------------------------
/server/api/learning-component-types/models/learning-component-types.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | /**
4 | * Read the documentation (https://strapi.io/documentation/developer-docs/latest/development/backend-customization.html#lifecycle-hooks)
5 | * to customize this model
6 | */
7 |
8 | module.exports = {};
9 |
--------------------------------------------------------------------------------
/server/api/learning-component-types/services/learning-component-types.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | /**
4 | * Read the documentation (https://strapi.io/documentation/developer-docs/latest/development/backend-customization.html#core-services)
5 | * to customize this service
6 | */
7 |
8 | module.exports = {};
9 |
--------------------------------------------------------------------------------
/server/api/learning-components/controllers/learning-components.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | /**
4 | * Read the documentation (https://strapi.io/documentation/developer-docs/latest/development/backend-customization.html#core-controllers)
5 | * to customize this controller
6 | */
7 |
8 | module.exports = {};
9 |
--------------------------------------------------------------------------------
/server/api/strapiusers/config/routes.json:
--------------------------------------------------------------------------------
1 | {
2 | "routes": [
3 | {
4 | "method": "GET",
5 | "path": "/strapiusers/super-admin",
6 | "handler": "strapiusers.findSuperAdmins",
7 | "config": {
8 | "policies": []
9 | }
10 | }
11 | ]
12 | }
--------------------------------------------------------------------------------
/client/src/components/Message.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Alert } from 'antd';
3 |
4 | const Message = ({ type, message }) => {
5 | return ;
6 | };
7 |
8 | Message.defaultProps = {
9 | type: 'error',
10 | };
11 |
12 | export default Message;
13 |
--------------------------------------------------------------------------------
/server/api/learning-component-types/controllers/learning-component-types.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | /**
4 | * Read the documentation (https://strapi.io/documentation/developer-docs/latest/development/backend-customization.html#core-controllers)
5 | * to customize this controller
6 | */
7 |
8 | module.exports = {};
9 |
--------------------------------------------------------------------------------
/server/api/classroom-manager/config/routes.json:
--------------------------------------------------------------------------------
1 | {
2 | "routes": [
3 | {
4 | "method": "GET",
5 | "path": "/classroom-managers/me",
6 | "handler": "classroom-manager.me",
7 | "config": {
8 | "policies": ["global::isClassroomManager"]
9 | }
10 | }
11 | ]
12 | }
13 |
--------------------------------------------------------------------------------
/server/config/policies/isStudent.js:
--------------------------------------------------------------------------------
1 | //
2 | // Check if the current user is a student
3 | //
4 | module.exports = async (ctx, next) => {
5 | if (ctx.state.user && ctx.state.user.isStudent) {
6 | // Go to next policy or controller
7 | return await next()
8 | }
9 |
10 | ctx.unauthorized(`You're not allowed to perform this action!`)
11 | }
--------------------------------------------------------------------------------
/client/src/components/RouteButton/RouteButton.less:
--------------------------------------------------------------------------------
1 | @import "../../assets/style.less";
2 |
3 | #route-button {
4 | height: 250px;
5 | padding: 10px 20px;
6 | border-radius: 5px;
7 | font-size: 90px;
8 | font-weight: 800;
9 | font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', monospace;
10 | margin: 150px 80px;
11 | }
12 |
--------------------------------------------------------------------------------
/client/src/Utils/PrivateRoute.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Navigate } from 'react-router-dom';
3 | import { getToken } from './AuthRequests';
4 |
5 | // creates private route handler
6 | function PrivateRoute({ children }) {
7 | const token = getToken();
8 | return token ? children : ;
9 | }
10 |
11 | export default PrivateRoute;
12 |
--------------------------------------------------------------------------------
/test/performance/load.test.js:
--------------------------------------------------------------------------------
1 | import http from 'k6/http'
2 | import { sleep } from 'k6'
3 |
4 | const host = 'http://localhost:1337'
5 |
6 | export const options = {
7 | duration: '0.1m',
8 | vus: 50,
9 | thresholds: {
10 | http_req_duration: ['p(95)<2500'],
11 | },
12 | }
13 |
14 | export default function () {
15 | http.get(`${host}/topics`)
16 | sleep(1)
17 | }
--------------------------------------------------------------------------------
/server/config/server.js:
--------------------------------------------------------------------------------
1 | module.exports = ({ env }) => ({
2 | host: env('HOST', '0.0.0.0'),
3 | port: env.int('PORT', 1337),
4 | // url: 'localhost:1337',
5 | admin: {
6 | auth: {
7 | secret: env(
8 | 'ADMIN_JWT_SECRET',
9 | process.env.ADMIN_JWT_TOKEN ||
10 | 'fd6af0fac1067asfasf0ef12AGWADGJe9d518d1604298'
11 | ),
12 | },
13 | },
14 | });
15 |
--------------------------------------------------------------------------------
/server/config/policies/isContentCreator.js:
--------------------------------------------------------------------------------
1 | //
2 | // Check if the current user is a classroom manager
3 | //
4 | module.exports = async (ctx, next) => {
5 | if (ctx.state.user && ctx.state.user.role.name === 'Content Creator') {
6 | // Go to next policy or controller
7 | return await next()
8 | }
9 |
10 | ctx.unauthorized(`You're not allowed to perform this action!`)
11 | }
--------------------------------------------------------------------------------
/.github/workflows/tag-master.yml:
--------------------------------------------------------------------------------
1 | name: Tag New Master Release
2 | on:
3 | pull_request:
4 | branches:
5 | - master
6 | types: [closed]
7 | jobs:
8 | run:
9 | runs-on: ubuntu-latest
10 | steps:
11 | - name: Checkout
12 | uses: actions/checkout@v2
13 | - name: Tag Master
14 | uses: STEM-C/auto/tag@v0.7.2
15 | with:
16 | repo_token: '${{ secrets.GITHUB_TOKEN }}'
17 |
--------------------------------------------------------------------------------
/compile/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "compile",
3 | "version": "0.0.1",
4 | "description": "Arduino compilier service",
5 | "author": "Nicholas Ionata",
6 | "license": "MIT",
7 | "dependencies": {
8 | "body-parser": "^1.18.2",
9 | "bull": "^4.9.0",
10 | "express": "^4.16.2",
11 | "properties": "^1.2.1",
12 | "redis-url-parse": "^2.0.0",
13 | "throng": "^5.0.0",
14 | "tmp": "0.2.1"
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/client/src/components/RouteButton/RouteButton.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Link } from 'react-router-dom';
3 | import './RouteButton.less';
4 |
5 | export default function RouteButton({ link, id, size, variant, children }) {
6 | return (
7 |
8 |
11 |
12 | );
13 | }
14 |
--------------------------------------------------------------------------------
/client/src/index.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 0;
3 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
4 | 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
5 | sans-serif;
6 | -webkit-font-smoothing: antialiased;
7 | -moz-osx-font-smoothing: grayscale;
8 | }
9 |
10 | code {
11 | font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
12 | monospace;
13 | }
14 |
15 |
--------------------------------------------------------------------------------
/client/src/views/NotFound.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | const NotFound = () => {
4 | return (
5 |
6 |
7 | 404
8 |
9 |
10 | Page Not Found
11 |
12 |
13 | )
14 | };
15 |
16 | export default NotFound;
17 |
--------------------------------------------------------------------------------
/test/integration/request.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | import axios from 'axios'
4 | //axios.defaults.adapter = require('axios/lib/adapters/http');
5 | const host = 'http://localhost:1337/api'
6 |
7 | export const getPublicRequestModule = () => axios.create({
8 | baseURL: host
9 | })
10 |
11 | export const getAuthorizedRequestModule = (token) => axios.create({
12 | baseURL: host,
13 | headers: {
14 | Authorization: `Bearer ${token}`
15 | }
16 | })
--------------------------------------------------------------------------------
/server/config/policies/isClassroomManager.js:
--------------------------------------------------------------------------------
1 | //
2 | // Check if the current user is a classroom manager
3 | //
4 | module.exports = async (ctx, next) => {
5 | if (
6 | (ctx.state.user && ctx.state.user.role.name === 'Classroom Manager') ||
7 | ctx.state.user.role.name === 'Researcher'
8 | ) {
9 | // Go to next policy or controller
10 | return await next();
11 | }
12 |
13 | ctx.unauthorized(`You're not allowed to perform this action!`);
14 | };
15 |
--------------------------------------------------------------------------------
/client/src/views/UploadBlocks/UpbloadBlocks.less:
--------------------------------------------------------------------------------
1 | @import '../../assets/style.less';
2 |
3 | #main-header {
4 | text {
5 | color: white;
6 | }
7 | a:active {
8 | color: white;
9 | background: transparent;
10 | }
11 | a:link {
12 | color: white;
13 | background: transparent;
14 | }
15 | a:hover {
16 | color: white;
17 | background: transparent;
18 | }
19 | a:visited {
20 | color: white;
21 | background: transparent;
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/.github/workflows/end-review.yml:
--------------------------------------------------------------------------------
1 | name: Delete Review App
2 | on:
3 | pull_request:
4 | branches: [develop]
5 | types: [closed]
6 | jobs:
7 | run:
8 | runs-on: ubuntu-latest
9 | steps:
10 | - uses: actions/checkout@v2
11 | - name: Delete review app
12 | id: app
13 | uses: STEM-C/auto/review@v0.7.2
14 | with:
15 | base: review
16 | pipeline: ${{ secrets.PIPELINE_ID }}
17 | token: ${{ secrets.HEROKU_TOKEN }}
18 |
--------------------------------------------------------------------------------
/test/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "dependencies": {
3 | "@babel/preset-env": "^7.10.4",
4 | "axios": "^0.19.2",
5 | "babel-jest": "^26.1.0",
6 | "jest": "^26.6.3",
7 | "k6": "^0.0.0"
8 | },
9 | "scripts": {
10 | "start": "docker-compose -f ../docker-compose.test.yml up",
11 | "functional": "jest ./functional/*.test.js",
12 | "integration": "jest ./integration/*.test.js",
13 | "performance": "k6 run ./performance/*.test.js"
14 | },
15 | "type": "module"
16 | }
17 |
--------------------------------------------------------------------------------
/server/api/selection/services/selection.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | /**
4 | * Read the documentation (https://strapi.io/documentation/v3.x/concepts/services.html#core-services)
5 | * to customize this service
6 | */
7 |
8 | module.exports.findCurrSelection = async (classroomId) => {
9 | const selection = await strapi
10 | .query('selection')
11 | .findOne({ current: true, classroom: classroomId, _sort: 'id:desc' }, [
12 | 'learning_standard',
13 | ]);
14 | return selection;
15 | };
16 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | #configuration ignores
2 | .idea/
3 | .vscode/
4 |
5 | # Logs
6 | logs
7 | *.log
8 | npm-debug.log*
9 | yarn-debug.log*
10 | yarn-error.log*
11 |
12 | # Environment
13 | .env
14 | *.sql
15 | .cache/
16 | *.dump
17 | !blocks_db.dump
18 | !development_db.dump
19 | !test_db.dump
20 | .strapi-updater.json
21 |
22 | # Packages
23 | *.zip
24 | *.tar
25 | *.gz
26 |
27 | # OS
28 | .DS_Store
29 | .tmp
30 |
31 | # node
32 | node_modules/
33 | build/
34 | server/public/client/*
35 | !server/public/client/.gitkeep
--------------------------------------------------------------------------------
/client/src/views/NotChrome.jsx:
--------------------------------------------------------------------------------
1 | const NotFound = () => {
2 | return (
3 |
8 |
9 | Please use{' '}
10 |
11 | Chrome
12 | {' '}
13 | browser to run CaSMM
14 |
15 |
16 | );
17 | };
18 |
19 | export default NotFound;
20 |
--------------------------------------------------------------------------------
/server/api/blocks-category/models/blocks-category.settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "kind": "collectionType",
3 | "connection": "default",
4 | "collectionName": "blocks_categories",
5 | "info": {
6 | "name": "Blocks Category"
7 | },
8 | "options": {
9 | "increments": true,
10 | "timestamps": true
11 | },
12 | "attributes": {
13 | "name": {
14 | "type": "string"
15 | },
16 | "blocks": {
17 | "via": "blocks_category",
18 | "collection": "block"
19 | }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/client/src/views/Home/Home.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import Logo from "../../assets/casmm_logo.png";
3 | import NavBar from "../../components/NavBar/NavBar";
4 | import './Home.less';
5 | import HomeJoin from "./HomeJoin";
6 |
7 | const Home = () => (
8 |
9 |
10 |
11 |

12 |
13 |
14 |
15 | )
16 |
17 | export default Home;
--------------------------------------------------------------------------------
/compile/src/cluster.js:
--------------------------------------------------------------------------------
1 | const throng = require('throng')
2 | const { init, start } = require('./handlers/worker')
3 |
4 | // Spin up multiple processes to handle jobs to take advantage of more CPU cores
5 | // See: https://devcenter.heroku.com/articles/node-concurrency for more info
6 | let workers = process.env.WEB_CONCURRENCY || 1
7 |
8 | // Initialize the clustered worker process
9 | // See: https://devcenter.heroku.com/articles/node-concurrency for more info
10 | throng({
11 | workers,
12 | master: init,
13 | start
14 | })
--------------------------------------------------------------------------------
/server/admin/src/themes/sizes.js:
--------------------------------------------------------------------------------
1 | const sizes = {
2 | borderRadius: '2px',
3 | header: {
4 | height: '6rem',
5 | },
6 | leftMenu: {
7 | height: '8rem',
8 | width: '24rem',
9 | },
10 | margins: {
11 | // TODO:
12 | sm: '10px',
13 | },
14 | paddings: {
15 | // TODO
16 | xs: '5px',
17 | sm: '10px',
18 | md: '30px',
19 | lg: '40px',
20 | },
21 | fonts: {
22 | xs: '11px',
23 | sm: '12px',
24 | md: '13px',
25 | lg: '18px',
26 | },
27 | };
28 |
29 | export default sizes;
30 |
--------------------------------------------------------------------------------
/server/config/functions/cron.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | /**
4 | * Cron config that gives you an opportunity
5 | * to run scheduled jobs.
6 | *
7 | * The cron format consists of:
8 | * [SECOND (optional)] [MINUTE] [HOUR] [DAY OF MONTH] [MONTH OF YEAR] [DAY OF WEEK]
9 | *
10 | * See more details here: https://strapi.io/documentation/3.0.0-beta.x/concepts/configurations.html#cron-tasks
11 | */
12 |
13 | module.exports = {
14 | /**
15 | * Simple example.
16 | * Every monday at 1am.
17 | */
18 | // '0 1 * * 1': () => {
19 | //
20 | // }
21 | };
22 |
--------------------------------------------------------------------------------
/client/src/views/ContentCreator/ContentCreator.less:
--------------------------------------------------------------------------------
1 | @import '../../assets/style.less';
2 | #content-creator-table-container {
3 | width: 80%;
4 | margin: 2.5vh auto 0 auto;
5 |
6 | #content-creator-btn-container {
7 | display: flex;
8 | flex-direction: row;
9 | float: right;
10 | margin-bottom: 5px;
11 | z-index: 10;
12 | }
13 |
14 | .ant-table-row {
15 | #quick-look {
16 | visibility: hidden;
17 | &:hover {
18 | visibility: visible;
19 | }
20 | }
21 | }
22 |
23 | .ant-table-wrapper {
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/client/public/lib/depreciated.js:
--------------------------------------------------------------------------------
1 | /**
2 | * This file is used to store all the depreciated codes
3 | * still being used by our version of blockly
4 | */
5 |
6 | var goog = goog || {}; //declare goog if not defined, else add to it
7 |
8 | goog.isArray = function (a) { return "array" == goog.typeOf(a) };
9 |
10 | goog.isString = function(val) {
11 | return typeof val == 'string';
12 | };
13 |
14 | goog.isNumber = function(val) {
15 | return typeof val == 'number';
16 | };
17 |
18 | goog.isFunction = function(val) {
19 | return goog.typeOf(val) == 'function';
20 | };
21 |
--------------------------------------------------------------------------------
/server/api/grade/models/grade.settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "kind": "collectionType",
3 | "collectionName": "grades",
4 | "info": {
5 | "name": "grade"
6 | },
7 | "options": {
8 | "increments": true,
9 | "timestamps": true
10 | },
11 | "attributes": {
12 | "name": {
13 | "type": "string",
14 | "required": true,
15 | "unique": true
16 | },
17 | "classrooms": {
18 | "via": "grade",
19 | "collection": "classroom"
20 | },
21 | "units": {
22 | "via": "grade",
23 | "collection": "unit"
24 | }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/server/api/learning-component-types/models/learning-component-types.settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "kind": "collectionType",
3 | "collectionName": "learning_component_types",
4 | "info": {
5 | "name": "Learning Component Types"
6 | },
7 | "options": {
8 | "increments": true,
9 | "timestamps": true,
10 | "draftAndPublish": true
11 | },
12 | "attributes": {
13 | "name": {
14 | "type": "string"
15 | },
16 | "learning_components": {
17 | "collection": "learning-components",
18 | "via": "learning_component_type"
19 | }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/server/api/selection/models/selection.settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "kind": "collectionType",
3 | "collectionName": "selections",
4 | "info": {
5 | "name": "Selection"
6 | },
7 | "options": {
8 | "increments": true,
9 | "timestamps": true
10 | },
11 | "attributes": {
12 | "classroom": {
13 | "via": "selections",
14 | "model": "classroom"
15 | },
16 | "learning_standard": {
17 | "model": "learning-standard"
18 | },
19 | "current": {
20 | "type": "boolean",
21 | "default": true,
22 | "required": true
23 | }
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/client/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "Client",
3 | "name": "STEM+C Client Application",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "64x64 32x32 24x24 16x16",
8 | "type": "image/x-icon"
9 | },
10 | {
11 | "src": "logo192.png",
12 | "type": "image/png",
13 | "sizes": "192x192"
14 | },
15 | {
16 | "src": "logo512.png",
17 | "type": "image/png",
18 | "sizes": "512x512"
19 | }
20 | ],
21 | "start_url": ".",
22 | "display": "standalone",
23 | "theme_color": "#000000",
24 | "background_color": "#ffffff"
25 | }
26 |
--------------------------------------------------------------------------------
/scripts/init_db.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | set -e
4 |
5 | env=$ENVIRONMENT
6 | db_url=$DATABASE_URL
7 | script_path=$SCRIPT_PATH
8 |
9 | if [[ -n $env ]] && [[ -n $db_url ]] && [[ -n $script_path ]];
10 | then
11 |
12 | dump="${env}_db.dump"
13 | path="$script_path/$dump"
14 |
15 | if [[ -f $path ]];
16 | then
17 |
18 | psql "$DATABASE_URL" < "$path"
19 | echo "Successfully imported $dump"
20 | else
21 | echo "Missing $dump"
22 | fi;
23 | else
24 | echo "Missing environment variable(s):"
25 | echo "ENVIRONMENT=$env or DATABASE_URL=$db_url or SCRIPT_PATH=$script_path"
26 | fi;
--------------------------------------------------------------------------------
/client/src/views/ContentCreator/UnitEditor/UnitEditor.less:
--------------------------------------------------------------------------------
1 | @import "../../../assets/style.less";
2 |
3 | #base{
4 | background-color: #colors[primary];
5 | }
6 |
7 | #search-button{
8 | left: 100px;
9 | }
10 |
11 | #list-position{
12 |
13 | position: relative;
14 |
15 | #add-button{
16 | position: absolute;
17 | }
18 | }
19 |
20 | #carousel{
21 | width: '160px';
22 | }
23 |
24 | #display-code-modal {
25 |
26 |
27 | #display-code-btn {
28 | width: 18vw;
29 | min-height: 4vh;
30 | font-size: 1em;
31 |
32 | &:hover {
33 | width: 20vw;
34 | }
35 | }
36 | }
--------------------------------------------------------------------------------
/client/src/index.jsx:
--------------------------------------------------------------------------------
1 | import { createRoot } from 'react-dom/client';
2 | import { BrowserRouter } from 'react-router-dom';
3 | import App from './App';
4 | import './index.less';
5 | import * as serviceWorker from './serviceWorker';
6 |
7 | const container = document.getElementById("root")
8 | createRoot(container).render(
9 |
10 |
11 |
12 | )
13 |
14 | // If you want your app to work offline and load faster, you can change
15 | // unregister() to register() below. Note this comes with some pitfalls.
16 | // Learn more about service workers: https://bit.ly/CRA-PWA
17 | serviceWorker.unregister();
18 |
--------------------------------------------------------------------------------
/server/api/school/models/school.settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "kind": "collectionType",
3 | "collectionName": "schools",
4 | "info": {
5 | "name": "School"
6 | },
7 | "options": {
8 | "increments": true,
9 | "timestamps": true
10 | },
11 | "attributes": {
12 | "name": {
13 | "type": "string"
14 | },
15 | "county": {
16 | "type": "string"
17 | },
18 | "state": {
19 | "type": "string"
20 | },
21 | "classrooms": {
22 | "via": "school",
23 | "collection": "classroom"
24 | },
25 | "mentors": {
26 | "via": "school",
27 | "collection": "mentor"
28 | }
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM strapi/base
2 | FROM node:20 as client-build
3 |
4 | WORKDIR /usr/src/app/client
5 | COPY ./client/package.json .
6 | COPY ./client/yarn.lock .
7 | RUN yarn install
8 | COPY ./client .
9 | RUN PUBLIC_URL=/client yarn build
10 |
11 | FROM node:14 as server-build
12 | WORKDIR /usr/src/app
13 | COPY ./server/package.json .
14 | COPY ./server/yarn.lock .
15 | RUN yarn install
16 | COPY ./server .
17 | ENV NODE_ENV production
18 | RUN PUBLIC_URL=/api yarn build
19 |
20 | FROM node:14 as final
21 | WORKDIR /usr/src/app
22 | COPY --from=server-build /usr/src/app ./
23 | COPY --from=client-build /usr/src/app/client/build ./public/client
24 |
25 | CMD yarn start
--------------------------------------------------------------------------------
/server/api/classroom-manager/controllers/classroom-manager.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const { sanitizeEntity } = require('strapi-utils/lib');
4 |
5 | module.exports = {
6 | /**
7 | * Get the currrent classroom manager that is logged in
8 | *
9 | * @return {Mentor}
10 | */
11 | async me(ctx) {
12 | const { id } = ctx.state.user;
13 |
14 | // get the classroom manager
15 | const { classroomManager } = await strapi.services[
16 | 'classroom-manager'
17 | ].findById(id);
18 |
19 | // remove private fields and return the classroom manager
20 | return sanitizeEntity(classroomManager, { model: strapi.models.mentor });
21 | },
22 | };
23 |
--------------------------------------------------------------------------------
/client/src/index.less:
--------------------------------------------------------------------------------
1 | @import 'antd/dist/antd.less';
2 | @import './assets/style.less';
3 |
4 | body {
5 | margin: 0;
6 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
7 | 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
8 | sans-serif;
9 | -webkit-font-smoothing: antialiased;
10 | -moz-osx-font-smoothing: grayscale;
11 | overflow-x: hidden !important;
12 | }
13 |
14 | code {
15 | font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
16 | monospace;
17 | }
18 |
19 | .blocklyTreeRow {
20 | border-radius: 4px;
21 | text-align: center;
22 | line-height: initial!important;
23 | }
--------------------------------------------------------------------------------
/server/api/learning-components/models/learning-components.settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "kind": "collectionType",
3 | "collectionName": "learning_components",
4 | "info": {
5 | "name": "Learning Components"
6 | },
7 | "options": {
8 | "increments": true,
9 | "timestamps": true,
10 | "draftAndPublish": true
11 | },
12 | "attributes": {
13 | "type": {
14 | "type": "string"
15 | },
16 | "days": {
17 | "via": "learning_components",
18 | "collection": "day",
19 | "dominant": true
20 | },
21 | "learning_component_type": {
22 | "via": "learning_components",
23 | "model": "learning-component-types"
24 | }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/server/api/save/models/save.settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "kind": "collectionType",
3 | "collectionName": "saves",
4 | "info": {
5 | "name": "save",
6 | "description": ""
7 | },
8 | "options": {
9 | "increments": true,
10 | "timestamps": true,
11 | "draftAndPublish": false
12 | },
13 | "attributes": {
14 | "day": {
15 | "model": "day"
16 | },
17 | "student": {
18 | "model": "student"
19 | },
20 | "workspace": {
21 | "type": "text",
22 | "required": true
23 | },
24 | "replay": {
25 | "type": "json"
26 | },
27 | "session": {
28 | "via": "saves",
29 | "model": "session"
30 | }
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/server/api/validator/services/validator.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | module.exports.isInt = (object) => {
4 | return object !== undefined && Number.isInteger(object);
5 | };
6 |
7 | module.exports.isPositiveInt = (object) => {
8 | return object !== undefined && Number.isInteger(object) && object >= 0;
9 | };
10 |
11 | module.exports.isIntArray = (arr) => {
12 | // ensure the input is an non-empty array
13 | if (!arr || !Array.isArray(arr) || !arr.length) return false;
14 |
15 | // ensure the array contains integers
16 | const nonIntegers = arr.filter((num) => !Number.isInteger(num));
17 | if (nonIntegers.length) return false;
18 |
19 | // passes all tests
20 | return true;
21 | };
22 |
--------------------------------------------------------------------------------
/server/api/block/models/block.settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "kind": "collectionType",
3 | "connection": "default",
4 | "collectionName": "blocks",
5 | "info": {
6 | "name": "Block",
7 | "description": ""
8 | },
9 | "options": {
10 | "increments": true,
11 | "timestamps": true,
12 | "draftAndPublish": false
13 | },
14 | "attributes": {
15 | "name": {
16 | "type": "string",
17 | "required": true,
18 | "unique": true
19 | },
20 | "description": {
21 | "type": "text"
22 | },
23 | "blocks_category": {
24 | "via": "blocks",
25 | "model": "blocks-category"
26 | },
27 | "image_url": {
28 | "type": "string"
29 | }
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Feature request
3 | about: Suggest an idea for this project
4 | title: ''
5 | labels: ''
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Story:**
11 | A clear and concise description of the feature from the perspective of the main user.
12 |
13 | **Implementation**
14 | A clear and concise description of the feature from the developers point of view.
15 |
16 | **Client/Server/Compile/Database**
17 | Choose one of the above and break goals down into individual tasks with checkboxes next to them. One issue may include multiple tasks for multiple of the above categories
18 |
19 | **Additional context**
20 | Add any other context or screenshots about the feature request here.
21 |
--------------------------------------------------------------------------------
/server/api/mentor/models/mentor.settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "kind": "collectionType",
3 | "collectionName": "mentors",
4 | "info": {
5 | "name": "Mentor"
6 | },
7 | "options": {
8 | "increments": true,
9 | "timestamps": true
10 | },
11 | "attributes": {
12 | "first_name": {
13 | "type": "string"
14 | },
15 | "last_name": {
16 | "type": "string"
17 | },
18 | "school": {
19 | "model": "school",
20 | "via": "mentors"
21 | },
22 | "classrooms": {
23 | "via": "mentors",
24 | "collection": "classroom",
25 | "dominant": true
26 | },
27 | "user": {
28 | "plugin": "users-permissions",
29 | "model": "user"
30 | }
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/compile/Dockerfile:
--------------------------------------------------------------------------------
1 | # Start from the node image
2 | FROM node:latest
3 |
4 | # Create app directory
5 | WORKDIR /usr/src/app
6 |
7 | # Install app dependencies
8 | COPY package.json .
9 | COPY yarn.lock .
10 | RUN yarn
11 |
12 | # Install arduino compiler
13 | RUN curl https://downloads.arduino.cc/arduino-1.8.5-linux64.tar.xz | tar -xJ
14 | # Install the DHT library AND its dependency
15 | RUN cd arduino-1.8.5/libraries && wget -c https://codeload.github.com/adafruit/DHT-sensor-library/tar.gz/refs/tags/1.4.3 -O - | tar -xz && wget -c https://codeload.github.com/adafruit/Adafruit_Sensor/tar.gz/refs/tags/1.1.4 -O - | tar -xz
16 |
17 | # Bundle app source
18 | COPY ./src .
19 |
20 | # Run when container is started
21 | CMD node cluster.js
--------------------------------------------------------------------------------
/test/README.md:
--------------------------------------------------------------------------------
1 | # Tests
2 |
3 | > Full stack test suite
4 |
5 |
6 |
7 | ## Setup
8 |
9 | 1. Install dependencies `yarn`
10 |
11 | 2. Run the service `yarn start`
12 |
13 | > Make sure to delete the db container to make sure it rebuilds for testing
14 |
15 |
16 |
17 | ## Testing
18 |
19 | ### Functional
20 |
21 | - Framework – Jest
22 | - Usage – Test UI w/ snapshots and mocking
23 | - Run `yarn functional`
24 |
25 | ### Integration
26 |
27 | - Framework – Jest
28 | - Usage – Test all the API endpoints for roles, data access, and function
29 | - Run `yarn integration`
30 |
31 | ### Performance
32 |
33 | - Framework – K6
34 | - Usage – Stress and Load test the application
35 | - Run yarn `performance`
36 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/.github/workflows/deploy-staging.yml:
--------------------------------------------------------------------------------
1 | name: Deploy Staging
2 | on:
3 | push:
4 | branches: ['release/v[0-9].[0-9]']
5 | jobs:
6 | run:
7 | runs-on: ubuntu-latest
8 | steps:
9 | - name: Checkout the repo
10 | uses: actions/checkout@v2
11 | - name: Get the version
12 | id: get_version
13 | run: echo ::set-output name=VERSION::${GITHUB_REF#refs/heads/release/}
14 | - name: Built, test, and deploy app
15 | uses: STEM-C/auto/build-test-deploy@v0.7.2
16 | with:
17 | image_tag: staging-${{ steps.get_version.outputs.VERSION }}
18 | app_name: casmm-staging
19 | github_token: ${{ secrets.GITHUB_TOKEN }}
20 | env:
21 | HEROKU_API_KEY: ${{ secrets.HEROKU_TOKEN }}
22 |
--------------------------------------------------------------------------------
/server/admin/src/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 | CaSMM Admin
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/server/api/unit/models/unit.settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "kind": "collectionType",
3 | "collectionName": "units",
4 | "info": {
5 | "name": "unit"
6 | },
7 | "options": {
8 | "increments": true,
9 | "timestamps": true
10 | },
11 | "attributes": {
12 | "grade": {
13 | "model": "grade",
14 | "via": "units"
15 | },
16 | "name": {
17 | "type": "string",
18 | "required": true
19 | },
20 | "teks_id": {
21 | "type": "string"
22 | },
23 | "teks_description": {
24 | "type": "text"
25 | },
26 | "number": {
27 | "type": "integer",
28 | "required":true
29 |
30 | },
31 | "learning_standards": {
32 | "via": "unit",
33 | "collection": "learning-standard"
34 | }
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/server/api/sandbox/config/routes.json:
--------------------------------------------------------------------------------
1 | {
2 | "routes": [
3 | {
4 | "method": "GET",
5 | "path": "/sandbox/toolbox",
6 | "handler": "sandbox.toolbox",
7 | "config": {
8 | "policies": []
9 | }
10 | },
11 | {
12 | "method": "GET",
13 | "path": "/sandbox/submission/:id",
14 | "handler": "sandbox.findOneSubmission",
15 | "config": {
16 | "policies": []
17 | }
18 | },
19 | {
20 | "method": "POST",
21 | "path": "/sandbox/submission",
22 | "handler": "sandbox.createSubmission",
23 | "config": {
24 | "policies": []
25 | }
26 | }
27 | ]
28 | }
--------------------------------------------------------------------------------
/server/api/cc-workspace/models/cc-workspace.settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "kind": "collectionType",
3 | "collectionName": "cc_workspaces",
4 | "info": {
5 | "name": "CCWorkspace",
6 | "description": ""
7 | },
8 | "options": {
9 | "increments": true,
10 | "timestamps": true,
11 | "draftAndPublish": true
12 | },
13 | "attributes": {
14 | "name": {
15 | "type": "string",
16 | "required": true
17 | },
18 | "description": {
19 | "type": "text"
20 | },
21 | "template": {
22 | "type": "text",
23 | "required": true
24 | },
25 | "blocks": {
26 | "private": true,
27 | "collection": "block"
28 | },
29 | "classroom": {
30 | "via": "cc_workspaces",
31 | "private": true,
32 | "model": "classroom"
33 | }
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/.github/workflows/update-review.yml:
--------------------------------------------------------------------------------
1 | name: Update Review App
2 | on:
3 | pull_request:
4 | branches: [develop]
5 | types: [synchronize]
6 | jobs:
7 | run:
8 | runs-on: ubuntu-latest
9 | steps:
10 | - name: Checkout the repo
11 | uses: actions/checkout@v2
12 | - name: Get review app name
13 | id: get_name
14 | run: echo ::set-output name=NAME::review-pr-$(echo $GITHUB_REF | awk 'BEGIN { FS = "/" } ; { print $3 }')
15 | - name: Built, test, and deploy app
16 | uses: STEM-C/auto/build-test-deploy@v0.7.2
17 | with:
18 | image_tag: ${{ steps.get_name.outputs.NAME }}
19 | app_name: ${{ steps.get_name.outputs.NAME }}
20 | github_token: ${{ secrets.GITHUB_TOKEN }}
21 | env:
22 | HEROKU_API_KEY: ${{ secrets.HEROKU_TOKEN }}
23 |
--------------------------------------------------------------------------------
/client/src/components/DayPanels/BlocklyCanvasPanel/Icons/textIcon.json:
--------------------------------------------------------------------------------
1 | {
2 | "path": "M85.83203,22.77656c-0.54801,0.01312 -1.09438,0.06551 -1.63489,0.15677h-44.0638h-5.73333c-3.1648,0 -5.73333,2.56853 -5.73333,5.73333v17.2c0,3.1648 2.56853,5.73333 5.73333,5.73333h5.73333c3.1648,0 5.73333,-2.56853 5.73333,-5.73333v-5.73333h28.66667v91.73333h-5.73333c-3.1648,0 -5.73333,2.56853 -5.73333,5.73333v5.73333c0,3.1648 2.56853,5.73333 5.73333,5.73333h15.34114c1.23099,0.20221 2.48672,0.20221 3.71771,0h15.34114c3.1648,0 5.73333,-2.56853 5.73333,-5.73333v-5.73333c0,-3.1648 -2.56853,-5.73333 -5.73333,-5.73333h-5.73333v-91.73333h28.66667v5.73333c0, 3.1648 2.56853,5.73333 5.73333,5.73333h5.73333c3.1648,0 5.73333,-2.56853 5.73333,-5.73333v-17.2c0,-3.1648 -2.56853,-5.73333 -5.73333, -5.73333h-5.73333h-44.03021c-0.66226,-0.11084 -1.33298,-0.1633 -2.00442,-0.15677z"
3 | }
4 |
--------------------------------------------------------------------------------
/server/api/classroom-manager/services/classroom-manager.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | // find a classroom manager by user id
4 | module.exports.findById = async (id) => {
5 | const mentor = await strapi.services.mentor.findOne({ user: id });
6 | return {
7 | classroomManager: mentor,
8 | };
9 | };
10 |
11 | // find all classes for a classroom manager
12 | module.exports.findClassesById = async (id) => {
13 | const mentor = await strapi.services.mentor.findOne({ user: id });
14 | const classrooms = mentor.classrooms;
15 |
16 | const crList = await Promise.all(
17 | classrooms.map(async (classroom) => {
18 | const model = await strapi
19 | .query('classroom')
20 | .model.where('id', classroom.id)
21 | .fetch();
22 | return model ? model.toJSON() : undefined;
23 | })
24 | );
25 |
26 | return crList;
27 | };
28 |
--------------------------------------------------------------------------------
/client/src/components/NavBar/NavBarConfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "routes": {
3 | "TeacherLogin": "/teacherlogin",
4 | "Sandbox": "/sandbox",
5 | "About": "/about",
6 | "Home": "/",
7 | "Dashboard": "/dashboard",
8 | "SignOut": "/",
9 | "AccountInfo": "/account",
10 | "ContentCreatorDashboard": "/ccdashboard",
11 | "ResearcherDashboard": "/report",
12 | "BugReport": "/bugreport"
13 | },
14 | "users": {
15 | "DefaultUser": ["Home", "TeacherLogin", "Sandbox", "About"],
16 | "Mentor": ["Dashboard", "AccountInfo", "SignOut", "Sandbox", "BugReport"],
17 | "Student": ["SignOut"],
18 | "ContentCreator": [
19 | "ContentCreatorDashboard",
20 | "AccountInfo",
21 | "Sandbox",
22 | "SignOut",
23 | "BugReport"
24 | ],
25 | "Researcher": ["ResearcherDashboard", "BugReport", "SignOut"]
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/client/src/views/Mentor/Classroom/Classroom.less:
--------------------------------------------------------------------------------
1 | @import '../../../assets/style.less';
2 |
3 | #main-header {
4 | margin: 0 0 1vh 5vw;
5 |
6 | div {
7 | color: #colors[text-secondary];
8 | font-weight: bolder;
9 | }
10 | #classroom {
11 | font-size: 5vh;
12 | }
13 | #code {
14 | font-size: 4vh;
15 | }
16 | }
17 |
18 | .ant-tabs-nav-wrap,
19 | .ant-tabs-nav {
20 | margin: auto 5vw;
21 | }
22 |
23 | .ant-tabs-tab {
24 | min-width: 10vw;
25 | justify-content: center;
26 | color: #colors[tertiary] !important;
27 |
28 | &:hover {
29 | color: #colors[tertiary];
30 | opacity: 0.6;
31 | }
32 | }
33 |
34 | .ant-tabs-tab.ant-tabs-tab-active .ant-tabs-tab-btn {
35 | color: #colors[secondary] !important;
36 |
37 | &:hover {
38 | color: #colors[secondary];
39 | opacity: 0.6;
40 | }
41 | }
42 |
43 | .ant-tabs-ink-bar {
44 | background: #colors[secondary] !important;
45 | }
46 |
--------------------------------------------------------------------------------
/client/src/views/Researcher/GroupReport.less:
--------------------------------------------------------------------------------
1 | @import '../../assets/style.less';
2 |
3 | #group-report-header {
4 | color: #colors[text-secondary];
5 | font-size: 3em;
6 | font-weight: bolder;
7 | margin-left: 5%;
8 | margin-bottom: 3%;
9 | text-align: left;
10 | padding-top: 20px;
11 | }
12 |
13 | .cards {
14 | display: flex;
15 | justify-content: space-around;
16 | margin-bottom: 2rem;
17 |
18 | }
19 |
20 | #container-section {
21 | border : 2px solid #5BABDE;
22 | border-radius: 2px;
23 | }
24 | section {
25 | background-color: white;
26 | text-align: left;
27 | padding: 2rem;
28 | }
29 |
30 | #group-level-return {
31 | height: 55px;
32 | margin: 20px 50px;
33 | padding: 10px 20px;
34 | border-radius: 5px;
35 | font-size: 20px;
36 | font-weight: 800;
37 | font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', monospace;
38 | box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.377);
39 | }
--------------------------------------------------------------------------------
/server/config/functions/bootstrap.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const fixRoles = async () => {
4 |
5 | // get all the current roles
6 | const roles = await strapi.query('role', 'users-permissions').find({}, [])
7 |
8 | // if the authenticated role has the default name, update it
9 | const authenticated = roles.find(role => role.type === 'authenticated')
10 | if (authenticated.name !== 'Classroom Manager') await strapi.query('role', 'users-permissions').update({ id: authenticated.id }, { name: 'Classroom Manager' })
11 |
12 | // if the student role doesn't exist, alert the user
13 | const student = roles.find(role => role.type === 'student')
14 | if (!student) console.log('There is currently not a student role! Make sure to make one.')
15 | }
16 |
17 | module.exports = async () => {
18 |
19 | // Connect to the compile queue
20 | strapi.services.submission.initCompileQueue()
21 |
22 | // Check the student roles
23 | await fixRoles()
24 | }
25 |
--------------------------------------------------------------------------------
/server/api/session/models/session.settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "kind": "collectionType",
3 | "collectionName": "sessions",
4 | "info": {
5 | "name": "Session",
6 | "description": ""
7 | },
8 | "options": {
9 | "increments": true,
10 | "timestamps": true,
11 | "draftAndPublish": false
12 | },
13 | "attributes": {
14 | "classroom": {
15 | "via": "sessions",
16 | "model": "classroom"
17 | },
18 | "students": {
19 | "via": "sessions",
20 | "collection": "student"
21 | },
22 | "submissions": {
23 | "via": "session",
24 | "collection": "submission"
25 | },
26 | "saves": {
27 | "collection": "save",
28 | "via": "session"
29 | },
30 | "unit": {
31 | "model": "unit"
32 | },
33 | "grade": {
34 | "model": "grade"
35 | },
36 | "learning_standard": {
37 | "model": "learning-standard"
38 | },
39 | "arduino": {
40 | "type": "text"
41 | }
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/client/src/views/Researcher/GroupReport.jsx:
--------------------------------------------------------------------------------
1 | import NavBar from '../../components/NavBar/NavBar';
2 | import React from 'react';
3 | import { useNavigate } from 'react-router-dom';
4 | import './GroupReport.less';
5 | import { confirmRedirect } from '../../App';
6 |
7 | export default function GroupReport(props) {
8 | const navigate = useNavigate();
9 | confirmRedirect();
10 | return (
11 |
12 |
13 | {/*
Group Report
*/}
14 |
15 |
16 |
17 | {/* Do we need a menu button to go back to report landing page?*/}
18 |
26 |
27 |
28 | );
29 | }
30 |
--------------------------------------------------------------------------------
/server/api/learning-standard/models/learning-standard.settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "kind": "collectionType",
3 | "collectionName": "learning_standards",
4 | "info": {
5 | "name": "learning Standard",
6 | "description": ""
7 | },
8 | "options": {
9 | "increments": true,
10 | "timestamps": true,
11 | "draftAndPublish": false
12 | },
13 | "attributes": {
14 | "number": {
15 | "type": "decimal",
16 | "required": true
17 | },
18 | "name": {
19 | "type": "string",
20 | "required": true
21 | },
22 | "expectations": {
23 | "type": "text",
24 | "required": false
25 | },
26 | "days": {
27 | "via": "learning_standard",
28 | "collection": "day"
29 | },
30 | "unit": {
31 | "model": "unit",
32 | "via": "learning_standards"
33 | },
34 | "teks": {
35 | "type": "string",
36 | "unique": false,
37 | "required": false
38 | },
39 | "link": {
40 | "type": "string"
41 | }
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug report
3 | about: Create a report to help us improve
4 | title: ''
5 | labels: ''
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Describe the bug**
11 | A clear and concise description of what the bug is.
12 |
13 | **To Reproduce**
14 | Steps to reproduce the behavior:
15 | 1. Go to '...'
16 | 2. Click on '....'
17 | 3. Scroll down to '....'
18 | 4. See error
19 |
20 | **Expected behavior**
21 | A clear and concise description of what you expected to happen.
22 |
23 | **Screenshots**
24 | If applicable, add screenshots to help explain your problem.
25 |
26 | **Desktop (please complete the following information):**
27 | - OS: [e.g. iOS]
28 | - Browser [e.g. chrome, safari]
29 | - Version [e.g. 22]
30 |
31 | **Smartphone (please complete the following information):**
32 | - Device: [e.g. iPhone6]
33 | - OS: [e.g. iOS8.1]
34 | - Browser [e.g. stock browser, safari]
35 | - Version [e.g. 22]
36 |
37 | **Additional context**
38 | Add any other context about the problem here.
39 |
--------------------------------------------------------------------------------
/server/config/policies/hasClassroom.js:
--------------------------------------------------------------------------------
1 | //
2 | // Check if the current user belongs to this classroom
3 | //
4 | module.exports = async (ctx, next) => {
5 | if (ctx.state.user && ctx.state.user.role.name === 'Researcher') {
6 | return await next();
7 | }
8 | // get the target classroom from either the
9 | // request body or the query params
10 | let { classroom } = ctx.request.body;
11 | if (!classroom) classroom = ctx.params.id;
12 |
13 | // make sure the classroom id
14 | // is in the proper format
15 | classroom = parseInt(classroom);
16 |
17 | // get the classrooms that the user belongs to
18 | const { id } = ctx.state.user;
19 | const { classrooms } = (
20 | await strapi.services['classroom-manager'].findById(id)
21 | ).classroomManager;
22 |
23 | // check if the target classroom is one of the user's classrooms
24 | if (classrooms.length && classrooms.find((cr) => cr.id === classroom)) {
25 | return await next();
26 | }
27 |
28 | ctx.unauthorized(`You're not allowed to perform this action!`);
29 | };
30 |
--------------------------------------------------------------------------------
/server/config/policies/hasStudentsClassroom.js:
--------------------------------------------------------------------------------
1 | //
2 | // Check if the current user manages a classroom that this student belongs to
3 | //
4 | module.exports = async (ctx, next) => {
5 |
6 | // get the target student from either the
7 | // request body or the query params
8 | let { student } = ctx.request.body
9 | if (!student) student = ctx.params.id
10 |
11 | // make sure the student id
12 | // is in the proper format
13 | student = parseInt(student)
14 |
15 | // get the classrooms that the user belongs to
16 | const { id } = ctx.state.user
17 | const classrooms = await strapi.services['classroom-manager'].findClassesById(id)
18 |
19 |
20 | // check if the target student is in one of the user's classrooms
21 | if (classrooms.length && classrooms.some(classroom => {
22 | return classroom.students.some(s => {
23 | return s.id === student
24 | })
25 | })) {
26 | return await next()
27 | }
28 |
29 | ctx.unauthorized(`You're not allowed to perform this action!`)
30 | }
--------------------------------------------------------------------------------
/server/api/learning-standard/controllers/learning-standard.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | /**
4 | * Read the documentation (https://strapi.io/documentation/v3.x/concepts/controllers.html#core-controllers)
5 | * to customize this controller
6 | */
7 |
8 | module.exports = {
9 | async update(ctx) {
10 | const { id } = ctx.params;
11 |
12 | // ensure request was not sent as formdata
13 | if (ctx.is('multipart'))
14 | return ctx.badRequest('Multipart requests are not accepted!', {
15 | id: 'Learning-standard.update.format.invalid',
16 | error: 'ValidationError',
17 | });
18 |
19 | // validate the request
20 | const { name, expectations, teks } = ctx.request.body;
21 | if (!name)
22 | return ctx.badRequest('A name, teks and expectations must be provided!', {
23 | id: 'Learning-standard.update.body.invalid',
24 | error: 'ValidationError',
25 | });
26 |
27 | return await strapi.services['learning-standard'].update(
28 | { id },
29 | ctx.request.body
30 | );
31 | },
32 | };
33 |
--------------------------------------------------------------------------------
/server/api/session/controllers/session.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | /**
4 | * Read the documentation (https://strapi.io/documentation/v3.x/concepts/controllers.html#core-controllers)
5 | * to customize this controller
6 | */
7 | const { sanitizeEntity } = require('strapi-utils/lib');
8 |
9 |
10 | module.exports = {
11 | async arduinoUpdate(ctx) {
12 | const {id} = ctx.params;
13 | const {arduino} = ctx.request.body;
14 | const session = await strapi.services.session.findOne({ id });
15 | //console.log(session)
16 | //console.log(arduino)
17 | session.arduino = arduino;
18 | const updatedSession = await strapi.services.session.update({id}, session);
19 | return updatedSession
20 |
21 | },
22 | async findOne(ctx) {
23 | // Extract the ID from the request parameters
24 | const { id } = ctx.params;
25 | // Implement custom logic to fetch a single record by ID
26 | const session = await strapi.services.session.findOne({ id });
27 | // Customize the response as needed
28 | ctx.send(session);
29 | },
30 | };
31 |
--------------------------------------------------------------------------------
/.github/workflows/deploy-production.yml:
--------------------------------------------------------------------------------
1 | name: Deploy Production
2 | on:
3 | release:
4 | types: [created]
5 | branches: [master]
6 | jobs:
7 | run:
8 | runs-on: ubuntu-latest
9 | steps:
10 | - name: Checkout the repo
11 | uses: actions/checkout@v2
12 | - name: Get the version
13 | id: get_version
14 | run: echo ::set-output name=VERSION::${GITHUB_REF#refs/tags/}
15 | - name: Built, test, and deploy app
16 | uses: STEM-C/auto/build-test-deploy@v0.7.2
17 | with:
18 | image_tag: ${{ steps.get_version.outputs.VERSION }}
19 | app_name: casmm
20 | github_token: ${{ secrets.GITHUB_TOKEN }}
21 | env:
22 | HEROKU_API_KEY: ${{ secrets.HEROKU_TOKEN }}
23 | - name: Create Sentry release
24 | uses: getsentry/action-release@v1
25 | env:
26 | SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
27 | SENTRY_ORG: engaging-learning-lab-uf
28 | SENTRY_PROJECT: casmm
29 | with:
30 | environment: production
31 |
--------------------------------------------------------------------------------
/server/api/strapiusers/controllers/strapiusers.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const crypto = require('crypto');
4 | const _ = require('lodash');
5 | const grant = require('grant-koa');
6 | const { sanitizeEntity } = require('strapi-utils');
7 |
8 |
9 | module.exports = {
10 |
11 |
12 | async findSuperAdmins() {
13 | try {
14 | // Find the role representing super admins
15 | const superAdminRole = await strapi
16 | .query('role', 'admin')
17 | .findOne({ code: 'strapi-super-admin' });
18 |
19 | if (!superAdminRole) {
20 | console.error('Super admin role not found');
21 | return [];
22 | }
23 | //console.log('Role name: ', superAdminRole);
24 | // Find users assigned the super admin role
25 | const superAdmins = await strapi
26 | .query('user', 'admin',)
27 | .find();
28 |
29 | return superAdmins;
30 | } catch (error) {
31 | console.error('Error finding super admins:', error);
32 | return [];
33 | }
34 | }
35 | }
--------------------------------------------------------------------------------
/compile/src/controllers/job.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 | const arduino_dir = path.resolve(__dirname, '../arduino-1.8.5');
3 | const arduino = new (require('../models/arduino.js'))(arduino_dir);
4 | const { compileLog } = require('../utils/base');
5 |
6 | module.exports.processJob = async (job) => {
7 | // get the job data
8 | const { sketch, board, submissionId } = job.data;
9 |
10 | // create an identified
11 | const identifier = `job ${job.id}, submission ${submissionId}`;
12 |
13 | // update the job progress
14 | await job.progress(50);
15 | compileLog(`Compiling ${identifier}`);
16 |
17 | let result = {};
18 | try {
19 | result = await arduino.compile(sketch, board);
20 | } catch (err) {
21 | // set the result object to failed
22 | result.success = false;
23 | result.stderr = err.message;
24 |
25 | compileLog(`Failed to compile ${identifier} - ${err}`);
26 | }
27 |
28 | // update the job progress
29 | await job.progress(100);
30 | compileLog(`Completed ${identifier}`);
31 |
32 | // return the compiled result
33 | return result;
34 | };
35 |
--------------------------------------------------------------------------------
/docker-compose.yml:
--------------------------------------------------------------------------------
1 | version: '3'
2 |
3 | services:
4 | server:
5 | container_name: casmm-server-dev
6 | image: strapi/strapi
7 | restart: always
8 | environment:
9 | - DATABASE_HOST=db
10 | ports:
11 | - 1337:1337
12 | volumes:
13 | - ./server:/srv/app
14 | depends_on:
15 | - db
16 | - compile_queue
17 | compile:
18 | container_name: casmm-compile-dev
19 | build: ./compile
20 | restart: always
21 | depends_on:
22 | - compile_queue
23 | db:
24 | container_name: casmm-db-dev
25 | image: postgres
26 | restart: always
27 | ports:
28 | - 5432:5432
29 | volumes:
30 | - /var/lib/postgresql/data
31 | - ./scripts:/docker-entrypoint-initdb.d
32 | environment:
33 | POSTGRES_DB: strapi
34 | POSTGRES_USER: postgres
35 | POSTGRES_PASSWORD: postgres
36 | ENVIRONMENT: development
37 | DATABASE_URL: strapi
38 | SCRIPT_PATH: /docker-entrypoint-initdb.d
39 | compile_queue:
40 | container_name: casmm-compile_queue-dev
41 | image: redis
42 | restart: always
--------------------------------------------------------------------------------
/server/config/middleware.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | timeout: 100,
3 | load: {
4 | before: ["responseTime", "logger", "cors", "responses", "gzip"],
5 | order: ["proxy", "parser"],
6 | after: ["router"]
7 | },
8 | settings: {
9 | public: {
10 | path: './public',
11 | maxAge: 60000,
12 | },
13 | proxy: {
14 | enabled: true,
15 | clientPath: './public/client',
16 | },
17 | parser: {
18 | jsonLimit: '10mb'
19 | },
20 | gzip: {
21 | enabled: true,
22 | options: {
23 | br: false
24 | }
25 | },
26 | // logger: {
27 | // // dev + prod
28 | // level: debug + info,
29 | // requests: true + false
30 | // }
31 |
32 | // dev
33 | cors: {
34 | enabled: true,
35 | origin: [ 'https://www.casmm.org', 'http://localhost:1337', 'http://localhost:3000'],
36 | headers: '*',
37 | },
38 | },
39 | }
--------------------------------------------------------------------------------
/docker-compose.test.yml:
--------------------------------------------------------------------------------
1 | version: '3'
2 |
3 | services:
4 | server:
5 | container_name: casmm-server-test
6 | image: strapi/strapi
7 | restart: always
8 | environment:
9 | - DATABASE_HOST=db
10 | ports:
11 | - 1337:1337
12 | volumes:
13 | - ./server:/srv/app
14 | depends_on:
15 | - db
16 | - compile_queue
17 | compile:
18 | container_name: casmm-compile-test
19 | build: ./compile
20 | restart: always
21 | depends_on:
22 | - compile_queue
23 | db:
24 | container_name: casmm-db-test
25 | image: postgres
26 | restart: always
27 | ports:
28 | - 5432:5432
29 | volumes:
30 | - /var/lib/postgresql/data
31 | - ./scripts:/docker-entrypoint-initdb.d
32 | environment:
33 | POSTGRES_DB: strapi
34 | POSTGRES_USER: postgres
35 | POSTGRES_PASSWORD: postgres
36 | ENVIRONMENT: test
37 | DATABASE_URL: strapi
38 | SCRIPT_PATH: /docker-entrypoint-initdb.d
39 | compile_queue:
40 | container_name: casmm-compile_queue-test
41 | image: redis
42 | restart: always
--------------------------------------------------------------------------------
/client/src/views/Student/form.less:
--------------------------------------------------------------------------------
1 | @import "../../assets/style.less";
2 |
3 | .container {
4 | background-color: #colors[primary];
5 | height: 100%;
6 | min-height: 100vh;
7 | width: 100%;
8 | min-width: 100vw;
9 | text-align: center;
10 | }
11 |
12 | #about-content-container {
13 | margin: 8vh auto 5vh auto;
14 | padding: 5vh 5vw;
15 | width: 80vw;
16 | background: #colors[tertiary];
17 | border-radius: 20px;
18 | border: 2px solid #colors[secondary];
19 |
20 | #logos {
21 | width: 50vw;
22 | margin: auto;
23 |
24 | img {
25 | width: 20%;
26 | height: auto;
27 | margin: auto;
28 | }
29 | }
30 |
31 | #title {
32 | font-size: 3em;
33 | color: #dc3545;
34 | margin-bottom: 0;
35 | }
36 |
37 | #secondary-title {
38 | font-size: 2em;
39 | color: #colors[text-primary];
40 | }
41 |
42 | #divider {
43 | width: 95%;
44 | height: 0;
45 | margin: 5vh 0;
46 | border-bottom: 1px solid #colors[secondary];
47 | opacity: 20%;
48 | }
49 |
50 | p {
51 | font-size: 1.5em;
52 | color: #colors[text-primary];
53 | }
54 | }
--------------------------------------------------------------------------------
/client/src/Utils/userState.js:
--------------------------------------------------------------------------------
1 | import { createGlobalState } from 'react-hooks-global-state';
2 |
3 | // Get the role of user from the session storage
4 | export const getCurrUser = () => {
5 | const result = JSON.parse(sessionStorage.getItem('user'));
6 | if (!result) {
7 | return {
8 | role: 'DefaultUser',
9 | };
10 | }
11 | if (!result.role) {
12 | return {
13 | role: 'Student',
14 | };
15 | } else if (result.role.type === 'content_creator') {
16 | return {
17 | role: 'ContentCreator',
18 | name: result.role.name,
19 | };
20 | } else if (result.role.type === 'researcher') {
21 | return {
22 | role: 'Researcher',
23 | name: result.role.name,
24 | };
25 | } else if (result.role.type === 'authenticated') {
26 | return {
27 | role: 'Mentor',
28 | name: result.role.name,
29 | };
30 | }
31 | };
32 |
33 | const { setGlobalState, useGlobalState } = createGlobalState({
34 | currUser: getCurrUser(),
35 | });
36 |
37 | export const setUserState = (s) => {
38 | setGlobalState('currUser', s);
39 | };
40 |
41 | export { useGlobalState };
42 |
--------------------------------------------------------------------------------
/client/src/views/TeacherLogin/Sorry.less:
--------------------------------------------------------------------------------
1 | @import "../../assets/style.less";
2 |
3 | .container {
4 | background-color: #colors[primary];
5 | height: 100%;
6 | min-height: 100vh;
7 | width: 100%;
8 | min-width: 100vw;
9 | text-align: center;
10 | }
11 |
12 | #about-content-container {
13 | margin: 8vh auto 5vh auto;
14 | padding: 5vh 5vw;
15 | width: 80vw;
16 | background: #colors[tertiary];
17 | border-radius: 20px;
18 | border: 2px solid #colors[secondary];
19 |
20 | #logos {
21 | width: 50vw;
22 | margin: auto;
23 |
24 | img {
25 | width: 20%;
26 | height: auto;
27 | margin: auto;
28 | }
29 | }
30 |
31 | #title {
32 | font-size: 3em;
33 | color: #dc3545;
34 | margin-bottom: 0;
35 | }
36 |
37 | #secondary-title {
38 | font-size: 2em;
39 | color: #colors[text-primary];
40 | }
41 |
42 | #divider {
43 | width: 95%;
44 | height: 0;
45 | margin: 5vh 0;
46 | border-bottom: 1px solid #colors[secondary];
47 | opacity: 20%;
48 | }
49 |
50 | p {
51 | font-size: 1.5em;
52 | color: #colors[text-primary];
53 | }
54 | }
--------------------------------------------------------------------------------
/client/src/views/About/About.less:
--------------------------------------------------------------------------------
1 | @import "../../assets/style.less";
2 |
3 | .container {
4 | background-color: #colors[primary];
5 | height: 100%;
6 | min-height: 100vh;
7 | width: 100%;
8 | min-width: 100vw;
9 | text-align: center;
10 | }
11 |
12 | #about-content-container {
13 | margin: 8vh auto 5vh auto;
14 | padding: 5vh 5vw;
15 | width: 80vw;
16 | background: #colors[tertiary];
17 | border-radius: 20px;
18 | border: 2px solid #colors[secondary];
19 |
20 | #logos {
21 | width: 50vw;
22 | margin: auto;
23 |
24 | img {
25 | width: 20%;
26 | height: auto;
27 | margin: auto;
28 | }
29 | }
30 |
31 | #title {
32 | font-size: 3em;
33 | color: #colors[text-primary];
34 | margin-bottom: 0;
35 | }
36 |
37 | #secondary-title {
38 | font-size: 2em;
39 | color: #colors[text-primary];
40 | }
41 |
42 | #divider {
43 | width: 95%;
44 | height: 0;
45 | margin: 5vh 0;
46 | border-bottom: 1px solid #colors[secondary];
47 | opacity: 20%;
48 | }
49 |
50 | p {
51 | font-size: 1.5em;
52 | color: #colors[text-primary];
53 | }
54 | }
--------------------------------------------------------------------------------
/server/api/unit/config/routes.json:
--------------------------------------------------------------------------------
1 | {
2 | "routes": [
3 | {
4 | "method": "GET",
5 | "path": "/units",
6 | "handler": "unit.find",
7 | "config": {
8 | "policies": []
9 | }
10 | },
11 | {
12 | "method": "GET",
13 | "path": "/units/count",
14 | "handler": "unit.count",
15 | "config": {
16 | "policies": []
17 | }
18 | },
19 | {
20 | "method": "GET",
21 | "path": "/units/:id",
22 | "handler": "unit.findOne",
23 | "config": {
24 | "policies": []
25 | }
26 | },
27 | {
28 | "method": "POST",
29 | "path": "/units",
30 | "handler": "unit.create",
31 | "config": {
32 | "policies": []
33 | }
34 | },
35 | {
36 | "method": "PUT",
37 | "path": "/units/:id",
38 | "handler": "unit.update",
39 | "config": {
40 | "policies": []
41 | }
42 | },
43 | {
44 | "method": "DELETE",
45 | "path": "/units/:id",
46 | "handler": "unit.delete",
47 | "config": {
48 | "policies": []
49 | }
50 | }
51 | ]
52 | }
53 |
--------------------------------------------------------------------------------
/server/api/block/config/routes.json:
--------------------------------------------------------------------------------
1 | {
2 | "routes": [
3 | {
4 | "method": "GET",
5 | "path": "/blocks",
6 | "handler": "block.find",
7 | "config": {
8 | "policies": []
9 | }
10 | },
11 | {
12 | "method": "GET",
13 | "path": "/blocks/count",
14 | "handler": "block.count",
15 | "config": {
16 | "policies": []
17 | }
18 | },
19 | {
20 | "method": "GET",
21 | "path": "/blocks/:id",
22 | "handler": "block.findOne",
23 | "config": {
24 | "policies": []
25 | }
26 | },
27 | {
28 | "method": "POST",
29 | "path": "/blocks",
30 | "handler": "block.create",
31 | "config": {
32 | "policies": []
33 | }
34 | },
35 | {
36 | "method": "PUT",
37 | "path": "/blocks/:id",
38 | "handler": "block.update",
39 | "config": {
40 | "policies": []
41 | }
42 | },
43 | {
44 | "method": "DELETE",
45 | "path": "/blocks/:id",
46 | "handler": "block.delete",
47 | "config": {
48 | "policies": []
49 | }
50 | }
51 | ]
52 | }
53 |
--------------------------------------------------------------------------------
/server/api/grade/config/routes.json:
--------------------------------------------------------------------------------
1 | {
2 | "routes": [
3 | {
4 | "method": "GET",
5 | "path": "/grades",
6 | "handler": "grade.find",
7 | "config": {
8 | "policies": []
9 | }
10 | },
11 | {
12 | "method": "GET",
13 | "path": "/grades/count",
14 | "handler": "grade.count",
15 | "config": {
16 | "policies": []
17 | }
18 | },
19 | {
20 | "method": "GET",
21 | "path": "/grades/:id",
22 | "handler": "grade.findOne",
23 | "config": {
24 | "policies": []
25 | }
26 | },
27 | {
28 | "method": "POST",
29 | "path": "/grades",
30 | "handler": "grade.create",
31 | "config": {
32 | "policies": []
33 | }
34 | },
35 | {
36 | "method": "PUT",
37 | "path": "/grades/:id",
38 | "handler": "grade.update",
39 | "config": {
40 | "policies": []
41 | }
42 | },
43 | {
44 | "method": "DELETE",
45 | "path": "/grades/:id",
46 | "handler": "grade.delete",
47 | "config": {
48 | "policies": []
49 | }
50 | }
51 | ]
52 | }
53 |
--------------------------------------------------------------------------------
/server/api/school/config/routes.json:
--------------------------------------------------------------------------------
1 | {
2 | "routes": [
3 | {
4 | "method": "GET",
5 | "path": "/schools",
6 | "handler": "school.find",
7 | "config": {
8 | "policies": []
9 | }
10 | },
11 | {
12 | "method": "GET",
13 | "path": "/schools/count",
14 | "handler": "school.count",
15 | "config": {
16 | "policies": []
17 | }
18 | },
19 | {
20 | "method": "GET",
21 | "path": "/schools/:id",
22 | "handler": "school.findOne",
23 | "config": {
24 | "policies": []
25 | }
26 | },
27 | {
28 | "method": "POST",
29 | "path": "/schools",
30 | "handler": "school.create",
31 | "config": {
32 | "policies": []
33 | }
34 | },
35 | {
36 | "method": "PUT",
37 | "path": "/schools/:id",
38 | "handler": "school.update",
39 | "config": {
40 | "policies": []
41 | }
42 | },
43 | {
44 | "method": "DELETE",
45 | "path": "/schools/:id",
46 | "handler": "school.delete",
47 | "config": {
48 | "policies": []
49 | }
50 | }
51 | ]
52 | }
53 |
--------------------------------------------------------------------------------
/server/api/mentor/config/routes.json:
--------------------------------------------------------------------------------
1 | {
2 | "routes": [
3 | {
4 | "method": "GET",
5 | "path": "/mentors",
6 | "handler": "mentor.find",
7 | "config": {
8 | "policies": []
9 | }
10 | },
11 | {
12 | "method": "GET",
13 | "path": "/mentors/count",
14 | "handler": "mentor.count",
15 | "config": {
16 | "policies": []
17 | }
18 | },
19 | {
20 | "method": "GET",
21 | "path": "/mentors/:id",
22 | "handler": "mentor.findOne",
23 | "config": {
24 | "policies": []
25 | }
26 | },
27 | {
28 | "method": "POST",
29 | "path": "/mentors",
30 | "handler": "mentor.create",
31 | "config": {
32 | "policies": ["global::isClassroomManager"]
33 | }
34 | },
35 | {
36 | "method": "PUT",
37 | "path": "/mentors/:id",
38 | "handler": "mentor.update",
39 | "config": {
40 | "policies": []
41 | }
42 | },
43 | {
44 | "method": "DELETE",
45 | "path": "/mentors/:id",
46 | "handler": "mentor.delete",
47 | "config": {
48 | "policies": []
49 | }
50 | }
51 | ]
52 | }
53 |
--------------------------------------------------------------------------------
/client/src/views/Mentor/Classroom/Home/DisplayCodeModal.jsx:
--------------------------------------------------------------------------------
1 | import {Modal, Button} from 'antd';
2 | import React, {useState} from "react";
3 | import './Home.less'
4 |
5 | export default function DisplayCodeModal(props) {
6 | const [visible, setVisible] = useState(false);
7 | const {code} = props;
8 |
9 | const showModal = () => {
10 | setVisible(true)
11 | };
12 |
13 | const handleCancel = () => {
14 | setVisible(false)
15 | };
16 |
17 | const handleOk = () => {
18 | setVisible(false)
19 | };
20 |
21 | return (
22 |
23 |
24 |
31 | OK
32 | ,
33 | ]}
34 | >
35 | {code}
36 |
37 |
38 | );
39 | }
40 |
--------------------------------------------------------------------------------
/server/api/student/models/student.settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "kind": "collectionType",
3 | "collectionName": "students",
4 | "info": {
5 | "name": "Student",
6 | "description": ""
7 | },
8 | "options": {
9 | "increments": true,
10 | "timestamps": true,
11 | "draftAndPublish": false
12 | },
13 | "attributes": {
14 | "name": {
15 | "type": "string",
16 | "regex": "^([A-Za-z]+)\\s*([A-Za-z]*)\\s+([A-Za-z])\\.$"
17 | },
18 | "character": {
19 | "type": "string"
20 | },
21 | "classroom": {
22 | "via": "students",
23 | "model": "classroom"
24 | },
25 | "sessions": {
26 | "via": "students",
27 | "collection": "session",
28 | "dominant": true
29 | },
30 | "enrolled": {
31 | "type": "boolean",
32 | "default": true,
33 | "required": true
34 | },
35 | "last_logged_in": {
36 | "type": "datetime"
37 | },
38 | "recordings": {
39 | "collection": "file",
40 | "via": "related",
41 | "allowedTypes": [
42 | "images",
43 | "files",
44 | "videos"
45 | ],
46 | "plugin": "upload",
47 | "required": false,
48 | "pluginOptions": {}
49 | }
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/server/api/submission/config/routes.json:
--------------------------------------------------------------------------------
1 | {
2 | "routes": [
3 | {
4 | "method": "GET",
5 | "path": "/submissions",
6 | "handler": "submission.find",
7 | "config": {
8 | "policies": []
9 | }
10 | },
11 | {
12 | "method": "GET",
13 | "path": "/submissions/count",
14 | "handler": "submission.count",
15 | "config": {
16 | "policies": []
17 | }
18 | },
19 | {
20 | "method": "GET",
21 | "path": "/submissions/:id",
22 | "handler": "submission.findOne",
23 | "config": {
24 | "policies": ["global::isStudent"]
25 | }
26 | },
27 | {
28 | "method": "POST",
29 | "path": "/submissions",
30 | "handler": "submission.create",
31 | "config": {
32 | "policies": ["global::isStudent"]
33 | }
34 | },
35 | {
36 | "method": "PUT",
37 | "path": "/submissions/:id",
38 | "handler": "submission.update",
39 | "config": {
40 | "policies": []
41 | }
42 | },
43 | {
44 | "method": "DELETE",
45 | "path": "/submissions/:id",
46 | "handler": "submission.delete",
47 | "config": {
48 | "policies": []
49 | }
50 | }
51 | ]
52 | }
53 |
--------------------------------------------------------------------------------
/server/api/selection/config/routes.json:
--------------------------------------------------------------------------------
1 | {
2 | "routes": [
3 | {
4 | "method": "GET",
5 | "path": "/selections",
6 | "handler": "selection.find",
7 | "config": {
8 | "policies": []
9 | }
10 | },
11 | {
12 | "method": "GET",
13 | "path": "/selections/count",
14 | "handler": "selection.count",
15 | "config": {
16 | "policies": []
17 | }
18 | },
19 | {
20 | "method": "GET",
21 | "path": "/selections/:id",
22 | "handler": "selection.findOne",
23 | "config": {
24 | "policies": []
25 | }
26 | },
27 | {
28 | "method": "POST",
29 | "path": "/selections",
30 | "handler": "selection.create",
31 | "config": {
32 | "policies": ["global::isClassroomManager", "global::hasClassroom"]
33 | }
34 | },
35 | {
36 | "method": "PUT",
37 | "path": "/selections/:id",
38 | "handler": "selection.update",
39 | "config": {
40 | "policies": []
41 | }
42 | },
43 | {
44 | "method": "DELETE",
45 | "path": "/selections/:id",
46 | "handler": "selection.delete",
47 | "config": {
48 | "policies": []
49 | }
50 | }
51 | ]
52 | }
53 |
--------------------------------------------------------------------------------
/server/api/classroom/models/classroom.settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "kind": "collectionType",
3 | "collectionName": "classrooms",
4 | "info": {
5 | "name": "Classroom",
6 | "description": ""
7 | },
8 | "options": {
9 | "increments": true,
10 | "timestamps": true,
11 | "draftAndPublish": false
12 | },
13 | "attributes": {
14 | "name": {
15 | "type": "string"
16 | },
17 | "school": {
18 | "model": "school",
19 | "via": "classrooms"
20 | },
21 | "sessions": {
22 | "via": "classroom",
23 | "collection": "session"
24 | },
25 | "mentors": {
26 | "collection": "mentor",
27 | "via": "classrooms"
28 | },
29 | "students": {
30 | "via": "classroom",
31 | "collection": "student"
32 | },
33 | "code": {
34 | "type": "string",
35 | "required": true
36 | },
37 | "grade": {
38 | "via": "classrooms",
39 | "model": "grade"
40 | },
41 | "selections": {
42 | "via": "classroom",
43 | "collection": "selection"
44 | },
45 | "cc_workspaces": {
46 | "private": true,
47 | "via": "classroom",
48 | "collection": "cc-workspace"
49 | },
50 | "form": {
51 | "type": "string"
52 | }
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/server/api/submission/models/submission.settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "kind": "collectionType",
3 | "collectionName": "submissions",
4 | "info": {
5 | "name": "Submission"
6 | },
7 | "options": {
8 | "increments": true,
9 | "timestamps": true
10 | },
11 | "attributes": {
12 | "status": {
13 | "type": "string",
14 | "default": "WAITING",
15 | "required": true
16 | },
17 | "workspace": {
18 | "type": "text",
19 | "required": true
20 | },
21 | "success": {
22 | "type": "boolean",
23 | "required": false
24 | },
25 | "hex": {
26 | "type": "text"
27 | },
28 | "stdout": {
29 | "type": "text"
30 | },
31 | "stderr": {
32 | "type": "text"
33 | },
34 | "board": {
35 | "type": "string",
36 | "required": true
37 | },
38 | "sketch": {
39 | "type": "text",
40 | "required": true
41 | },
42 | "sandbox": {
43 | "type": "boolean",
44 | "default": false,
45 | "required": true
46 | },
47 | "session": {
48 | "via": "submissions",
49 | "model": "session"
50 | },
51 | "day": {
52 | "model": "day"
53 | },
54 | "job_id": {
55 | "type": "biginteger"
56 | }
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/client/src/assets/style.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 0;
3 | }
4 |
5 | h3, p {
6 | margin: 0;
7 | }
8 |
9 | .flex {
10 | display: flex;
11 | }
12 |
13 | .flex-column {
14 | flex-direction: column;
15 | }
16 |
17 | .space-between {
18 | justify-content: space-between;
19 | }
20 |
21 | #container {
22 | height: 100vh;
23 | background-color: rgb(241, 241, 241);
24 | }
25 |
26 | .vertical-container {
27 | margin: 1.5em;
28 | }
29 |
30 | #nav-container {
31 | margin: 0;
32 | background-color: rgb(32, 145, 173);
33 | color: white;
34 | align-items: center;
35 | }
36 |
37 | #title {
38 | flex: 10;
39 | margin: 0.5em;
40 | }
41 |
42 | #action-btn-container {
43 | flex: 1;
44 | margin: 1em;
45 | }
46 |
47 | #top-container {
48 | flex: 1;
49 | margin-bottom: 0.75em
50 | }
51 |
52 | .card {
53 | background-color: white;
54 | padding: 1em;
55 | }
56 |
57 | #description-container {
58 | height: 100%;
59 | }
60 |
61 | #bottom-container {
62 | flex: 6;
63 | margin-top: 0.75em
64 | }
65 |
66 | #blockly-canvas {
67 | flex: 2;
68 | }
69 |
70 | #models-container {
71 | flex: 1;
72 | margin-left: 1.5em;
73 | }
74 |
75 | #mode-img {
76 | width: auto;
77 | height: auto;
78 | }
79 |
80 |
--------------------------------------------------------------------------------
/server/admin/src/themes/colors.js:
--------------------------------------------------------------------------------
1 | const colors = {
2 | black: '#333740',
3 | white: '#ffffff',
4 | red: '#ff203c',
5 | orange: '#ff5d00',
6 | lightOrange: '#f64d0a',
7 | yellow: '#ffd500',
8 | green: '#6dbb1a',
9 | blue: '#0097f7',
10 | teal: '#5bc0de',
11 | pink: '#ff5b77',
12 | purple: '#613d7c',
13 | gray: '#464a4c',
14 | border: '#E3E9F3',
15 | 'gray-dark': '#292b2c',
16 | grayLight: '#636c72',
17 | 'gray-lighter': '#eceeef',
18 | 'gray-lightest': '#f7f7f9',
19 | brightGrey: '#f0f3f8',
20 | darkGrey: '#e3e9f3',
21 | lightGrey: '#fafafa',
22 | lightestGrey: '#fbfbfb',
23 | mediumGrey: '#F2F3F4',
24 | grey: '#9ea7b8',
25 | greyDark: '#292b2c',
26 | greyAlpha: 'rgba(227, 233, 243, 0.5)',
27 | lightBlue: '#E6F0FB',
28 | mediumBlue: '#007EFF',
29 | darkBlue: '#AED4FB',
30 | content: {
31 | background: '#fafafb',
32 | 'background-alpha': 'rgba(14, 22, 34, 0.02)',
33 | },
34 | leftMenu: {
35 | 'link-hover': '#4e76a6',
36 | 'link-color': '#85bcde',
37 | 'title-color': '#5BABDE',
38 | },
39 | strapi: {
40 | 'gray-light': '#eff3f6',
41 | gray: '#535f76',
42 | 'blue-darker': '#3D5C82',
43 | 'blue-dark': '#4e76a6',
44 | blue: '#5BABDE',
45 | },
46 | };
47 |
48 | export default colors;
49 |
--------------------------------------------------------------------------------
/client/src/components/MentorSubHeader/MentorSubHeader.less:
--------------------------------------------------------------------------------
1 | @import '../../assets/style.less';
2 |
3 | #page-header {
4 | width: 80vw;
5 | margin: 0 auto 0 auto;
6 | padding-bottom: 2vh;
7 |
8 | h1 {
9 | margin-bottom: 0;
10 | float: left;
11 | position: relative;
12 | display: flex;
13 | align-items: center;
14 | justify-content: center;
15 | color: #colors[text-secondary];
16 | height: auto;
17 | padding: 10px 5px;
18 | width: 34%;
19 | border-radius: 80px;
20 | background: #colors[secondary];
21 | font-size: 1.2em;
22 | top: 5vh;
23 | left: -2vw;
24 | z-index: 1;
25 | }
26 |
27 | #header-nav {
28 | position: relative;
29 | bottom: -2vh;
30 | float: right;
31 | margin-right: 0;
32 | white-space: nowrap;
33 |
34 | #link {
35 | //float: right;
36 | background: none !important;
37 | border: none;
38 | padding: 0 !important;
39 | display: inline-block;
40 | margin-left: 10px;
41 |
42 | &:focus {
43 | outline: none;
44 | }
45 |
46 | i {
47 | font-size: 2em;
48 | color: #colors[text-secondary];
49 |
50 | &:hover {
51 | color: #colors[secondary];
52 | }
53 | }
54 | }
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/server/api/blocks-category/config/routes.json:
--------------------------------------------------------------------------------
1 | {
2 | "routes": [
3 | {
4 | "method": "GET",
5 | "path": "/blocks-categories",
6 | "handler": "blocks-category.find",
7 | "config": {
8 | "policies": []
9 | }
10 | },
11 | {
12 | "method": "GET",
13 | "path": "/blocks-categories/count",
14 | "handler": "blocks-category.count",
15 | "config": {
16 | "policies": []
17 | }
18 | },
19 | {
20 | "method": "GET",
21 | "path": "/blocks-categories/:id",
22 | "handler": "blocks-category.findOne",
23 | "config": {
24 | "policies": []
25 | }
26 | },
27 | {
28 | "method": "POST",
29 | "path": "/blocks-categories",
30 | "handler": "blocks-category.create",
31 | "config": {
32 | "policies": []
33 | }
34 | },
35 | {
36 | "method": "PUT",
37 | "path": "/blocks-categories/:id",
38 | "handler": "blocks-category.update",
39 | "config": {
40 | "policies": []
41 | }
42 | },
43 | {
44 | "method": "DELETE",
45 | "path": "/blocks-categories/:id",
46 | "handler": "blocks-category.delete",
47 | "config": {
48 | "policies": []
49 | }
50 | }
51 | ]
52 | }
53 |
--------------------------------------------------------------------------------
/server/api/learning-standard/config/routes.json:
--------------------------------------------------------------------------------
1 | {
2 | "routes": [
3 | {
4 | "method": "GET",
5 | "path": "/learning-standards",
6 | "handler": "learning-standard.find",
7 | "config": {
8 | "policies": []
9 | }
10 | },
11 | {
12 | "method": "GET",
13 | "path": "/learning-standards/count",
14 | "handler": "learning-standard.count",
15 | "config": {
16 | "policies": []
17 | }
18 | },
19 | {
20 | "method": "GET",
21 | "path": "/learning-standards/:id",
22 | "handler": "learning-standard.findOne",
23 | "config": {
24 | "policies": []
25 | }
26 | },
27 | {
28 | "method": "POST",
29 | "path": "/learning-standards",
30 | "handler": "learning-standard.create",
31 | "config": {
32 | "policies": []
33 | }
34 | },
35 | {
36 | "method": "PUT",
37 | "path": "/learning-standards/:id",
38 | "handler": "learning-standard.update",
39 | "config": {
40 | "policies": []
41 | }
42 | },
43 | {
44 | "method": "DELETE",
45 | "path": "/learning-standards/:id",
46 | "handler": "learning-standard.delete",
47 | "config": {
48 | "policies": []
49 | }
50 | }
51 | ]
52 | }
53 |
--------------------------------------------------------------------------------
/client/src/views/Student/form.jsx:
--------------------------------------------------------------------------------
1 | import {React, useEffect, useState} from "react"
2 | import NavBar from "../../components/NavBar/NavBar"
3 | import "./form.less"
4 | import { getSupers } from "../../Utils/AuthRequests"
5 | import { sendEmailConfirmationEmail, getStudentClassroom } from "../../Utils/requests"
6 |
7 |
8 | export default function Form(props) {
9 | const superEmails = []
10 | const[formcode, setFormCode] = useState('')
11 |
12 |
13 | useEffect ( () => {
14 |
15 | getStudentClassroom()
16 | .then((response) => {
17 | //console.log(response.data.classroom.form)
18 | setFormCode(`${response.data.classroom.form}?embedded=true`)
19 |
20 | //console.log(`${response.data.classroom.form}`)
21 | })
22 |
23 | }, [])
24 |
25 |
26 |
27 | return (
28 |
29 |
30 |
31 |
43 |
44 |
45 |
46 | )
47 | }
48 |
--------------------------------------------------------------------------------
/server/admin/src/components/LeftMenu/LeftMenuHeader/Wrapper.js:
--------------------------------------------------------------------------------
1 | import styled from 'styled-components';
2 | import PropTypes from 'prop-types';
3 |
4 | import Logo from '../../../assets/images/logo-strapi.png';
5 |
6 | const Wrapper = styled.div`
7 | background-color: #007eff;
8 | padding-left: 2rem;
9 | height: ${(props) => props.theme.main.sizes.leftMenu.height};
10 |
11 | .leftMenuHeaderLink {
12 | &:hover {
13 | text-decoration: none;
14 | }
15 | }
16 |
17 | .projectName {
18 | display: block;
19 | width: 100%;
20 | height: ${(props) => props.theme.main.sizes.leftMenu.height};
21 | font-size: 2rem;
22 | letter-spacing: 0.2rem;
23 | color: $white;
24 |
25 | background-image: url(${Logo});
26 | background-repeat: no-repeat;
27 | background-position: left center;
28 | background-size: auto 6.5rem;
29 | }
30 | `;
31 | //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
32 | //Change background-size to: auto 6.5rem
33 |
34 | Wrapper.defaultProps = {
35 | theme: {
36 | main: {
37 | colors: {
38 | leftMenu: {},
39 | },
40 | sizes: {
41 | header: {},
42 | leftMenu: {},
43 | },
44 | },
45 | },
46 | };
47 |
48 | Wrapper.propTypes = {
49 | theme: PropTypes.object,
50 | };
51 |
52 | export default Wrapper;
53 |
--------------------------------------------------------------------------------
/server/api/learning-components/config/routes.json:
--------------------------------------------------------------------------------
1 | {
2 | "routes": [
3 | {
4 | "method": "GET",
5 | "path": "/learning-components",
6 | "handler": "learning-components.find",
7 | "config": {
8 | "policies": []
9 | }
10 | },
11 | {
12 | "method": "GET",
13 | "path": "/learning-components/count",
14 | "handler": "learning-components.count",
15 | "config": {
16 | "policies": []
17 | }
18 | },
19 | {
20 | "method": "GET",
21 | "path": "/learning-components/:id",
22 | "handler": "learning-components.findOne",
23 | "config": {
24 | "policies": []
25 | }
26 | },
27 | {
28 | "method": "POST",
29 | "path": "/learning-components",
30 | "handler": "learning-components.create",
31 | "config": {
32 | "policies": []
33 | }
34 | },
35 | {
36 | "method": "PUT",
37 | "path": "/learning-components/:id",
38 | "handler": "learning-components.update",
39 | "config": {
40 | "policies": []
41 | }
42 | },
43 | {
44 | "method": "DELETE",
45 | "path": "/learning-components/:id",
46 | "handler": "learning-components.delete",
47 | "config": {
48 | "policies": []
49 | }
50 | }
51 | ]
52 | }
53 |
--------------------------------------------------------------------------------
/client/src/components/NavBar/NavBar.less:
--------------------------------------------------------------------------------
1 | @import "../../assets/style.less";
2 |
3 | #navBar {
4 | display: flex;
5 | justify-content: space-between;
6 | align-items: center;
7 | flex-wrap: nowrap;
8 | width: 100%;
9 | position: -webkit-sticky;
10 | position: fixed;
11 | top: 0;
12 | z-index: 2;
13 | height: 8vh;
14 | background-color: #colors[primary];
15 | padding-right: 2vw;
16 |
17 | -moz-box-shadow: 0 1px 5px -1px darken(#colors[primary], 90%);
18 | -webkit-box-shadow: 0 1px 5px -1px darken(#colors[primary], 90%);
19 | box-shadow: 0 1px 5px -1px darken(#colors[primary], 90%);
20 |
21 | #link {
22 | #casmm-logo {
23 | position: relative;
24 | bottom: 0;
25 | height: 6vh;
26 | width: auto;
27 | }
28 | }
29 | }
30 |
31 | #menu-link {
32 | background: none !important;
33 | border: none;
34 | padding: 0 !important;
35 | display: inline-block;
36 |
37 | &:focus {
38 | outline: none;
39 | }
40 |
41 | &:hover {
42 | cursor: pointer;
43 | }
44 | }
45 |
46 | .ant-dropdown-link {
47 | color: #colors[tertiary];
48 | background: none !important;
49 | border: none;
50 | padding: 0 !important;
51 |
52 | &:focus {
53 | outline: none;
54 | }
55 |
56 | &:hover {
57 | color: #colors[secondary];
58 | cursor: pointer;
59 | }
60 | }
--------------------------------------------------------------------------------
/client/src/components/DayPanels/BlocklyCanvasPanel/BlocklyCanvasPanel.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PublicCanvas from './canvas/PublicCanvas';
3 | import StudentCanvas from './canvas/StudentCanvas';
4 | import MentorCanvas from './canvas/MentorCanvas';
5 | import ContentCreatorCanvas from './canvas/ContentCreatorCanvas';
6 | import { useGlobalState } from '../../../Utils/userState';
7 |
8 | const BlocklyCanvasPanel = ({ day, isSandbox, setDay }) => {
9 | const [value] = useGlobalState('currUser');
10 |
11 | const userRole = value.role;
12 |
13 | switch (userRole) {
14 | case 'DefaultUser':
15 | return ;
16 | case 'Student':
17 | return ;
18 | case 'Mentor':
19 | return ;
25 | case 'ContentCreator':
26 | return (
27 |
33 | );
34 | default:
35 | return ;
36 | }
37 | };
38 |
39 | export default BlocklyCanvasPanel;
40 |
--------------------------------------------------------------------------------
/client/src/views/Mentor/Dashboard/DashboardDisplayCodeModal.jsx:
--------------------------------------------------------------------------------
1 | import {Modal, Button} from 'antd';
2 | import React, {useState} from "react";
3 | import './Dashboard.less'
4 |
5 | export default function DashboardDisplayCodeModal(props) {
6 | const [visible, setVisible] = useState(false);
7 | const {code} = props;
8 |
9 | const showModal = () => {
10 | setVisible(true)
11 | };
12 |
13 | const handleCancel = () => {
14 | setVisible(false)
15 | };
16 |
17 | const handleOk = () => {
18 | setVisible(false)
19 | };
20 |
21 | return (
22 |
23 |
27 |
34 | OK
35 | ,
36 | ]}
37 | >
38 | {code}
39 |
40 |
41 | );
42 | }
--------------------------------------------------------------------------------
/server/api/cc-workspace/config/policies/isContentCreatorOrHasClassroom.js:
--------------------------------------------------------------------------------
1 | //
2 | // Check if the current user belongs to this classroom
3 | //
4 | module.exports = async (ctx, next) => {
5 | if (ctx.state.user && ctx.state.user.role.name === 'Content Creator') {
6 | // Go to next policy or controller
7 | return await next();
8 | }
9 | let classroomId;
10 |
11 | // get the target classroom from either the
12 | // request body or the query params
13 | let { id } = ctx.params;
14 | if (id) {
15 | id = parseInt(id);
16 | const workspace = await strapi.services['cc-workspace'].findOne(
17 | { id: id },
18 | ['classroom.id']
19 | );
20 | if (workspace && workspace.classroom) {
21 | classroomId = workspace.classroom.id;
22 | }
23 | } else {
24 | classroomId = ctx.request.body.classroomId;
25 | classroomId = parseInt(classroomId);
26 | }
27 |
28 | const { id: userId } = ctx.state.user;
29 | const { classrooms } = (
30 | await strapi.services['classroom-manager'].findById(userId)
31 | ).classroomManager;
32 |
33 | //check if the target classroom is one of the user's classrooms
34 | if (classrooms.length && classrooms.find((cr) => cr.id === classroomId)) {
35 | return await next();
36 | }
37 |
38 | ctx.unauthorized(`You're not allowed to perform this action!`);
39 | };
40 |
--------------------------------------------------------------------------------
/server/api/save/config/routes.json:
--------------------------------------------------------------------------------
1 | {
2 | "routes": [
3 | {
4 | "method": "GET",
5 | "path": "/saves",
6 | "handler": "save.find",
7 | "config": {
8 | "policies": []
9 | }
10 | },
11 | {
12 | "method": "GET",
13 | "path": "/saves/count",
14 | "handler": "save.count",
15 | "config": {
16 | "policies": []
17 | }
18 | },
19 | {
20 | "method": "GET",
21 | "path": "/saves/day/:day",
22 | "handler": "save.findByDay",
23 | "config": {
24 | "policies": ["global::isStudent"]
25 | }
26 | },
27 | {
28 | "method": "GET",
29 | "path": "/saves/:id",
30 | "handler": "save.findOne",
31 | "config": {
32 | "policies": []
33 | }
34 | },
35 | {
36 | "method": "POST",
37 | "path": "/saves",
38 | "handler": "save.create",
39 | "config": {
40 | "policies": []
41 | }
42 | },
43 | {
44 | "method": "PUT",
45 | "path": "/saves/:id",
46 | "handler": "save.update",
47 | "config": {
48 | "policies": []
49 | }
50 | },
51 | {
52 | "method": "DELETE",
53 | "path": "/saves/:id",
54 | "handler": "save.delete",
55 | "config": {
56 | "policies": []
57 | }
58 | }
59 | ]
60 | }
61 |
--------------------------------------------------------------------------------
/server/config/plugins.js:
--------------------------------------------------------------------------------
1 | require('dotenv').config({ path: '../.env' });
2 | const Sentry = require('@sentry/node');
3 | const Tracing = require('@sentry/tracing');
4 |
5 | module.exports = () => ({
6 | email: {
7 | provider: 'nodemailer',
8 | providerOptions: {
9 | host: 'smtp-relay.sendinblue.com',
10 | port: 587,
11 | auth: {
12 | user: process.env.EMAIL_SMTP_USER,
13 | pass: process.env.EMAIL_SMTP_PASS,
14 | },
15 | },
16 | settings: {
17 | defaultFrom: 'no-reply@casmm.org',
18 | defaultReplyTo: 'no-reply@casmm.org',
19 | },
20 | },
21 | sentry: {
22 | dsn: process.env.SENTRY_DNS || '',
23 | integrations: [new Sentry.Integrations.Http({ tracing: true })],
24 | tracesSampleRate: 1.0,
25 | },
26 | upload: {
27 | config: {
28 | provider: 'local', // or another provider like 'aws-s3'
29 | providerOptions: {
30 | //sizeLimit: 1000000, // Set a file size limit in bytes (optional)
31 | // Additional provider-specific configuration, e.g., AWS S3 credentials
32 | local: {
33 | path: './public/uploads',
34 | keepExtensions: true, // Preserve file extensions during upload
35 | },
36 | },
37 | actionOptions: {
38 | upload: {},
39 | delete: {},
40 | },
41 | },
42 | },
43 | });
44 |
--------------------------------------------------------------------------------
/client/src/views/Mentor/Classroom/Roster/AddStudents/AddStudents.less:
--------------------------------------------------------------------------------
1 | @import '../../../../../assets/style.less';
2 |
3 | #add-students {
4 | #manual-input {
5 | input[type='text'] {
6 | display: block;
7 | background: none;
8 | margin: 20px auto 0px;
9 | border: 1px solid;
10 | border-color: #colors[secondary];
11 | padding: 10px 10px;
12 | width: 40%;
13 | outline: none;
14 | color: #colors[text-primary];
15 | border-radius: 8px;
16 | transition: 0.25s;
17 | }
18 | }
19 |
20 | input[type='button'] {
21 | display: flex;
22 | justify-content: center;
23 | align-items: center;
24 | background: #colors[quaternary];
25 | width: 40%;
26 | height: 6vh;
27 | margin: 0 auto;
28 | border: none;
29 | color: #colors[text-primary];
30 | font-size: 1em;
31 | font-weight: 500;
32 | border-radius: 30px;
33 | box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.377);
34 | cursor: pointer;
35 | transition: 0.25s;
36 |
37 | &:hover {
38 | width: 42%;
39 | background: #colors[quinary];
40 | }
41 | }
42 |
43 | #emoji-picker {
44 | width: 40%;
45 | margin: 2vh auto;
46 |
47 | .emoji-picker-react {
48 | width: 100%;
49 | }
50 | .emoji-categories {
51 | justify-content: space-around;
52 | }
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/client/src/views/Mentor/Classroom/Roster/AddStudents/AddStudentsModal.jsx:
--------------------------------------------------------------------------------
1 | import {Modal, Button} from 'antd';
2 | import React, {useState} from "react";
3 | import AddStudents from "./AddStudents";
4 |
5 | export default function AddStudentsModal(props) {
6 | const [visible, setVisible] = useState(false);
7 | const {classroomId, addStudentsToTable} = props;
8 |
9 | const showModal = () => {
10 | setVisible(true)
11 | };
12 |
13 | const handleCancel = () => {
14 | setVisible(false)
15 | };
16 |
17 | const handleOk = () => {
18 | setVisible(false)
19 | };
20 |
21 | return (
22 |
23 |
26 |
33 | OK
34 | ,
35 | ]}
36 | >
37 |
38 |
39 |
40 | );
41 | }
--------------------------------------------------------------------------------
/server/api/classroom/services/classroom.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | // find a classroom by code
4 | module.exports.findByCode = async (code) => {
5 |
6 | // get the whole classroom model
7 | const model = await strapi.query('classroom').model.where('code', code).fetch()
8 |
9 | // return the model and the classroom
10 | return {
11 | model,
12 | classroom: model ? model.toJSON() : undefined
13 | }
14 | }
15 |
16 | const randomCode = (n) => {
17 |
18 | let code = ''
19 | for (n; n > 0; n--)
20 | code += Math.floor(Math.random()*10)
21 | return code
22 | }
23 |
24 | // TODO: add a maximum number of tries
25 | // generate a code that is unique amongst classroom
26 | module.exports.getUniqueCode = async (currentCode = undefined) => {
27 |
28 | let code = currentCode
29 | let codeExists
30 | do {
31 | // allow an existing code to be passed,
32 | // checking if it active and returning
33 | // a new code if it is being used
34 | if (!code) {
35 | // get a four digit code
36 | code = randomCode(4)
37 | }
38 |
39 | // check if a classroom is using the code
40 | const classroom = await strapi.query('classroom').findOne({ code })
41 | codeExists = classroom !== null
42 | }
43 | while (codeExists)
44 | return code
45 | }
--------------------------------------------------------------------------------
/client/src/views/TeacherLogin/ConfirmEmail.jsx:
--------------------------------------------------------------------------------
1 | import React, { useEffect, useState } from 'react';
2 | import { useLocation, useParams } from 'react-router-dom';
3 | import { confirmEmail } from '../../Utils/requests';
4 | import NavBar from '../../components/NavBar/NavBar';
5 | import './Sorry.less';
6 |
7 |
8 | export default function ConfirmEmail() {
9 | const search = useLocation().search;
10 | const [code, setCode] = useState(null);
11 | const {confirmationCode} = useParams();
12 | useEffect(() => {
13 |
14 | setCode(confirmationCode); // Store the code in state
15 | //console.log(confirmationCode); // Debug: Check if the code is extracted correctly
16 |
17 | // If a code exists, make the API call
18 | if (confirmationCode) {
19 | confirmEmail(confirmationCode)
20 | .then((response) => {
21 | console.log(response);
22 | })
23 | .catch((error) => {
24 | console.error("Error confirming email:", error);
25 | });
26 | }
27 | }, [search]); // Only trigger when the search string changes
28 | return (
29 |
30 |
31 |
32 |
Thank you for confirming!
33 |
34 |
35 |
36 | );
37 | }
--------------------------------------------------------------------------------
/client/src/views/Researcher/Report.jsx:
--------------------------------------------------------------------------------
1 | import NavBar from '../../components/NavBar/NavBar';
2 | import RouteButton from '../../components/RouteButton/RouteButton';
3 | import React, { useState, useEffect } from 'react';
4 | import { Link } from 'react-router-dom';
5 | import './Report.less';
6 |
7 | export default function Report(props) {
8 |
9 | return (
10 |
11 |
12 |
Welcome Researcher!
13 |
14 |
15 |
39 |
40 | );
41 | }
42 |
--------------------------------------------------------------------------------
/client/src/views/BugReport/BugReport.less:
--------------------------------------------------------------------------------
1 | @import '../../assets/style.less';
2 |
3 | #bug-report-wrapper {
4 | margin: 12vh auto 8vh auto;
5 | display: inline-block;
6 | }
7 |
8 | #bug-report-title {
9 | background-color: #colors[secondary];
10 | border-radius: 80px;
11 | width: 80%;
12 | min-height: 8%;
13 | color: #colors[text-secondary];
14 | font-size: 1.4em;
15 | font-weight: bold;
16 | position: relative;
17 | top: 25px;
18 | left: -40px;
19 | line-height: 50px;
20 | text-align: center;
21 | }
22 |
23 | #bug-report-form {
24 | width: 400px;
25 | padding: 40px 20px;
26 | background-color: #colors[tertiary];
27 | text-align: center;
28 | border-radius: 15px;
29 | border: 4px solid;
30 | border-color: #colors[secondary];
31 |
32 | .ant-btn-primary {
33 | display: flex;
34 | justify-content: center;
35 | align-items: center;
36 | background: #colors[quaternary];
37 | width: 50%;
38 | // display: block;
39 | margin: 0 auto;
40 | border: 2px solid;
41 | border-color: #colors[secondary];
42 | padding: 14px 40px;
43 | outline: none;
44 | color: #colors[text-primary];
45 | font-size: 1.2em;
46 | font-weight: 500;
47 | border-radius: 30px;
48 | position: relative;
49 | bottom: -90px;
50 |
51 | &:hover {
52 | background-color: #colors[quinary];
53 | }
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/server/api/session/config/routes.json:
--------------------------------------------------------------------------------
1 | {
2 | "routes": [
3 | {
4 | "method": "GET",
5 | "path": "/sessions",
6 | "handler": "session.find",
7 | "config": {
8 | "policies": []
9 | }
10 | },
11 | {
12 | "method": "GET",
13 | "path": "/sessions/count",
14 | "handler": "session.count",
15 | "config": {
16 | "policies": []
17 | }
18 | },
19 | {
20 | "method": "GET",
21 | "path": "/sessions/:id",
22 | "handler": "session.findOne",
23 | "config": {
24 | "policies": []
25 | }
26 | },
27 | {
28 | "method": "POST",
29 | "path": "/sessions",
30 | "handler": "session.create",
31 | "config": {
32 | "policies": []
33 | }
34 | },
35 | {
36 | "method": "PUT",
37 | "path": "/sessions/:id",
38 | "handler": "session.update",
39 | "config": {
40 | "policies": []
41 | }
42 | },
43 | {
44 | "method": "PUT",
45 | "path": "/sessions/arduino/:id",
46 | "handler": "session.arduinoUpdate",
47 | "config": {
48 | "policies": []
49 | }
50 | },
51 | {
52 | "method": "DELETE",
53 | "path": "/sessions/:id",
54 | "handler": "session.delete",
55 | "config": {
56 | "policies": []
57 | }
58 | }
59 | ]
60 | }
61 |
--------------------------------------------------------------------------------
/client/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "client",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "@loadable/component": "^5.15.2",
7 | "antd": "^4.24.8",
8 | "axios": "^1.4.0",
9 | "cross-env": "^7.0.3",
10 | "emoji-picker-react": "^3.6.1",
11 | "http-server": "^0.12.3",
12 | "less": "^4.1.3",
13 | "react": "^18.2.0",
14 | "react-dom": "^18.2.0",
15 | "react-hooks-global-state": "^2.1.0",
16 | "react-media-library": "^2.0.3",
17 | "react-media-recorder": "^1.7.1",
18 | "react-papaparse": "^3.17.1",
19 | "react-router-dom": "^6.9.0",
20 | "recharts": "^2.5.0",
21 | "vite": "^4.2.0",
22 | "yarn": "^1.22.21"
23 | },
24 | "scripts": {
25 | "start": "vite",
26 | "start-build": "http-server build -p 3000",
27 | "build": "vite build",
28 | "serve": "vite serve"
29 | },
30 | "eslintConfig": {
31 | "extends": "react-app"
32 | },
33 | "browserslist": {
34 | "production": [
35 | ">0.2%",
36 | "not dead",
37 | "not op_mini all"
38 | ],
39 | "development": [
40 | "last 1 chrome version",
41 | "last 1 firefox version",
42 | "last 1 safari version"
43 | ]
44 | },
45 | "devDependencies": {
46 | "@types/w3c-web-serial": "^1.0.3",
47 | "@vitejs/plugin-react-swc": "^3.2.0",
48 | "parse-full-name": "^1.2.6"
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/server/api/learning-component-types/config/routes.json:
--------------------------------------------------------------------------------
1 | {
2 | "routes": [
3 | {
4 | "method": "GET",
5 | "path": "/learning-component-types",
6 | "handler": "learning-component-types.find",
7 | "config": {
8 | "policies": []
9 | }
10 | },
11 | {
12 | "method": "GET",
13 | "path": "/learning-component-types/count",
14 | "handler": "learning-component-types.count",
15 | "config": {
16 | "policies": []
17 | }
18 | },
19 | {
20 | "method": "GET",
21 | "path": "/learning-component-types/:id",
22 | "handler": "learning-component-types.findOne",
23 | "config": {
24 | "policies": []
25 | }
26 | },
27 | {
28 | "method": "POST",
29 | "path": "/learning-component-types",
30 | "handler": "learning-component-types.create",
31 | "config": {
32 | "policies": []
33 | }
34 | },
35 | {
36 | "method": "PUT",
37 | "path": "/learning-component-types/:id",
38 | "handler": "learning-component-types.update",
39 | "config": {
40 | "policies": []
41 | }
42 | },
43 | {
44 | "method": "DELETE",
45 | "path": "/learning-component-types/:id",
46 | "handler": "learning-component-types.delete",
47 | "config": {
48 | "policies": []
49 | }
50 | }
51 | ]
52 | }
53 |
--------------------------------------------------------------------------------
/client/src/assets/style.less:
--------------------------------------------------------------------------------
1 |
2 | #colors() {
3 | primary: #3D5C82;
4 | secondary: #5BABDE;
5 | tertiary: #F4F4F5;
6 | quaternary: #F3D250;
7 | quinary:#F1C307;
8 | senary: #E9E9E9;
9 | text-primary: #414141;
10 | text-secondary: #FFFFFF;
11 | }
12 |
13 | .flex {
14 | display: flex;
15 | }
16 |
17 | .flex-column {
18 | flex-direction: column;
19 | }
20 |
21 | .flex-row {
22 | flex-direction: row;
23 | }
24 |
25 | .space-between {
26 | justify-content: space-between;
27 | }
28 |
29 | .justify-end {
30 | justify-content: flex-end;
31 | }
32 |
33 | .justify-center {
34 | justify-content: center;
35 | }
36 |
37 | .container {
38 | height: 100%;
39 | width: 100%;
40 | background-color: #colors[primary];
41 | min-height: 100vh;
42 | overflow-x: hidden;
43 | }
44 | .container::-webkit-scrollbar {
45 | display: none;
46 | }
47 |
48 | .vertical-container {
49 | margin: 1.5em;
50 | }
51 |
52 | .hvr-info {
53 | cursor: pointer;
54 | }
55 |
56 | .card {
57 | background-color: #colors[tertiary];
58 | padding: .7em;
59 | }
60 |
61 | .overflow-visible {
62 | overflow: visible;
63 | }
64 |
65 | .overflow-hidden {
66 | overflow: hidden;
67 | }
68 |
69 | .nav-padding {
70 | padding-top: 8vh;
71 | }
72 |
73 | .replayButton {
74 | cursor: pointer;
75 | }
76 |
77 | .bold {
78 | font-weight: bold;
79 | }
--------------------------------------------------------------------------------
/client/src/views/Home/HomeJoin.jsx:
--------------------------------------------------------------------------------
1 | import React, { useState } from 'react';
2 | import { useNavigate } from 'react-router-dom';
3 | import { message } from 'antd';
4 | import './Home.less';
5 | import { getStudents } from '../../Utils/requests';
6 |
7 | export default function HomeJoin(props) {
8 | const [loading, setLoading] = useState(false);
9 | const [joinCode, setJoinCode] = useState('');
10 | const navigate = useNavigate();
11 | const handleLogin = () => {
12 | setLoading(true);
13 |
14 | getStudents(joinCode).then((res) => {
15 | if (res.data) {
16 | setLoading(false);
17 | localStorage.setItem('join-code', joinCode);
18 | navigate('/login');
19 | } else {
20 | setLoading(false);
21 | message.error('Join failed. Please input a valid join code.');
22 | }
23 | });
24 | };
25 |
26 | return (
27 | {
30 | if (e.key === 'Enter') handleLogin();
31 | }}
32 | >
33 | setJoinCode(e.target.value)}
38 | />
39 |
45 |
46 | );
47 | }
48 |
--------------------------------------------------------------------------------
/server/config/database.js:
--------------------------------------------------------------------------------
1 | /**
2 | * parses a PostgreSQL DB connection URL into the parts needed
3 | * by Strapi. Without this, heroku may throw ECONNREFUSED 127.0.0.1:xxxx.
4 | */
5 |
6 | const url = require('url')
7 |
8 | if (process.env.DATABASE_URL) {
9 | const parsed = url.parse(process.env.DATABASE_URL, true)
10 | const [username, password] = parsed.auth.split(':')
11 |
12 | process.env.DATABASE_HOST = parsed.hostname
13 | process.env.DATABASE_PORT = Number(parsed.port)
14 | process.env.DATABASE_NAME = parsed.pathname.substr(1)
15 | process.env.DATABASE_USERNAME = username
16 | process.env.DATABASE_PASSWORD = password
17 | }
18 |
19 | module.exports = ({ env }) => ({
20 | defaultConnection: 'default',
21 | connections: {
22 | default: {
23 | connector: 'bookshelf',
24 | settings: {
25 | client: 'postgres',
26 | host: env('DATABASE_HOST', 'localhost'),
27 | port: env.int('DATABASE_PORT', 5432),
28 | database: env('DATABASE_NAME', 'strapi'),
29 | username: env('DATABASE_USERNAME', 'postgres'),
30 | password: env('DATABASE_PASSWORD', 'postgres'),
31 | schema: 'public',
32 | ssl: {rejectUnauthorized: false},
33 | },
34 | options: {
35 | 'pool': {
36 | 'min': 0,
37 | 'max': 15,
38 | 'idleTimeoutMillis': 30000,
39 | 'createTimeoutMillis': 30000,
40 | 'acquireTimeoutMillis': 30000
41 | }
42 | }
43 | },
44 | },
45 | })
--------------------------------------------------------------------------------
/server/api/day/models/day.settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "kind": "collectionType",
3 | "collectionName": "days",
4 | "info": {
5 | "name": "Day",
6 | "description": ""
7 | },
8 | "options": {
9 | "increments": true,
10 | "timestamps": true,
11 | "draftAndPublish": false
12 | },
13 | "attributes": {
14 | "learning_standard": {
15 | "via": "days",
16 | "model": "learning-standard"
17 | },
18 | "number": {
19 | "type": "biginteger",
20 | "required": true
21 | },
22 | "template": {
23 | "type": "text",
24 | "required": true
25 | },
26 | "template_code": {
27 | "type": "text",
28 | "required": false
29 | },
30 | "template_code_answer": {
31 | "type": "text",
32 | "required": false
33 | },
34 | "blocks": {
35 | "collection": "block"
36 | },
37 | "description": {
38 | "type": "text",
39 | "required":false
40 | },
41 | "TekS": {
42 | "type": "string",
43 | "required": false
44 | },
45 | "images": {
46 | "type": "text"
47 | },
48 | "link": {
49 | "type": "string",
50 | "required": false
51 | },
52 | "learning_components": {
53 | "via": "days",
54 | "collection": "learning-components"
55 | },
56 | "activity_template": {
57 | "type": "text",
58 | "required": false
59 | }
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/server/api/mentor/controllers/mentor.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const { sanitizeEntity } = require("strapi-utils/lib")
4 |
5 | module.exports = {
6 |
7 | async me(ctx) {
8 |
9 | // get the mentor that is currently logged in
10 | const { user } = ctx.state
11 | const mentor = await strapi.services.mentor.findOne({ user: user.id })
12 |
13 | // remove private fields and return the mentor
14 | return sanitizeEntity(mentor, { model: strapi.models.mentor })
15 | },
16 |
17 | /**
18 | * Create a new mentor
19 | *
20 | * @param {String} first_name
21 | * @param {String} last_name
22 | *
23 | * @return {Mentor}
24 | */
25 | async create(ctx) {
26 |
27 | // ensure the request is in the right format
28 | if (ctx.is('multipart')) return ctx.badRequest(
29 | 'Multipart requests are not accepted!',
30 | { id: 'Mentor.create.format.invalid', error: 'ValidationError' }
31 | )
32 |
33 | // get the user that is currently logged in
34 | // to set as the new mentor's user
35 | const { user } = ctx.state
36 | ctx.request.body.user = user.id
37 |
38 | // remove private fields and return the new mentor
39 | const mentor = await strapi.services.mentor.create(ctx.request.body)
40 | return sanitizeEntity(mentor, { model: strapi.models.mentor })
41 | }
42 | }
--------------------------------------------------------------------------------
/client/public/lib/readme.md:
--------------------------------------------------------------------------------
1 | ## About This Directory
2 | This directory is used to store the libraries being used for this web app. Notably, the [ArduBlockly](https://github.com/carlosperate/ardublockly) library, which relies on Google's [Blockly](https://github.com/google/blockly) library. The files are referenced in `index.html`.
3 |
4 | These compressed files are generated with ArduBlockly's build script. Since ArduBlockly have ended development since 2018, we've made our own branch of ArduBlockly, as seen [here](https://github.com/STEM-C/ardublockly), where we've made bug fixes and added additional support for different blocks.
5 |
6 | Details about building the compressed files and adding to our ArduBlockly library can be seen [here](https://github.com/STEM-C/ardublockly/wiki/Working-with-the-ArduBlockly-Library).
7 |
8 | ## depreciated.js
9 | Inside this file, depreciated code from google's closure library can be found.
10 |
11 | The Blockly library that ArduBlockly is based off of makes use of Google's closure library. During the build process, the most recent version of the closure library is pulled. Since the original ArduBlockly ended development in 2018, and the version of Blockly that it's based off of came from 2016, a number of elements in the codebase have been depreciated according to Google's code base.
12 |
13 | By keeping these depreciated code in this file, we ensure that our version of Blockly continues to function.
--------------------------------------------------------------------------------
/compile/src/handlers/worker.js:
--------------------------------------------------------------------------------
1 | const { processJob } = require("../controllers/job")
2 | const { compileLog } = require("../utils/base")
3 |
4 | const Queue = require("bull")
5 | const redisUrlParse = require("redis-url-parse")
6 |
7 | // Connect to a local redis instance locally, and the Heroku-provided URL in production
8 | const REDIS_URL = process.env.REDIS_URL || "redis://compile_queue:6379"
9 | const { host, port, password } = redisUrlParse(REDIS_URL)
10 | const bullOptions = REDIS_URL.includes("rediss://")
11 | ? {
12 | redis: {
13 | port: Number(port),
14 | host,
15 | password,
16 | tls: {
17 | rejectUnauthorized: false,
18 | requestCert: true,
19 | },
20 | },
21 | }
22 | : REDIS_URL
23 | // The maximum number of jobs each worker should process at once
24 | // Each job is CPU-intensive, so this value should not be too high
25 | const maxJobsPerWorker = process.env.JOB_CONCURRENCY || 1
26 |
27 | module.exports.init = () => {
28 | // starting up the service
29 | compileLog("Starting compile cluster")
30 | }
31 |
32 | module.exports.start = id => {
33 | // Signal worked started
34 | compileLog(`Started worker ${id}`)
35 |
36 | // Connect to the named queue
37 | const compile_queue = new Queue("submissions", bullOptions)
38 |
39 | // start processing jobs from the submission queue
40 | compile_queue.process(maxJobsPerWorker, processJob)
41 | }
42 |
--------------------------------------------------------------------------------
/server/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "server",
3 | "private": true,
4 | "version": "0.1.0",
5 | "description": "A REST API and admin portal",
6 | "scripts": {
7 | "develop": "strapi develop",
8 | "start": "strapi start",
9 | "build": "strapi build",
10 | "strapi": "strapi",
11 | "build-client": "rm -rf ./public/client/* && cd ../client && env PUBLIC_URL=/client yarn build && mv ./build/* ../server/public/client/"
12 | },
13 | "dependencies": {
14 | "bull": "^4.10.0",
15 | "dotenv": "^10.0.0",
16 | "knex": "0.21.18",
17 | "pg": "^8.10.0",
18 | "redis-url-parse": "^2.0.0",
19 | "sharp": "^0.31.3",
20 | "strapi": "^3.6.11",
21 | "strapi-admin": "^3.6.11",
22 | "strapi-connector-bookshelf": "^3.6.11",
23 | "strapi-plugin-content-manager": "^3.6.11",
24 | "strapi-plugin-content-type-builder": "^3.6.11",
25 | "strapi-plugin-documentation": "^3.6.11",
26 | "strapi-plugin-email": "^3.6.11",
27 | "strapi-plugin-sentry": "^3.6.11",
28 | "strapi-plugin-upload": "^3.6.11",
29 | "strapi-plugin-users-permissions": "^3.6.11",
30 | "strapi-provider-email-nodemailer": "^3.6.11",
31 | "strapi-utils": "^3.6.11"
32 | },
33 | "author": {
34 | "name": "Nicholas Ionata, Siyu Chen"
35 | },
36 | "strapi": {
37 | "uuid": "d9c4d9b6-4733-4e82-80ee-0a812c951edb"
38 | },
39 | "engines": {
40 | "node": ">=10.0.0",
41 | "npm": ">=6.0.0"
42 | },
43 | "license": "MIT"
44 | }
45 |
--------------------------------------------------------------------------------
/.github/workflows/start-review.yml:
--------------------------------------------------------------------------------
1 | name: Create Review App
2 | on:
3 | pull_request:
4 | branches: [develop]
5 | types: [opened, reopened]
6 | jobs:
7 | run:
8 | runs-on: ubuntu-latest
9 | steps:
10 | - name: Checkout the repo
11 | uses: actions/checkout@v2
12 | - name: Create review app
13 | id: app
14 | uses: STEM-C/auto/review@v0.7.2
15 | with:
16 | base: review
17 | pipeline: ${{ secrets.PIPELINE_ID }}
18 | token: ${{ secrets.HEROKU_TOKEN }}
19 | - name: Import development database
20 | run: ./scripts/init_db.sh
21 | env:
22 | ENVIRONMENT: development
23 | DATABASE_URL: ${{ steps.app.outputs.database_url }}
24 | SCRIPT_PATH: ./scripts
25 | - name: Built, test, and deploy app
26 | uses: STEM-C/auto/build-test-deploy@v0.7.2
27 | with:
28 | image_tag: ${{ steps.app.outputs.app_name }}
29 | app_name: ${{ steps.app.outputs.app_name }}
30 | github_token: ${{ secrets.GITHUB_TOKEN }}
31 | env:
32 | HEROKU_API_KEY: ${{ secrets.HEROKU_TOKEN }}
33 | - uses: chrnorm/deployment-action@releases/v1
34 | name: Create GitHub deployment
35 | if: success()
36 | id: deployment
37 | with:
38 | initial_status: 'success'
39 | token: ${{ github.token }}
40 | target_url: https://${{ steps.app.outputs.app_name }}.herokuapp.com
41 | environment: ${{ steps.app.outputs.app_name }}
42 |
--------------------------------------------------------------------------------
/server/api/cc-workspace/config/routes.json:
--------------------------------------------------------------------------------
1 | {
2 | "routes": [
3 | {
4 | "method": "GET",
5 | "path": "/cc-workspaces",
6 | "handler": "cc-workspace.find",
7 | "config": {
8 | "policies": []
9 | }
10 | },
11 | {
12 | "method": "GET",
13 | "path": "/cc-workspaces/count",
14 | "handler": "cc-workspace.count",
15 | "config": {
16 | "policies": []
17 | }
18 | },
19 | {
20 | "method": "GET",
21 | "path": "/cc-workspaces/:id",
22 | "handler": "cc-workspace.findOne",
23 | "config": {
24 | "policies": ["isContentCreatorOrHasClassroom"]
25 | }
26 | },
27 | {
28 | "method": "GET",
29 | "path": "/cc-workspaces/toolbox/:id",
30 | "handler": "cc-workspace.toolbox",
31 | "config": {
32 | "policies": []
33 | }
34 | },
35 | {
36 | "method": "POST",
37 | "path": "/cc-workspaces",
38 | "handler": "cc-workspace.create",
39 | "config": {
40 | "policies": ["isContentCreatorOrHasClassroom"]
41 | }
42 | },
43 | {
44 | "method": "PUT",
45 | "path": "/cc-workspaces/:id",
46 | "handler": "cc-workspace.update",
47 | "config": {
48 | "policies": ["isContentCreatorOrHasClassroom"]
49 | }
50 | },
51 | {
52 | "method": "DELETE",
53 | "path": "/cc-workspaces/:id",
54 | "handler": "cc-workspace.delete",
55 | "config": {
56 | "policies": ["isContentCreatorOrHasClassroom"]
57 | }
58 | }
59 | ]
60 | }
61 |
--------------------------------------------------------------------------------
/client/src/views/TeacherLogin/Sorry.jsx:
--------------------------------------------------------------------------------
1 | import {React, useEffect} from "react"
2 | import NavBar from "../../components/NavBar/NavBar"
3 | import "./Sorry.less"
4 | import { getSupers } from "../../Utils/AuthRequests"
5 | import { sendEmailConfirmationEmail } from "../../Utils/requests"
6 |
7 |
8 | export default function Sorry(props) {
9 | const superEmails = []
10 | const myUser = JSON.parse(sessionStorage.user)
11 |
12 | useEffect( () => {
13 |
14 | console.log(myUser)
15 | getSupers()
16 | .then(response => {
17 | //console.log(response.data)
18 | for (var i = 0; i < response.data.length; i++){
19 | if (response.data[i].isActive) {
20 | superEmails.push(response.data[i].email);
21 | };
22 | //console.log(superEmails)
23 | };
24 | })
25 |
26 | }, [])
27 |
28 | const resendEmail = () => {
29 | for (var i = 0; i < superEmails.length; i++){
30 | sendEmailConfirmationEmail(myUser.email, superEmails[i])
31 | .then(response => {
32 | //console.log('hihihi');
33 | })
34 | }
35 | }
36 |
37 | return (
38 |
39 |
40 |
41 |
Hey! Looks like your account is not confirmed. Please ask a "STRAPI SUPER ADMIN" to
42 | check their email for a confirmation code.
43 |
If you wish to resend a confirmation email, click here
44 |
45 |
46 |
47 | )
48 | }
49 |
--------------------------------------------------------------------------------
/client/vite.config.js:
--------------------------------------------------------------------------------
1 | import react from "@vitejs/plugin-react-swc"
2 | import { defineConfig } from "vite"
3 |
4 | const base = process.env.PUBLIC_URL ?? "/"
5 | console.log(base);
6 |
7 | export default defineConfig({
8 | css: {
9 | preprocessorOptions: {
10 | less: {
11 | modifyVars: {
12 | "@primary-color": "#3d5c82", // primary color for all components
13 | "@link-color": "#3d5c82", // link color
14 | "@success-color": "#52c41a", // success state color
15 | "@warning-color": "#faad14", // warning state color
16 | "@error-color": "#f5222d", // error state color
17 | "@font-size-base": "14px", // major text font size
18 | "@heading-color": "rgba(0, 0, 0, 0.85)", // heading text color
19 | "@text-color": "rgba(0, 0, 0, 0.65)", // major text color
20 | "@text-color-secondary": "rgba(0, 0, 0, 0.45)", // secondary text color
21 | "@disabled-color": "rgba(0, 0, 0, 0.25)", // disable state color
22 | "@border-radius-base": "4px", // major border radius
23 | "@border-color-base": "#d9d9d9", // major border color
24 | "@box-shadow-base": "0 2px 8px rgba(0, 0, 0, 0.15)", // major shadow for layers
25 | },
26 | javascriptEnabled: true,
27 | },
28 | },
29 | },
30 | //in build mode, the output will be copoied to server/puiblic/client
31 | base,
32 | plugins: [react()],
33 | server: {
34 | port: 3000,
35 | proxy: {
36 | "/api": {
37 | target: "http://localhost:1337",
38 | },
39 | },
40 | },
41 | build: {
42 | outDir: "build",
43 | },
44 | })
45 |
--------------------------------------------------------------------------------
/client/src/views/Replay/Replay.less:
--------------------------------------------------------------------------------
1 | @import '../../assets/style.less';
2 |
3 | #timeline-container {
4 | display: flex;
5 | justify-content: center;
6 | }
7 |
8 | #timeline {
9 | height: 1px;
10 | background: rgb(255, 255, 255);
11 | margin: 60px 20px;
12 | width: 100%;
13 | display: flex;
14 | align-items: center;
15 | justify-content: space-around;
16 | font-size: 13px;
17 | }
18 |
19 | .current-time {
20 | display: flex;
21 | justify-content: center;
22 | align-items: center;
23 | font-weight: bold;
24 | color: #colors[quaternary];
25 | background-color: #colors[primary];
26 | transform: rotate(45deg);
27 | width: auto;
28 | height: 19px;
29 | border-radius: 10px;
30 | }
31 |
32 | .all-times {
33 | color: white;
34 | background-color: #colors[primary];
35 | transform: rotate(45deg);
36 | &:hover {
37 | cursor: pointer;
38 | }
39 | }
40 |
41 | #description-container {
42 | border-radius: 10px;
43 | top: 10px;
44 | }
45 |
46 | #logs-title {
47 | font-size: 18px;
48 | }
49 |
50 | .playspeed-slider {
51 | width: 100px;
52 | .ant-slider-rail,
53 | .ant-slider-track {
54 | &,
55 | &:hover {
56 | background-color: #colors[secondary] !important;
57 | }
58 | }
59 | }
60 |
61 | #action-title{
62 | color: rgb(19, 18, 18);
63 | font-size: larger;
64 | font-weight: 600;
65 | }
66 |
67 | #replay-log{
68 |
69 | tbody > tr > td {
70 | background: none;
71 | }
72 | .table-row {
73 | cursor: pointer;
74 | }
75 | .table-row-light {
76 | background-color: #ffffff;
77 | }
78 | .table-row-dark {
79 | background-color: #cecece;
80 | }
81 |
82 | }
83 |
--------------------------------------------------------------------------------
/client/src/components/MentorSubHeader/MentorSubHeader.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Link } from 'react-router-dom';
3 | import AddStudentsModal from '../../views/Mentor/Classroom/Roster/AddStudents/AddStudentsModal';
4 | import './MentorSubHeader.less';
5 |
6 | export default function MentorSubHeader(props) {
7 | const {
8 | title,
9 | addActivityActive,
10 | addUserActive,
11 | classroomId,
12 | cardViewActive,
13 | listViewActive,
14 | checkoutActive,
15 | setListView,
16 | addStudentsToTable,
17 | } = props;
18 |
19 | return (
20 |
51 | );
52 | }
53 |
--------------------------------------------------------------------------------
/client/src/views/Workspace/Workspace.jsx:
--------------------------------------------------------------------------------
1 | import React, { useEffect, useState } from 'react';
2 | import { getDayToolbox } from '../../Utils/requests.js';
3 | import BlocklyCanvasPanel from '../../components/DayPanels/BlocklyCanvasPanel/BlocklyCanvasPanel';
4 | import { message } from 'antd';
5 | import NavBar from '../../components/NavBar/NavBar';
6 | import { useNavigate } from 'react-router-dom';
7 |
8 | export default function Workspace({ handleLogout }) {
9 | const [day, setDay] = useState({});
10 |
11 | useEffect(() => {
12 | const localDay = JSON.parse(localStorage.getItem('my-day'));
13 | const navigate = useNavigate();
14 |
15 | if (localDay) {
16 | if (localDay.toolbox) {
17 | setDay(localDay);
18 | } else {
19 | getDayToolbox(localDay.id).then((res) => {
20 | if (res.data) {
21 | let loadedDay = { ...localDay, toolbox: res.data.toolbox };
22 |
23 | localStorage.setItem('my-day', JSON.stringify(loadedDay));
24 | setDay(loadedDay);
25 | } else {
26 | message.error(res.err);
27 | }
28 | });
29 | }
30 | } else {
31 | navigate(-1);
32 | }
33 | }, []);
34 |
35 | const handleGoBack = () => {
36 | navigate(-1);
37 | };
38 |
39 | return (
40 |
41 |
42 |
49 |
50 | );
51 | }
52 |
--------------------------------------------------------------------------------
/client/src/views/Mentor/Classroom/Roster/StudentModal.jsx:
--------------------------------------------------------------------------------
1 | import { Modal, Button } from 'antd';
2 | import React, { useState } from 'react';
3 |
4 | export default function StudentModal({ linkBtn, student, getFormattedDate }) {
5 | const [visible, setVisible] = useState(false);
6 |
7 | const showModal = () => {
8 | setVisible(true);
9 | };
10 |
11 | const handleCancel = () => {
12 | setVisible(false);
13 | };
14 |
15 | const handleOk = () => {
16 | setVisible(false);
17 | };
18 |
19 | return (
20 |
21 |
24 |
30 | OK
31 | ,
32 | ]}
33 | >
34 |
38 |
39 |
40 |
Last logged in:
41 |
{getFormattedDate(student.last_logged_in)}
42 |
43 |
44 |
45 |
Status:
46 |
47 | {student.enrolled.enrolled ? 'Enrolled' : 'Unenrolled'}
48 |
49 |
50 |
51 |
52 |
53 | );
54 | }
55 |
--------------------------------------------------------------------------------
/client/src/Utils/AuthRequests.js:
--------------------------------------------------------------------------------
1 | import { server } from './hosts';
2 | import { setUserState, getCurrUser } from './userState';
3 |
4 | import axios from 'axios';
5 |
6 | // return user token from strapi
7 | export const postUser = async (body) => {
8 | const response = await axios.post(`${server}/auth/local`, body);
9 | return response;
10 | };
11 |
12 | export const regUser = async (body) => {
13 | const response = await axios.post(`${server}/auth/local/register`, body);
14 | //username: 'testuser',
15 | //email: 'tu@mail.com',
16 | //password: '123456',
17 |
18 | return response;
19 | };
20 |
21 |
22 |
23 | // return token from session storage
24 | export const getToken = () => {
25 | return sessionStorage.getItem('token') || null;
26 | };
27 |
28 | // remove the token ans user from the session storage
29 | export const removeUserSession = () => {
30 | sessionStorage.removeItem('token');
31 | sessionStorage.removeItem('user');
32 | setUserState(getCurrUser());
33 | };
34 |
35 | // set the token and user from the session storage
36 | export const setUserSession = (jwt, user) => {
37 | sessionStorage.setItem('token', jwt);
38 | sessionStorage.setItem('user', user);
39 | setUserState(getCurrUser());
40 | };
41 |
42 | export const getSupers = async () => {
43 | const response = await axios.get(`${server}/strapiusers/super-admin`);
44 |
45 | return response;
46 | };
47 |
48 | export const getConfirmed = async () => {
49 | var thisUser = JSON.parse(sessionStorage.getItem('user'));
50 | return thisUser.confirmed;
51 | };
52 |
53 | export const getMyRole = async () => {
54 | var thisUser = JSON.parse(sessionStorage.getItem('user'));
55 | return thisUser.role;
56 | };
--------------------------------------------------------------------------------
/client/src/views/Mentor/Classroom/Home/DisplayFormModal.jsx:
--------------------------------------------------------------------------------
1 | import {Modal, Button} from 'antd';
2 | import React, {useState} from "react";
3 | import { Form } from 'react-router-dom';
4 | import './Home.less'
5 | import { updateClassroom } from '../../../../Utils/requests';
6 |
7 | export default function DisplayFormModal(props) {
8 | const [visible, setVisible] = useState(false);
9 | const {classroom} = props;
10 | const [form, setForm] = useState('')
11 |
12 | const showModal = () => {
13 | setForm('')
14 | setVisible(true)
15 | };
16 |
17 | const handleCancel = () => {
18 | setVisible(false)
19 | };
20 |
21 | const handleTyping = (event) => {
22 |
23 | setForm(event.target.value)
24 | }
25 |
26 | const handleOk = () => {
27 | //console.log(form)
28 | classroom.form = form
29 | updateClassroom(classroom.id, classroom.form)
30 | setVisible(false)
31 | };
32 |
33 | return (
34 |
35 |
36 |
43 | OK
44 | ,
45 | ]}
46 | >
47 |
48 |
49 |
50 | );
51 | }
52 |
--------------------------------------------------------------------------------
/client/src/components/DayPanels/BlocklyCanvasPanel/modals/CodeModal.jsx:
--------------------------------------------------------------------------------
1 | import { Modal, Button, Typography, Menu } from 'antd';
2 | import React, { useState } from 'react';
3 | import { getArduino, getXml } from '../../Utils/helpers';
4 |
5 | export default function CodeModal(props) {
6 | const [visible, setVisible] = useState(false);
7 | const { title, workspaceRef } = props;
8 | const { Text } = Typography;
9 |
10 | const showModal = () => {
11 | setVisible(true);
12 | };
13 |
14 | const handleCancel = () => {
15 | setVisible(false);
16 | };
17 |
18 | const handleOk = () => {
19 | setVisible(false);
20 | };
21 |
22 | return (
23 |
24 | {title === 'XML' ? (
25 |
26 |
27 | Show XML
28 |
29 | ) : (
30 |
31 |
32 | Show Arduino Code
33 |
34 | )}
35 |
42 | OK
43 | ,
44 | ]}
45 | >
46 | {workspaceRef ? (
47 |
48 |
49 | {title === 'XML'
50 | ? getXml(workspaceRef, false)
51 | : getArduino(workspaceRef, false)}
52 |
53 |
54 | ) : null}
55 |
56 |
57 | );
58 | }
59 |
--------------------------------------------------------------------------------
/server/api/classroom-manager/documentation/1.0.0/classroom-manager.json:
--------------------------------------------------------------------------------
1 | {
2 | "paths": {
3 | "/classroom-managers/me": {
4 | "get": {
5 | "deprecated": false,
6 | "description": "",
7 | "responses": {
8 | "200": {
9 | "description": "response",
10 | "content": {
11 | "application/json": {
12 | "schema": {
13 | "properties": {
14 | "foo": {
15 | "type": "string"
16 | }
17 | }
18 | }
19 | }
20 | }
21 | },
22 | "403": {
23 | "description": "Forbidden",
24 | "content": {
25 | "application/json": {
26 | "schema": {
27 | "$ref": "#/components/schemas/Error"
28 | }
29 | }
30 | }
31 | },
32 | "404": {
33 | "description": "Not found",
34 | "content": {
35 | "application/json": {
36 | "schema": {
37 | "$ref": "#/components/schemas/Error"
38 | }
39 | }
40 | }
41 | },
42 | "default": {
43 | "description": "unexpected error",
44 | "content": {
45 | "application/json": {
46 | "schema": {
47 | "$ref": "#/components/schemas/Error"
48 | }
49 | }
50 | }
51 | }
52 | },
53 | "summary": "",
54 | "tags": [
55 | "Classroom-manager"
56 | ],
57 | "parameters": []
58 | }
59 | }
60 | },
61 | "tags": []
62 | }
--------------------------------------------------------------------------------
/server/middlewares/proxy/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | /**
4 | * Module dependencies
5 | */
6 | const fs = require('fs');
7 | const path = require('path');
8 |
9 | module.exports = (strapi) => {
10 | return {
11 | /**
12 | * Initialize the hook
13 | */
14 |
15 | initialize() {
16 | strapi.app.use(async (ctx, next) => {
17 | const reqPath = ctx.request.path;
18 | const reqHost = ctx.request.header.host;
19 | const reqReferer = ctx.request.header.referer;
20 | const refererUrl = reqReferer
21 | ? reqReferer
22 | .replace('http://', '')
23 | .replace('https://', '')
24 | .replace(reqHost, '')
25 | : '';
26 |
27 | if (
28 | reqPath === '/favicon.ico' ||
29 | reqPath.startsWith('/admin') ||
30 | reqPath.startsWith('/client') ||
31 | refererUrl.startsWith('/admin') ||
32 | reqPath.startsWith('/documentation') ||
33 | refererUrl.startsWith('/documentation')
34 | ) {
35 | // if request for favicon, admin, or client or if request from admin, go next
36 | await next();
37 | } else if (reqPath.startsWith('/api')) {
38 | // if api request, remove /api
39 | ctx.request.path = reqPath.replace('/api', '');
40 | await next();
41 | } else {
42 | // serve the index.html for the client route
43 | const { clientPath } = strapi.config.middleware.settings.proxy;
44 | const clientDir = path.resolve(strapi.dir, clientPath);
45 |
46 | ctx.type = 'html';
47 | ctx.body = fs.createReadStream(path.join(clientDir + '/index.html'));
48 | }
49 | });
50 | },
51 | };
52 | };
53 |
--------------------------------------------------------------------------------
/server/api/strapiusers/documentation/1.0.0/admin.json:
--------------------------------------------------------------------------------
1 | {
2 | "paths": {
3 | "/admin/super-admin": {
4 | "get": {
5 | "deprecated": false,
6 | "description": "",
7 | "responses": {
8 | "200": {
9 | "description": "response",
10 | "content": {
11 | "application/json": {
12 | "schema": {
13 | "properties": {
14 | "foo": {
15 | "type": "string"
16 | }
17 | }
18 | }
19 | }
20 | }
21 | },
22 | "403": {
23 | "description": "Forbidden",
24 | "content": {
25 | "application/json": {
26 | "schema": {
27 | "$ref": "#/components/schemas/Error"
28 | }
29 | }
30 | }
31 | },
32 | "404": {
33 | "description": "Not found",
34 | "content": {
35 | "application/json": {
36 | "schema": {
37 | "$ref": "#/components/schemas/Error"
38 | }
39 | }
40 | }
41 | },
42 | "default": {
43 | "description": "unexpected error",
44 | "content": {
45 | "application/json": {
46 | "schema": {
47 | "$ref": "#/components/schemas/Error"
48 | }
49 | }
50 | }
51 | }
52 | },
53 | "summary": "",
54 | "tags": [
55 | "Admin"
56 | ],
57 | "parameters": []
58 | }
59 | }
60 | },
61 | "tags": [
62 | {
63 | "name": "Admin"
64 | }
65 | ]
66 | }
--------------------------------------------------------------------------------
/client/src/views/Mentor/Classroom/Roster/CardView.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import StudentModal from "./StudentModal";
3 |
4 | export default function CardView(props) {
5 | const {studentData, onEnrollToggle, getFormattedDate} = props;
6 |
7 | return (
8 |
9 | {studentData.map(student =>
10 |
11 |
15 |
16 |
17 |
Last logged in:
18 |
{getFormattedDate(student.last_logged_in)}
19 |
20 |
21 |
22 |
Status:
23 |
{student.enrolled.enrolled ? 'Enrolled' : 'Unenrolled'}
24 |
25 |
26 |
27 |
28 |
33 |
34 |
35 | )}
36 |
37 | )
38 | }
39 |
--------------------------------------------------------------------------------
/server/api/strapiusers/documentation/1.0.0/strapiusers.json:
--------------------------------------------------------------------------------
1 | {
2 | "paths": {
3 | "/strapiusers/super-admin": {
4 | "get": {
5 | "deprecated": false,
6 | "description": "",
7 | "responses": {
8 | "200": {
9 | "description": "response",
10 | "content": {
11 | "application/json": {
12 | "schema": {
13 | "properties": {
14 | "foo": {
15 | "type": "string"
16 | }
17 | }
18 | }
19 | }
20 | }
21 | },
22 | "403": {
23 | "description": "Forbidden",
24 | "content": {
25 | "application/json": {
26 | "schema": {
27 | "$ref": "#/components/schemas/Error"
28 | }
29 | }
30 | }
31 | },
32 | "404": {
33 | "description": "Not found",
34 | "content": {
35 | "application/json": {
36 | "schema": {
37 | "$ref": "#/components/schemas/Error"
38 | }
39 | }
40 | }
41 | },
42 | "default": {
43 | "description": "unexpected error",
44 | "content": {
45 | "application/json": {
46 | "schema": {
47 | "$ref": "#/components/schemas/Error"
48 | }
49 | }
50 | }
51 | }
52 | },
53 | "summary": "",
54 | "tags": [
55 | "Strapiusers"
56 | ],
57 | "parameters": []
58 | }
59 | }
60 | },
61 | "tags": [
62 | {
63 | "name": "Strapiusers"
64 | }
65 | ]
66 | }
--------------------------------------------------------------------------------
/client/src/views/ContentCreator/UnitCreator/UnitCreator.less:
--------------------------------------------------------------------------------
1 | @import '../../../assets/style.less';
2 |
3 | #class-card {
4 | text-align: left;
5 | width: 35vw;
6 | height: 100%;
7 | margin: 2vw auto 0 auto;
8 | background: #colors[tertiary];
9 | color: #colors[text-primary];
10 | border: solid 4px;
11 | border-color: #colors[secondary];
12 | border-radius: 10px;
13 | }
14 |
15 | #add-unit-btn {
16 | position: relative;
17 | top: -2vh;
18 | width: auto;
19 | max-height: 9vh;
20 | padding: 5px 10px;
21 | font-size: 1em;
22 | font-weight: 500;
23 | border-radius: 30px;
24 | margin-right: 20px;
25 | background: #colors[quaternary];
26 | border: none;
27 | color: #colors[text-primary];
28 | transition: 0.25s;
29 | cursor: pointer;
30 | box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.377);
31 |
32 | &:hover {
33 | background: #colors[quinary];
34 | }
35 | }
36 |
37 | #add-units {
38 | input,
39 | textarea {
40 | display: block;
41 | margin: 5px 5px 0px;
42 | background: none;
43 | border: 1px solid;
44 | border-color: #colors[secondary];
45 | padding: 10px 10px;
46 | width: 95%;
47 | outline: none;
48 | color: #colors[text-primary];
49 | border-radius: 4px;
50 | transition: 0.25s;
51 | }
52 |
53 | label {
54 | margin: 10px auto 12px;
55 | white-space: pre-wrap;
56 | }
57 | }
58 |
59 | #grade-dropdown {
60 | display: block;
61 | background: none;
62 | margin: 5px auto 0px;
63 | border: 1px solid;
64 | border-color: #colors[secondary];
65 | padding: 14px 10px;
66 | width: 95%;
67 | outline: none;
68 | color: #colors[text-primary];
69 | border-radius: 4px;
70 | transition: 0.25s;
71 | }
72 |
73 | #grade:focus {
74 | width: 100%;
75 | }
76 |
77 | .content-creator-button {
78 | float: right;
79 | margin: 0px 5px;
80 | width: 8vw;
81 | }
82 |
--------------------------------------------------------------------------------
/server/api/block/models/block.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | /**
4 | * Lifecycle callbacks for the `block` model.
5 | */
6 |
7 | module.exports = {
8 | // Before saving a value.
9 | // Fired before an `insert` or `update` query.
10 | // beforeSave: async (model, attrs, options) => {},
11 |
12 | // After saving a value.
13 | // Fired after an `insert` or `update` query.
14 | // afterSave: async (model, response, options) => {},
15 |
16 | // Before fetching a value.
17 | // Fired before a `fetch` operation.
18 | // beforeFetch: async (model, columns, options) => {},
19 |
20 | // After fetching a value.
21 | // Fired after a `fetch` operation.
22 | // afterFetch: async (model, response, options) => {},
23 |
24 | // Before fetching all values.
25 | // Fired before a `fetchAll` operation.
26 | // beforeFetchAll: async (model, columns, options) => {},
27 |
28 | // After fetching all values.
29 | // Fired after a `fetchAll` operation.
30 | // afterFetchAll: async (model, response, options) => {},
31 |
32 | // Before creating a value.
33 | // Fired before an `insert` query.
34 | // beforeCreate: async (model, attrs, options) => {},
35 |
36 | // After creating a value.
37 | // Fired after an `insert` query.
38 | // afterCreate: async (model, attrs, options) => {},
39 |
40 | // Before updating a value.
41 | // Fired before an `update` query.
42 | // beforeUpdate: async (model, attrs, options) => {},
43 |
44 | // After updating a value.
45 | // Fired after an `update` query.
46 | // afterUpdate: async (model, attrs, options) => {},
47 |
48 | // Before destroying a value.
49 | // Fired before a `delete` query.
50 | // beforeDestroy: async (model, attrs, options) => {},
51 |
52 | // After destroying a value.
53 | // Fired after a `delete` query.
54 | // afterDestroy: async (model, attrs, options) => {}
55 | };
56 |
--------------------------------------------------------------------------------
/client/src/views/Mentor/Classroom/Home/LearningStandardSelect/CheckUnits.jsx:
--------------------------------------------------------------------------------
1 | import {Checkbox} from 'antd';
2 | import React, {useState} from "react";
3 |
4 | const CheckboxGroup = Checkbox.Group;
5 |
6 | export default function CheckUnits(props) {
7 | const {plainOptions, checkedList, setCheckedList} = props;
8 | const [indeterminate, setIndeterminate] = useState(false);
9 | const [checkAll, setCheckAll] = useState(true);
10 |
11 |
12 | const onChange = checked => {
13 | setCheckedList(checked.map(value => {
14 | const option = getOptions(value);
15 | return {...option}
16 | }));
17 | setIndeterminate(!!checked.length && checked.length < plainOptions.length);
18 | setCheckAll(checked.length === plainOptions.length)
19 | };
20 |
21 | const onCheckAllChange = e => {
22 | setCheckedList(e.target.checked ? plainOptions : []);
23 | setIndeterminate(false);
24 | setCheckAll(e.target.checked)
25 | };
26 |
27 | const getOptions = (value) => {
28 | return plainOptions.find(option => option.number === parseInt(value))
29 | };
30 |
31 | return (
32 | <>
33 |
34 |
39 | All Units
40 |
41 |
42 |
43 | { return { label: `Unit ${option.number}`, value: option.number } })}
45 | value={checkedList.map(checked => checked.number)}
46 | onChange={onChange}
47 | />
48 | >
49 | );
50 | }
51 |
--------------------------------------------------------------------------------
/client/src/views/Mentor/Classroom/Classroom.jsx:
--------------------------------------------------------------------------------
1 | import {React, useEffect} from 'react';
2 | import { Tabs } from 'antd';
3 | import './Classroom.less';
4 |
5 | import NavBar from '../../../components/NavBar/NavBar';
6 | import Roster from './Roster/Roster';
7 | import Home from './Home/Home';
8 | import SavedWorkSpaceTab from '../../../components/Tabs/SavedWorkspaceTab';
9 | import { useSearchParams, useParams } from 'react-router-dom';
10 |
11 | const { TabPane } = Tabs;
12 |
13 | export default function Classroom({
14 | handleLogout,
15 | selectedActivity,
16 | setSelectedActivity,
17 | }) {
18 | const [searchParams, setSearchParams] = useSearchParams();
19 |
20 | const { id } = useParams();
21 | const tab = searchParams.get('tab');
22 | const viewing = searchParams.get('viewing');
23 |
24 | useEffect(() => {
25 | sessionStorage.setItem('classroomId', id);
26 |
27 | }, [id]);
28 |
29 | return (
30 |
31 |
32 | setSearchParams({ tab: key })}
35 | >
36 |
37 |
43 |
44 |
45 |
46 |
47 |
48 |
53 |
54 |
55 |
56 | );
57 | }
58 |
--------------------------------------------------------------------------------
/server/api/blocks-category/models/blocks-category.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | /**
4 | * Lifecycle callbacks for the `blocks-category` model.
5 | */
6 |
7 | module.exports = {
8 | // Before saving a value.
9 | // Fired before an `insert` or `update` query.
10 | // beforeSave: async (model, attrs, options) => {},
11 |
12 | // After saving a value.
13 | // Fired after an `insert` or `update` query.
14 | // afterSave: async (model, response, options) => {},
15 |
16 | // Before fetching a value.
17 | // Fired before a `fetch` operation.
18 | // beforeFetch: async (model, columns, options) => {},
19 |
20 | // After fetching a value.
21 | // Fired after a `fetch` operation.
22 | // afterFetch: async (model, response, options) => {},
23 |
24 | // Before fetching all values.
25 | // Fired before a `fetchAll` operation.
26 | // beforeFetchAll: async (model, columns, options) => {},
27 |
28 | // After fetching all values.
29 | // Fired after a `fetchAll` operation.
30 | // afterFetchAll: async (model, response, options) => {},
31 |
32 | // Before creating a value.
33 | // Fired before an `insert` query.
34 | // beforeCreate: async (model, attrs, options) => {},
35 |
36 | // After creating a value.
37 | // Fired after an `insert` query.
38 | // afterCreate: async (model, attrs, options) => {},
39 |
40 | // Before updating a value.
41 | // Fired before an `update` query.
42 | // beforeUpdate: async (model, attrs, options) => {},
43 |
44 | // After updating a value.
45 | // Fired after an `update` query.
46 | // afterUpdate: async (model, attrs, options) => {},
47 |
48 | // Before destroying a value.
49 | // Fired before a `delete` query.
50 | // beforeDestroy: async (model, attrs, options) => {},
51 |
52 | // After destroying a value.
53 | // Fired after a `delete` query.
54 | // afterDestroy: async (model, attrs, options) => {}
55 | };
56 |
--------------------------------------------------------------------------------
/server/api/day/config/routes.json:
--------------------------------------------------------------------------------
1 | {
2 | "routes": [
3 | {
4 | "method": "GET",
5 | "path": "/days",
6 | "handler": "day.find",
7 | "config": {
8 | "policies": []
9 | }
10 | },
11 | {
12 | "method": "GET",
13 | "path": "/days/count",
14 | "handler": "day.count",
15 | "config": {
16 | "policies": []
17 | }
18 | },
19 | {
20 | "method": "GET",
21 | "path": "/days/:id",
22 | "handler": "day.findOne",
23 | "config": {
24 | "policies": []
25 | }
26 | },
27 | {
28 | "method": "GET",
29 | "path": "/days/toolbox/:id",
30 | "handler": "day.toolbox",
31 | "config": {
32 | "policies": []
33 | }
34 | },
35 | {
36 | "method": "PUT",
37 | "path": "/days/arduino/:id",
38 | "handler": "day.arduinoUpdate",
39 | "config": {
40 | "policies": []
41 | }
42 | },
43 | {
44 | "method": "POST",
45 | "path": "/days",
46 | "handler": "day.create",
47 | "config": {
48 | "policies": []
49 | }
50 | },
51 | {
52 | "method": "PUT",
53 | "path": "/days/template/:id",
54 | "handler": "day.templateUpdate",
55 | "config": {
56 | "policies": []
57 | }
58 | },
59 | {
60 | "method": "PUT",
61 | "path": "/days/activity_template/:id",
62 | "handler": "day.activityTemplateUpdate",
63 | "config": {
64 | "policies": []
65 | }
66 | },
67 | {
68 | "method": "PUT",
69 | "path": "/days/:id",
70 | "handler": "day.update",
71 | "config": {
72 | "policies": []
73 | }
74 | },
75 | {
76 | "method": "DELETE",
77 | "path": "/days/:id",
78 | "handler": "day.delete",
79 | "config": {
80 | "policies": []
81 | }
82 | }
83 | ]
84 | }
85 |
--------------------------------------------------------------------------------
/server/api/unit/controllers/unit.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | /**
4 | * Read the documentation (https://strapi.io/documentation/v3.x/concepts/controllers.html#core-controllers)
5 | * to customize this controller
6 | */
7 |
8 | module.exports = {
9 | async update(ctx) {
10 | const { id } = ctx.params;
11 |
12 | // ensure request was not sent as formdata
13 | if (ctx.is('multipart'))
14 | return ctx.badRequest('Multipart requests are not accepted!', {
15 | id: 'Unit.update.format.invalid',
16 | error: 'ValidationError',
17 | });
18 |
19 | // ensure the request has the right number of params
20 | const params = Object.keys(ctx.request.body).length;
21 | if (params !== 5)
22 | return ctx.badRequest('Invalid number of params!', {
23 | id: 'Unit.update.body.invalid',
24 | error: 'ValidationError',
25 | });
26 |
27 | // validate the request
28 | const {
29 | grade: gradeId,
30 | name,
31 | number,
32 | teks_id,
33 | teks_description,
34 | } = ctx.request.body;
35 | if (
36 | !strapi.services.validator.isPositiveInt(number) ||
37 | !strapi.services.validator.isPositiveInt(gradeId) ||
38 | // !teks_id ||
39 | !name
40 | // !teks_description
41 | )
42 | return ctx.badRequest(
43 | 'A grade, name, teks_description must be provided! Number and Teks_id must be positive interger! ',
44 | { id: 'Unit.update.body.invalid', error: 'ValidationError' }
45 | );
46 |
47 | // ensure the grade is valid
48 | const grade = await strapi.services.grade.findOne({ id: gradeId });
49 | if (!grade)
50 | return ctx.notFound('The grade provided is invalid!', {
51 | id: 'Unit.update.grade.invalid',
52 | error: 'ValidationError',
53 | });
54 |
55 | return await strapi.services.unit.update({ id }, ctx.request.body);
56 | },
57 | };
58 |
--------------------------------------------------------------------------------
/test/functional/contentcreatorMocks.test.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @jest-environment node
3 | */
4 | 'use strict'
5 |
6 | import { getPublicRequestModule, getAuthorizedRequestModule, getMentorLoginData } from '../functional/request'
7 |
8 | const publicRequest = getPublicRequestModule()
9 | var adminRequest
10 | var contentcreatorRequest
11 |
12 | //
13 | // Setup before running tests
14 | //
15 |
16 | beforeAll(async () => {
17 | // login as an admin
18 | const { data: admin } = await publicRequest.post('/admin/login', {
19 | email: 'test@mail.com',
20 | password: '123456'
21 | })
22 |
23 | // create an admin request instance
24 | adminRequest = getAuthorizedRequestModule(admin.data.token)
25 | })
26 |
27 | //Tests
28 | test('content creator can login', async () => {
29 | const response = await publicRequest.post('/auth/local', {
30 | identifier: 'defaultcontentcreator',
31 | password: '123456'
32 | })
33 |
34 | expect(response.status).toBe(200)
35 | expect(response.data).toHaveProperty('jwt')
36 | expect(response.data).toHaveProperty('user')
37 |
38 | contentcreatorRequest = getAuthorizedRequestModule(response.data.jwt)
39 | //console.log("Content Creator Request", response.data.jwt)
40 | })
41 |
42 |
43 | test('content creator dashboard contains all grades for dropdown of add unit', async () => {//need work on formatting
44 | const response = await contentcreatorRequest.get('/grades');
45 |
46 | expect(response).toMatchObject({
47 | data:
48 | [
49 | {
50 | "id": 1,
51 | "name": "3rd",
52 | },
53 | {
54 | "id": 3,
55 | "name": "4th",
56 | },
57 | {
58 | "id": 4,
59 | "name": "5th",
60 | },
61 | ]
62 | })
63 |
64 | })
65 |
66 |
67 |
68 |
69 |
--------------------------------------------------------------------------------
/client/src/views/ContentCreator/LearningStandardCreator/LearningStandardCreator.less:
--------------------------------------------------------------------------------
1 | @import '../../../assets/style.less';
2 |
3 | #class-card {
4 | text-align: left;
5 | width: 35vw;
6 | height: 100%;
7 | margin: 2vw auto 0 auto;
8 | background: #colors[tertiary];
9 | color: #colors[text-primary];
10 | border: solid 4px;
11 | border-color: #colors[secondary];
12 | border-radius: 10px;
13 | }
14 |
15 | #add-learning-standard-btn {
16 | position: relative;
17 | top: -2vh;
18 | width: auto;
19 | max-height: 9vh;
20 | padding: 5px 10px;
21 | font-size: 1em;
22 | font-weight: 500;
23 | border-radius: 30px;
24 | margin: auto;
25 | background: #colors[quaternary];
26 | border: none;
27 | color: #colors[text-primary];
28 | transition: 0.25s;
29 | cursor: pointer;
30 | box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.377);
31 |
32 | &:hover {
33 | background: #colors[quinary];
34 | }
35 | }
36 |
37 | #add-learning-standard {
38 | input,
39 | textarea {
40 | display: block;
41 | margin: 5px auto 0px;
42 | background: none;
43 | border: 1px solid;
44 | border-color: #colors[secondary];
45 | padding: 10px 10px;
46 | width: 95%;
47 | outline: none;
48 | color: #colors[text-primary];
49 | border-radius: 4px;
50 | transition: 0.25s;
51 | }
52 |
53 | input[type='text']:focus {
54 | // width: 100%;
55 | }
56 |
57 | label {
58 | margin: 10px auto 12px;
59 | white-space: pre-wrap;
60 | }
61 | }
62 |
63 | #unit-name-dropdown {
64 | display: block;
65 | background: none;
66 | margin: 5px auto 0px;
67 | border: 1px solid;
68 | border-color: #colors[secondary];
69 | padding: 14px 10px;
70 | width: 95%;
71 | outline: none;
72 | color: #colors[text-primary];
73 | border-radius: 4px;
74 | transition: 0.25s;
75 | }
76 |
77 | #unit:focus {
78 | width: 100%;
79 | }
80 |
81 | #link-Input {
82 | white-space: pre-wrap;
83 | margin-bottom: 20px;
84 | }
85 |
--------------------------------------------------------------------------------
/server/api/student/config/routes.json:
--------------------------------------------------------------------------------
1 | {
2 | "routes": [
3 | {
4 | "method": "GET",
5 | "path": "/students",
6 | "handler": "student.find",
7 | "config": {
8 | "policies": []
9 | }
10 | },
11 | {
12 | "method": "GET",
13 | "path": "/students/me",
14 | "handler": "student.me",
15 | "config": {
16 | "policies": ["global::isStudent"]
17 | }
18 | },
19 | {
20 | "method": "GET",
21 | "path": "/students/count",
22 | "handler": "student.count",
23 | "config": {
24 | "policies": []
25 | }
26 | },
27 | {
28 | "method": "GET",
29 | "path": "/students/:id",
30 | "handler": "student.findOne",
31 | "config": {
32 | "policies": []
33 | }
34 | },
35 | {
36 | "method": "POST",
37 | "path": "/students",
38 | "handler": "student.create",
39 | "config": {
40 | "policies": ["global::isClassroomManager", "global::hasClassroom"]
41 | }
42 | },
43 | {
44 | "method": "PUT",
45 | "path": "/students/:id",
46 | "handler": "student.update",
47 | "config": {
48 | "policies": ["global::isClassroomManager", "global::hasStudentsClassroom"]
49 | }
50 | },
51 | {
52 | "method": "PUT",
53 | "path": "/students/:id/addvideo",
54 | "handler": "student.addVideo",
55 | "config": {
56 | "policies" : []
57 | }
58 | },
59 | {
60 | "method": "DELETE",
61 | "path": "/students/:id",
62 | "handler": "student.delete",
63 | "config": {
64 | "policies": ["global::isClassroomManager", "global::hasStudentsClassroom"]
65 | }
66 | },
67 | {
68 | "method": "PUT",
69 | "path": "/students/enrolled/:id",
70 | "handler": "student.enrolled",
71 | "config": {
72 | "policies": ["global::isClassroomManager", "global::hasStudentsClassroom"]
73 | }
74 | }
75 | ]
76 | }
77 |
--------------------------------------------------------------------------------
/client/src/views/Student/Student.less:
--------------------------------------------------------------------------------
1 | @import "../../assets/style";
2 |
3 | #activity-container {
4 | position: absolute;
5 | top: 53vh;
6 | left: 10vw;
7 | margin: -40vh auto 0 auto;
8 | height: 85vh;
9 | width: 80vw;
10 | background-color: #colors[tertiary];
11 | border-radius: 15px;
12 | border: 4px solid;
13 | border-color: #colors[secondary];
14 | }
15 |
16 | #header {
17 | background-color: #colors[secondary];
18 | border-radius: 80px;
19 | width: 40%;
20 | min-height: 4vh;
21 | color: #colors[text-secondary];
22 | font-size: 2.25vh;
23 | font-weight: bold;
24 | position: relative;
25 | top: -20px;
26 | left: -30px;
27 | line-height: 45px;
28 | text-align: center;
29 | }
30 |
31 | #list-item-wrapper {
32 | display: flex;
33 | justify-content: center;
34 | align-items: center;
35 | background-color: #colors[quaternary];
36 | border: none;
37 | border-radius: 10px;
38 | margin: 0 auto 1.2em auto;
39 | width: 80%;
40 | height: 40px;
41 | font-size: 2.5vh;
42 | font-weight: 500;
43 | color: #colors[text-primary];
44 | line-height: 30px;
45 | transition: 0.25s;
46 | &:hover {
47 | cursor: pointer;
48 | background-color: #colors[quinary];
49 | width: 82%;
50 | }
51 | }
52 |
53 | ul {
54 | margin: 0;
55 | padding: 0;
56 | }
57 |
58 | li {
59 | list-style-type: none;
60 | margin: 0;
61 | padding: 0;
62 | }
63 |
64 | #launcher {
65 | position: fixed;
66 | bottom: 3vh;
67 | right: 11vw;
68 | color: #colors[primary];
69 | background-color: darken(#colors[tertiary], 5%);
70 | font-weight: 550;
71 | width: 10vw;
72 | font-size: 2.75vh;
73 | border: solid 3px;
74 | border-radius: 10px;
75 | border-color: #colors[primary];
76 | i {
77 | margin-top: 5px;
78 | font-size: 8vh;
79 | }
80 | transition: 0.25s;
81 | &:hover {
82 | cursor: pointer;
83 | color: #colors[secondary];
84 | border-color: #colors[secondary];
85 | i {
86 | color: #colors[secondary];
87 | }
88 | }
89 | }
--------------------------------------------------------------------------------
/server/api/block/services/block.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const sortCategory = (toolbox) => {
4 | const order = [
5 | 'Logic',
6 | 'Control',
7 | 'Math',
8 | 'Text',
9 | 'Variables',
10 | 'Functions',
11 | 'IO',
12 | 'Time',
13 | 'Audio',
14 | 'Motors',
15 | 'Comms',
16 | 'DHT',
17 | ];
18 |
19 | const sorted = toolbox.sort(function (a, b) {
20 | return order.indexOf(a[0]) - order.indexOf(b[0]);
21 | });
22 | return sorted;
23 | };
24 |
25 | // get all the blocks for a day
26 | module.exports.findByDay = async (id) => {
27 | // get the target day
28 | const day = await strapi.services.day.findOne({ id }, [
29 | 'blocks.blocks_category',
30 | ]);
31 |
32 | // return the blocks only if the day is found
33 | return day ? day.blocks : undefined;
34 | };
35 |
36 | // get all the blocks for a cc workspace
37 | module.exports.findByWorkspace = async (id) => {
38 | // get the target day
39 | const workspace = await strapi.services['cc-workspace'].findOne({ id }, [
40 | 'blocks.blocks_category',
41 | ]);
42 |
43 | // return the blocks only if the day is found
44 | return workspace ? workspace.blocks : undefined;
45 | };
46 |
47 | // create a blockly friendly toolbox from blocks
48 | module.exports.blocksToToolbox = (blocks) => {
49 | let toolbox = {};
50 | blocks.forEach((block) => {
51 | // validate the block fields
52 | const { blocks_category, name, description, image_url } = block;
53 | if (!blocks_category) return;
54 |
55 | // only take the required fields from the block
56 | let sanitizedBlock = { name, description, image_url };
57 |
58 | // append the block to an existing category
59 | // else create a new category
60 | if (toolbox[blocks_category.name]) {
61 | toolbox[blocks_category.name].push(sanitizedBlock);
62 | } else {
63 | toolbox[blocks_category.name] = [sanitizedBlock];
64 | }
65 | });
66 | const arr = Object.entries(toolbox);
67 |
68 | return sortCategory(arr);
69 | };
70 |
--------------------------------------------------------------------------------