├── .github
├── ISSUE_TEMPLATE
│ └── feature_request.md
└── PULL_REQUEST_TEMPLATE.md
├── .gitignore
├── .prettierrc
├── README.md
├── package-lock.json
├── package.json
├── public
├── android-chrome-192x192.png
├── android-chrome-512x512.png
├── favicon.ico
├── index.html
├── manifest.json
└── robots.txt
└── src
├── App.js
├── assets
├── fonts
│ ├── Calibre
│ │ ├── Calibre-Light.ttf
│ │ ├── Calibre-Light.woff
│ │ ├── Calibre-Light.woff2
│ │ ├── Calibre-LightItalic.ttf
│ │ ├── Calibre-LightItalic.woff
│ │ ├── Calibre-LightItalic.woff2
│ │ ├── Calibre-Medium.ttf
│ │ ├── Calibre-Medium.woff
│ │ ├── Calibre-Medium.woff2
│ │ ├── Calibre-MediumItalic.ttf
│ │ ├── Calibre-MediumItalic.woff
│ │ ├── Calibre-MediumItalic.woff2
│ │ ├── Calibre-Regular.ttf
│ │ ├── Calibre-Regular.woff
│ │ ├── Calibre-Regular.woff2
│ │ ├── Calibre-RegularItalic.ttf
│ │ ├── Calibre-RegularItalic.woff
│ │ ├── Calibre-RegularItalic.woff2
│ │ ├── Calibre-Semibold.ttf
│ │ ├── Calibre-Semibold.woff
│ │ ├── Calibre-Semibold.woff2
│ │ ├── Calibre-SemiboldItalic.ttf
│ │ ├── Calibre-SemiboldItalic.woff
│ │ └── Calibre-SemiboldItalic.woff2
│ ├── Imperial
│ │ ├── Imperial Bold Italic.ttf
│ │ ├── Imperial Bold.ttf
│ │ └── Imperial.ttf
│ └── SFMono
│ │ ├── SFMono-Medium.ttf
│ │ ├── SFMono-Medium.woff
│ │ ├── SFMono-Medium.woff2
│ │ ├── SFMono-MediumItalic.ttf
│ │ ├── SFMono-MediumItalic.woff
│ │ ├── SFMono-MediumItalic.woff2
│ │ ├── SFMono-Regular.ttf
│ │ ├── SFMono-Regular.woff
│ │ ├── SFMono-Regular.woff2
│ │ ├── SFMono-RegularItalic.ttf
│ │ ├── SFMono-RegularItalic.woff
│ │ ├── SFMono-RegularItalic.woff2
│ │ ├── SFMono-Semibold.ttf
│ │ ├── SFMono-Semibold.woff
│ │ ├── SFMono-Semibold.woff2
│ │ ├── SFMono-SemiboldItalic.ttf
│ │ ├── SFMono-SemiboldItalic.woff
│ │ └── SFMono-SemiboldItalic.woff2
├── icons
│ ├── add_new 1.png
│ ├── admin-mentoring.svg
│ ├── autograder-loading.svg
│ ├── beaker.svg
│ ├── cards.svg
│ ├── check.js
│ ├── checkpoint.svg
│ ├── cli.svg
│ ├── document.svg
│ ├── exclamation.js
│ ├── flag.svg
│ ├── github.js
│ ├── lock.js
│ ├── locked-hint.svg
│ ├── prof-pic.png
│ ├── resume-pie.svg
│ ├── split-cards.svg
│ ├── start-flag.svg
│ ├── tests.zip
│ ├── threedots.js
│ ├── unused
│ │ ├── atom.js
│ │ ├── brickwall.js
│ │ └── circle.js
│ └── upload.svg
└── logo
│ ├── README.md
│ ├── android-chrome-192x192.png
│ ├── android-chrome-512x512.png
│ ├── apple-touch-icon.png
│ ├── browserconfig.xml
│ ├── favicon-16x16.png
│ ├── favicon-32x32.png
│ ├── favicon.ico
│ ├── html_code.html
│ ├── logo.png
│ ├── logo.svg
│ ├── mstile-150x150.png
│ ├── safari-pinned-tab.svg
│ └── site.webmanifest
├── components
├── Error
│ └── 404NotFound.js
├── Explore
│ ├── Content.js
│ ├── Explore.js
│ ├── FeaturedDisplay.js
│ ├── Sidebar.js
│ └── TopicDisplay.js
├── HOC
│ ├── WithApiCache.js
│ ├── WithAuthentication.js
│ ├── WithChakraTheme.js
│ ├── WithErrorBoundaries.js
│ ├── WithGlobalHOC.js
│ ├── WithNavBar.js
│ ├── WithPageSpinner.js
│ ├── WithStyledTheme.js
│ └── WithUserData.js
├── Home.js
├── Learn
│ ├── Checkpoint
│ │ ├── Autograder
│ │ │ ├── CLI.js
│ │ │ ├── Home.js
│ │ │ └── Result.js
│ │ ├── Checkpoint.js
│ │ ├── Loading.js
│ │ ├── Media
│ │ │ └── Home.js
│ │ ├── MultipleChoice
│ │ │ ├── Home.js
│ │ │ └── McCard.js
│ │ ├── ShortAnswer
│ │ │ └── Home.js
│ │ ├── Upload.js
│ │ └── unused
│ │ │ └── Upload.js
│ ├── Concept
│ │ ├── Concept.js
│ │ └── Slide.js
│ ├── Content
│ │ ├── Content.js
│ │ └── ContentHeader.js
│ ├── Hint
│ │ ├── LockedHint.js
│ │ ├── LockedHintSection.js
│ │ ├── UnlockedHint.js
│ │ └── UnlockedHintSection.js
│ ├── Learn.js
│ ├── NextButton
│ │ ├── Central
│ │ │ └── Central.js
│ │ ├── NextButton.js
│ │ ├── Peripheral.js
│ │ └── SelectState
│ │ │ ├── CentralAnimes.js
│ │ │ ├── CentralContent.js
│ │ │ └── CentralStyles.js
│ ├── Sidebar
│ │ ├── CardHints.js
│ │ ├── Sidebar.js
│ │ ├── SidebarHeader.js
│ │ └── SidebarNav.js
│ ├── Toolbar
│ │ ├── Settings.js
│ │ └── Toolbar.js
│ └── unused
│ │ ├── Checkpoint
│ │ ├── CheckpointModal.js
│ │ ├── Result.js
│ │ └── Upload.js
│ │ ├── Concept
│ │ ├── Concept.js
│ │ └── ConceptModal.js
│ │ ├── Content
│ │ └── Content.js
│ │ ├── Hint
│ │ ├── Hint.js
│ │ ├── HintModal.js
│ │ └── HintSection.js
│ │ ├── Navigation
│ │ ├── NavDropdown.js
│ │ └── Navigation.js
│ │ └── OldLearn.js
├── NavBar
│ └── NavBar.js
├── Student
│ ├── Activity
│ │ └── Activity.js
│ ├── Dashboard
│ │ ├── ChooseProject.js
│ │ ├── Dashboard.js
│ │ ├── DashboardHero.js
│ │ ├── Journey
│ │ │ ├── Floating.js
│ │ │ ├── Journey.js
│ │ │ ├── Progress.js
│ │ │ ├── ProgressItem.js
│ │ │ └── unused
│ │ │ │ ├── ActivityCard.js
│ │ │ │ ├── Details.js
│ │ │ │ └── Journey.js
│ │ ├── PickCard.js
│ │ ├── Suggested.js
│ │ └── unused
│ │ │ └── ProgressCard.js
│ ├── Module
│ │ ├── ActivityList.js
│ │ ├── ActivityModal.js
│ │ ├── Module.js
│ │ └── ProjectModal.js
│ ├── Profile
│ │ └── Profile.js
│ ├── Student.js
│ ├── World
│ │ └── World.js
│ └── unused
│ │ ├── CurrentTopics.js
│ │ ├── CurrentTrack.js
│ │ ├── GreetingSection.js
│ │ ├── LabVerticalCard.js
│ │ ├── MenuBar.js
│ │ ├── OldFinalCopy.js
│ │ ├── SelectTopic.js
│ │ └── TopicInfo.js
├── Teacher
│ ├── Content
│ │ ├── Content.js
│ │ └── SubmissionCheckpoint.js
│ ├── Details
│ │ ├── Details.js
│ │ └── DetailsHeader.js
│ ├── Sidebar
│ │ ├── NavItem.js
│ │ ├── Sidebar.js
│ │ ├── SidebarHeader.js
│ │ └── SidebarNav.js
│ ├── Teacher.js
│ ├── Toolbar
│ │ └── Toolbar.js
│ └── unused
│ │ ├── Grade
│ │ ├── EntryBox.js
│ │ └── Submission.js
│ │ ├── Teacher.js
│ │ ├── TeacherContent.js
│ │ └── TeacherHero.js
├── Visitor
│ └── Visitor.js
└── shared
│ ├── MarkdownContent.js
│ ├── containers
│ ├── ActiveList.js
│ ├── DynamicModal.js
│ ├── PostModal.js
│ ├── Scrollable.js
│ └── TwoPanel.js
│ ├── high
│ ├── ActionCard.js
│ ├── AvatarGroup.js
│ ├── GoBack.js
│ ├── GradeStatus.js
│ ├── HelpCard.js
│ ├── IconWithProgress.js
│ ├── MarkdownArea.js
│ ├── MuiIconFormatter.js
│ └── QuickAction.js
│ ├── low
│ ├── Button.js
│ ├── ConfirmCancel.js
│ ├── DotIndicator.js
│ ├── DotRating.js
│ ├── HeaderShadow.js
│ ├── Hero.js
│ ├── Icon.js
│ ├── IconArea.js
│ ├── IconLine.js
│ ├── ImgAndContent.js
│ ├── Interactive.js
│ ├── MediaLightbox.js
│ ├── ProfPic.js
│ ├── ProgressBar.js
│ ├── ProgressCircle.js
│ ├── StatusIcon.js
│ └── ThreeCheckbox.js
│ ├── unused
│ ├── Button.js
│ └── Card.js
│ └── utils
│ ├── ClampedDiv.js
│ └── ClampedText.js
├── index.js
├── redux
├── actionTypes.js
├── actions
│ ├── account.js
│ ├── cache.js
│ ├── learnData.js
│ ├── studentData.js
│ ├── teacherData.js
│ └── theme.js
├── reducers
│ ├── account.js
│ ├── cache.js
│ ├── learnData.js
│ ├── studentData.js
│ ├── teacherData.js
│ └── theme.js
├── rootReducer.js
└── store.js
├── services
├── AccountService.js
├── Auth0Service.js
├── AxiosInstances.js
├── ContentService.js
├── ExploreService.js
├── LearnService.js
├── PusherService.js
├── StudentService.js
├── TeacherService.js
└── TestService.js
├── styles
├── GlobalAnime.js
├── GlobalStyles.js
├── media.js
└── theme.js
└── utils
├── DataStructures.js
├── customHooks.js
└── objUtils.js
/.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 | **Is your feature request related to a problem? Please describe.**
11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
12 |
13 | **Describe the solution you'd like**
14 | A clear and concise description of what you want to happen.
15 |
16 | **Figma Design**
17 | Visualization of what needs to get created
18 |
19 | **Component Details**
20 |
--------------------------------------------------------------------------------
/.github/PULL_REQUEST_TEMPLATE.md:
--------------------------------------------------------------------------------
1 | # What did you implement/accomplish in this pull request
2 | A clear and concise description of what you did and the changes that you are merging.
3 |
4 | # What are the issues that you completed
5 | Link the issues that you completed/solved:
6 | - #ISSUE_NUMBER (eg: #17)
7 |
8 | # Close Issues(Only fill this out if merging to master branch)
9 | You can use the keyword 'close' and the issue number to close an issue automatically when the pull request is merged
10 | - close #ISSUE_NUMBER (ex: "close #17")
11 |
12 | # Checklist
13 | - [ ] Add staging tag to issues if the pull request is being merged into new-ui
14 | - [ ] Remove staging tag from issues if pull request is being merged in to master
15 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # production
12 | /build
13 |
14 | # misc
15 | .DS_Store
16 | .env
17 | .env.local
18 | .env.development.local
19 | .env.test.local
20 | .env.production.local
21 |
22 | npm-debug.log*
23 | yarn-debug.log*
24 | yarn-error.log*
25 |
26 | /.VSCodeCounter
27 |
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "printWidth": 80,
3 | "singleQuote": true,
4 | "semi": false,
5 | "tabWidth": 2,
6 | "useTabs": true,
7 | "trailingComma": "none",
8 | "arrowParens": "avoid"
9 | }
10 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "bit-project-react",
3 | "version": "0.1.0",
4 | "private": true,
5 | "engines": {
6 | "npm": "6.13.7",
7 | "node": "12.x"
8 | },
9 | "dependencies": {
10 | "@auth0/auth0-spa-js": "^1.6.5",
11 | "@chakra-ui/core": "^0.7.0",
12 | "@emotion/core": "^10.0.28",
13 | "@emotion/styled": "^10.0.27",
14 | "@material-ui/core": "^4.8.0",
15 | "@material-ui/icons": "^4.5.1",
16 | "@material-ui/lab": "^4.0.0-alpha.49",
17 | "@testing-library/jest-dom": "^4.2.4",
18 | "@testing-library/react": "^9.3.2",
19 | "@testing-library/user-event": "^7.1.2",
20 | "animejs": "^3.1.0",
21 | "auth0-js": "^9.12.2",
22 | "axios": "^0.19.0",
23 | "camelcase-keys-deep": "^0.1.0",
24 | "contentful": "^7.11.3",
25 | "dotenv": "^8.2.0",
26 | "emotion-theming": "^10.0.27",
27 | "filepond": "^4.9.3",
28 | "filepond-plugin-image-exif-orientation": "^1.0.6",
29 | "filepond-plugin-image-preview": "^4.6.0",
30 | "jwt-decode": "^2.2.0",
31 | "lodash": "^4.17.15",
32 | "p-cancelable": "^2.0.0",
33 | "pusher-js": "^5.1.1",
34 | "react": "^16.12.0",
35 | "react-aspect-ratio": "^1.0.42",
36 | "react-avatar": "^3.9.2",
37 | "react-dom": "^16.12.0",
38 | "react-dropzone": "^10.2.1",
39 | "react-expand-animated": "^1.0.1",
40 | "react-filepond": "^7.0.1",
41 | "react-images": "^1.1.0-beta.3",
42 | "react-markdown": "^4.2.2",
43 | "react-portal": "^4.2.1",
44 | "react-pusher": "^0.2.0",
45 | "react-quill": "^1.3.3",
46 | "react-redux": "^7.1.3",
47 | "react-responsive-carousel": "^3.1.51",
48 | "react-router-dom": "^5.1.2",
49 | "react-scripts": "^3.4.1",
50 | "react-scroll": "^1.7.16",
51 | "react-spinkit": "^3.0.0",
52 | "react-syntax-highlighter": "^12.2.1",
53 | "react-transition-group": "^4.3.0",
54 | "redux": "^4.0.4",
55 | "redux-thunk": "^2.3.0",
56 | "rich-text-to-react": "^1.1.0",
57 | "snakecase-keys": "^3.1.2",
58 | "styled-components": "^4.4.1",
59 | "turndown": "^5.0.3",
60 | "use-callback-ref": "^1.2.1",
61 | "uuid": "^7.0.3",
62 | "validator": "^13.0.0"
63 | },
64 | "scripts": {
65 | "start": "react-scripts start",
66 | "build": "react-scripts build",
67 | "test": "react-scripts test",
68 | "eject": "react-scripts eject"
69 | },
70 | "eslintConfig": {
71 | "extends": "react-app"
72 | },
73 | "browserslist": {
74 | "production": [
75 | ">0.2%",
76 | "not dead",
77 | "not op_mini all"
78 | ],
79 | "development": [
80 | "last 1 chrome version",
81 | "last 1 firefox version",
82 | "last 1 safari version"
83 | ]
84 | },
85 | "devDependencies": {
86 | "eslint": "^6.8.0",
87 | "redux-immutable-state-invariant": "^2.1.0"
88 | }
89 | }
90 |
--------------------------------------------------------------------------------
/public/android-chrome-192x192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bitprj/bit-frontend/d7450ca1e6c1c2c9b6d77cf806c8e92c81ff8956/public/android-chrome-192x192.png
--------------------------------------------------------------------------------
/public/android-chrome-512x512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bitprj/bit-frontend/d7450ca1e6c1c2c9b6d77cf806c8e92c81ff8956/public/android-chrome-512x512.png
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bitprj/bit-frontend/d7450ca1e6c1c2c9b6d77cf806c8e92c81ff8956/public/favicon.ico
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 | Bit Project
13 |
14 |
15 |
16 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "Bit Project",
3 | "name": "Bit Project",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "64x64 32x32 24x24 16x16",
8 | "type": "image/x-icon"
9 | },
10 | {
11 | "src": "android-chrome-192x192.png",
12 | "type": "image/png",
13 | "sizes": "192x192"
14 | },
15 | {
16 | "src": "android-chrome-512x512.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 |
--------------------------------------------------------------------------------
/public/robots.txt:
--------------------------------------------------------------------------------
1 | # https://www.robotstxt.org/robotstxt.html
2 | User-agent: *
3 |
--------------------------------------------------------------------------------
/src/App.js:
--------------------------------------------------------------------------------
1 | import React, { useEffect } from 'react'
2 | import { Route, Switch, BrowserRouter } from 'react-router-dom'
3 |
4 | import Home from './components/Home'
5 |
6 | import Student from './components/Student/Student'
7 | import Teacher from './components/Teacher/Teacher'
8 |
9 | import Learn from './components/Learn/Learn'
10 | import Explore from './components/Explore/Explore'
11 | import Module from './components/Student/Module/Module'
12 | import Activity from './components/Student/Activity/Activity'
13 | import World from './components/Student/World/World'
14 |
15 | import NotFound from './components/Error/404NotFound'
16 |
17 | import WithGlobalHOC from './components/HOC/WithGlobalHOC'
18 |
19 | import { ping } from './services/TestService'
20 |
21 | const App = () => {
22 | useEffect(() => {
23 | ping()
24 | }, [])
25 |
26 | return (
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 | )
43 | }
44 |
45 | export default App
46 |
--------------------------------------------------------------------------------
/src/assets/fonts/Calibre/Calibre-Light.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bitprj/bit-frontend/d7450ca1e6c1c2c9b6d77cf806c8e92c81ff8956/src/assets/fonts/Calibre/Calibre-Light.ttf
--------------------------------------------------------------------------------
/src/assets/fonts/Calibre/Calibre-Light.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bitprj/bit-frontend/d7450ca1e6c1c2c9b6d77cf806c8e92c81ff8956/src/assets/fonts/Calibre/Calibre-Light.woff
--------------------------------------------------------------------------------
/src/assets/fonts/Calibre/Calibre-Light.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bitprj/bit-frontend/d7450ca1e6c1c2c9b6d77cf806c8e92c81ff8956/src/assets/fonts/Calibre/Calibre-Light.woff2
--------------------------------------------------------------------------------
/src/assets/fonts/Calibre/Calibre-LightItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bitprj/bit-frontend/d7450ca1e6c1c2c9b6d77cf806c8e92c81ff8956/src/assets/fonts/Calibre/Calibre-LightItalic.ttf
--------------------------------------------------------------------------------
/src/assets/fonts/Calibre/Calibre-LightItalic.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bitprj/bit-frontend/d7450ca1e6c1c2c9b6d77cf806c8e92c81ff8956/src/assets/fonts/Calibre/Calibre-LightItalic.woff
--------------------------------------------------------------------------------
/src/assets/fonts/Calibre/Calibre-LightItalic.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bitprj/bit-frontend/d7450ca1e6c1c2c9b6d77cf806c8e92c81ff8956/src/assets/fonts/Calibre/Calibre-LightItalic.woff2
--------------------------------------------------------------------------------
/src/assets/fonts/Calibre/Calibre-Medium.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bitprj/bit-frontend/d7450ca1e6c1c2c9b6d77cf806c8e92c81ff8956/src/assets/fonts/Calibre/Calibre-Medium.ttf
--------------------------------------------------------------------------------
/src/assets/fonts/Calibre/Calibre-Medium.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bitprj/bit-frontend/d7450ca1e6c1c2c9b6d77cf806c8e92c81ff8956/src/assets/fonts/Calibre/Calibre-Medium.woff
--------------------------------------------------------------------------------
/src/assets/fonts/Calibre/Calibre-Medium.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bitprj/bit-frontend/d7450ca1e6c1c2c9b6d77cf806c8e92c81ff8956/src/assets/fonts/Calibre/Calibre-Medium.woff2
--------------------------------------------------------------------------------
/src/assets/fonts/Calibre/Calibre-MediumItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bitprj/bit-frontend/d7450ca1e6c1c2c9b6d77cf806c8e92c81ff8956/src/assets/fonts/Calibre/Calibre-MediumItalic.ttf
--------------------------------------------------------------------------------
/src/assets/fonts/Calibre/Calibre-MediumItalic.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bitprj/bit-frontend/d7450ca1e6c1c2c9b6d77cf806c8e92c81ff8956/src/assets/fonts/Calibre/Calibre-MediumItalic.woff
--------------------------------------------------------------------------------
/src/assets/fonts/Calibre/Calibre-MediumItalic.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bitprj/bit-frontend/d7450ca1e6c1c2c9b6d77cf806c8e92c81ff8956/src/assets/fonts/Calibre/Calibre-MediumItalic.woff2
--------------------------------------------------------------------------------
/src/assets/fonts/Calibre/Calibre-Regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bitprj/bit-frontend/d7450ca1e6c1c2c9b6d77cf806c8e92c81ff8956/src/assets/fonts/Calibre/Calibre-Regular.ttf
--------------------------------------------------------------------------------
/src/assets/fonts/Calibre/Calibre-Regular.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bitprj/bit-frontend/d7450ca1e6c1c2c9b6d77cf806c8e92c81ff8956/src/assets/fonts/Calibre/Calibre-Regular.woff
--------------------------------------------------------------------------------
/src/assets/fonts/Calibre/Calibre-Regular.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bitprj/bit-frontend/d7450ca1e6c1c2c9b6d77cf806c8e92c81ff8956/src/assets/fonts/Calibre/Calibre-Regular.woff2
--------------------------------------------------------------------------------
/src/assets/fonts/Calibre/Calibre-RegularItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bitprj/bit-frontend/d7450ca1e6c1c2c9b6d77cf806c8e92c81ff8956/src/assets/fonts/Calibre/Calibre-RegularItalic.ttf
--------------------------------------------------------------------------------
/src/assets/fonts/Calibre/Calibre-RegularItalic.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bitprj/bit-frontend/d7450ca1e6c1c2c9b6d77cf806c8e92c81ff8956/src/assets/fonts/Calibre/Calibre-RegularItalic.woff
--------------------------------------------------------------------------------
/src/assets/fonts/Calibre/Calibre-RegularItalic.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bitprj/bit-frontend/d7450ca1e6c1c2c9b6d77cf806c8e92c81ff8956/src/assets/fonts/Calibre/Calibre-RegularItalic.woff2
--------------------------------------------------------------------------------
/src/assets/fonts/Calibre/Calibre-Semibold.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bitprj/bit-frontend/d7450ca1e6c1c2c9b6d77cf806c8e92c81ff8956/src/assets/fonts/Calibre/Calibre-Semibold.ttf
--------------------------------------------------------------------------------
/src/assets/fonts/Calibre/Calibre-Semibold.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bitprj/bit-frontend/d7450ca1e6c1c2c9b6d77cf806c8e92c81ff8956/src/assets/fonts/Calibre/Calibre-Semibold.woff
--------------------------------------------------------------------------------
/src/assets/fonts/Calibre/Calibre-Semibold.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bitprj/bit-frontend/d7450ca1e6c1c2c9b6d77cf806c8e92c81ff8956/src/assets/fonts/Calibre/Calibre-Semibold.woff2
--------------------------------------------------------------------------------
/src/assets/fonts/Calibre/Calibre-SemiboldItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bitprj/bit-frontend/d7450ca1e6c1c2c9b6d77cf806c8e92c81ff8956/src/assets/fonts/Calibre/Calibre-SemiboldItalic.ttf
--------------------------------------------------------------------------------
/src/assets/fonts/Calibre/Calibre-SemiboldItalic.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bitprj/bit-frontend/d7450ca1e6c1c2c9b6d77cf806c8e92c81ff8956/src/assets/fonts/Calibre/Calibre-SemiboldItalic.woff
--------------------------------------------------------------------------------
/src/assets/fonts/Calibre/Calibre-SemiboldItalic.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bitprj/bit-frontend/d7450ca1e6c1c2c9b6d77cf806c8e92c81ff8956/src/assets/fonts/Calibre/Calibre-SemiboldItalic.woff2
--------------------------------------------------------------------------------
/src/assets/fonts/Imperial/Imperial Bold Italic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bitprj/bit-frontend/d7450ca1e6c1c2c9b6d77cf806c8e92c81ff8956/src/assets/fonts/Imperial/Imperial Bold Italic.ttf
--------------------------------------------------------------------------------
/src/assets/fonts/Imperial/Imperial Bold.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bitprj/bit-frontend/d7450ca1e6c1c2c9b6d77cf806c8e92c81ff8956/src/assets/fonts/Imperial/Imperial Bold.ttf
--------------------------------------------------------------------------------
/src/assets/fonts/Imperial/Imperial.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bitprj/bit-frontend/d7450ca1e6c1c2c9b6d77cf806c8e92c81ff8956/src/assets/fonts/Imperial/Imperial.ttf
--------------------------------------------------------------------------------
/src/assets/fonts/SFMono/SFMono-Medium.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bitprj/bit-frontend/d7450ca1e6c1c2c9b6d77cf806c8e92c81ff8956/src/assets/fonts/SFMono/SFMono-Medium.ttf
--------------------------------------------------------------------------------
/src/assets/fonts/SFMono/SFMono-Medium.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bitprj/bit-frontend/d7450ca1e6c1c2c9b6d77cf806c8e92c81ff8956/src/assets/fonts/SFMono/SFMono-Medium.woff
--------------------------------------------------------------------------------
/src/assets/fonts/SFMono/SFMono-Medium.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bitprj/bit-frontend/d7450ca1e6c1c2c9b6d77cf806c8e92c81ff8956/src/assets/fonts/SFMono/SFMono-Medium.woff2
--------------------------------------------------------------------------------
/src/assets/fonts/SFMono/SFMono-MediumItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bitprj/bit-frontend/d7450ca1e6c1c2c9b6d77cf806c8e92c81ff8956/src/assets/fonts/SFMono/SFMono-MediumItalic.ttf
--------------------------------------------------------------------------------
/src/assets/fonts/SFMono/SFMono-MediumItalic.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bitprj/bit-frontend/d7450ca1e6c1c2c9b6d77cf806c8e92c81ff8956/src/assets/fonts/SFMono/SFMono-MediumItalic.woff
--------------------------------------------------------------------------------
/src/assets/fonts/SFMono/SFMono-MediumItalic.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bitprj/bit-frontend/d7450ca1e6c1c2c9b6d77cf806c8e92c81ff8956/src/assets/fonts/SFMono/SFMono-MediumItalic.woff2
--------------------------------------------------------------------------------
/src/assets/fonts/SFMono/SFMono-Regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bitprj/bit-frontend/d7450ca1e6c1c2c9b6d77cf806c8e92c81ff8956/src/assets/fonts/SFMono/SFMono-Regular.ttf
--------------------------------------------------------------------------------
/src/assets/fonts/SFMono/SFMono-Regular.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bitprj/bit-frontend/d7450ca1e6c1c2c9b6d77cf806c8e92c81ff8956/src/assets/fonts/SFMono/SFMono-Regular.woff
--------------------------------------------------------------------------------
/src/assets/fonts/SFMono/SFMono-Regular.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bitprj/bit-frontend/d7450ca1e6c1c2c9b6d77cf806c8e92c81ff8956/src/assets/fonts/SFMono/SFMono-Regular.woff2
--------------------------------------------------------------------------------
/src/assets/fonts/SFMono/SFMono-RegularItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bitprj/bit-frontend/d7450ca1e6c1c2c9b6d77cf806c8e92c81ff8956/src/assets/fonts/SFMono/SFMono-RegularItalic.ttf
--------------------------------------------------------------------------------
/src/assets/fonts/SFMono/SFMono-RegularItalic.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bitprj/bit-frontend/d7450ca1e6c1c2c9b6d77cf806c8e92c81ff8956/src/assets/fonts/SFMono/SFMono-RegularItalic.woff
--------------------------------------------------------------------------------
/src/assets/fonts/SFMono/SFMono-RegularItalic.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bitprj/bit-frontend/d7450ca1e6c1c2c9b6d77cf806c8e92c81ff8956/src/assets/fonts/SFMono/SFMono-RegularItalic.woff2
--------------------------------------------------------------------------------
/src/assets/fonts/SFMono/SFMono-Semibold.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bitprj/bit-frontend/d7450ca1e6c1c2c9b6d77cf806c8e92c81ff8956/src/assets/fonts/SFMono/SFMono-Semibold.ttf
--------------------------------------------------------------------------------
/src/assets/fonts/SFMono/SFMono-Semibold.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bitprj/bit-frontend/d7450ca1e6c1c2c9b6d77cf806c8e92c81ff8956/src/assets/fonts/SFMono/SFMono-Semibold.woff
--------------------------------------------------------------------------------
/src/assets/fonts/SFMono/SFMono-Semibold.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bitprj/bit-frontend/d7450ca1e6c1c2c9b6d77cf806c8e92c81ff8956/src/assets/fonts/SFMono/SFMono-Semibold.woff2
--------------------------------------------------------------------------------
/src/assets/fonts/SFMono/SFMono-SemiboldItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bitprj/bit-frontend/d7450ca1e6c1c2c9b6d77cf806c8e92c81ff8956/src/assets/fonts/SFMono/SFMono-SemiboldItalic.ttf
--------------------------------------------------------------------------------
/src/assets/fonts/SFMono/SFMono-SemiboldItalic.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bitprj/bit-frontend/d7450ca1e6c1c2c9b6d77cf806c8e92c81ff8956/src/assets/fonts/SFMono/SFMono-SemiboldItalic.woff
--------------------------------------------------------------------------------
/src/assets/fonts/SFMono/SFMono-SemiboldItalic.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bitprj/bit-frontend/d7450ca1e6c1c2c9b6d77cf806c8e92c81ff8956/src/assets/fonts/SFMono/SFMono-SemiboldItalic.woff2
--------------------------------------------------------------------------------
/src/assets/icons/add_new 1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bitprj/bit-frontend/d7450ca1e6c1c2c9b6d77cf806c8e92c81ff8956/src/assets/icons/add_new 1.png
--------------------------------------------------------------------------------
/src/assets/icons/beaker.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
31 |
--------------------------------------------------------------------------------
/src/assets/icons/cards.svg:
--------------------------------------------------------------------------------
1 |
29 |
--------------------------------------------------------------------------------
/src/assets/icons/check.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | export default ({ color, check_width, check_height, icon_style }) => {
4 | const container_style = {
5 | minWidth: check_width,
6 | marginTop: '-.7rem'
7 | }
8 |
9 | return (
10 |
11 |
19 |
20 | )
21 | };
22 |
--------------------------------------------------------------------------------
/src/assets/icons/cli.svg:
--------------------------------------------------------------------------------
1 |
20 |
--------------------------------------------------------------------------------
/src/assets/icons/exclamation.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | const exclamation = () => (
4 |
15 | )
16 |
17 | export default exclamation
18 |
--------------------------------------------------------------------------------
/src/assets/icons/flag.svg:
--------------------------------------------------------------------------------
1 |
6 |
--------------------------------------------------------------------------------
/src/assets/icons/github.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | export default ({ color, width, height }) => (
4 |
15 | );
16 |
--------------------------------------------------------------------------------
/src/assets/icons/lock.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | export default ({ color, width, height }) => (
4 | // More advanced padlock but relies on background color for padlock
5 |
23 |
24 | // Simple black-outline padlock
25 | //
40 | );
41 |
--------------------------------------------------------------------------------
/src/assets/icons/prof-pic.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bitprj/bit-frontend/d7450ca1e6c1c2c9b6d77cf806c8e92c81ff8956/src/assets/icons/prof-pic.png
--------------------------------------------------------------------------------
/src/assets/icons/resume-pie.svg:
--------------------------------------------------------------------------------
1 |
5 |
--------------------------------------------------------------------------------
/src/assets/icons/split-cards.svg:
--------------------------------------------------------------------------------
1 |
29 |
--------------------------------------------------------------------------------
/src/assets/icons/tests.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bitprj/bit-frontend/d7450ca1e6c1c2c9b6d77cf806c8e92c81ff8956/src/assets/icons/tests.zip
--------------------------------------------------------------------------------
/src/assets/icons/threedots.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import styled from 'styled-components';
3 |
4 | const ThreeDots = styled.div`
5 |
6 | `
7 |
8 | export default ({ color, width, height }) => (
9 |
10 |
18 |
19 | );
20 |
--------------------------------------------------------------------------------
/src/assets/icons/unused/brickwall.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | export default ({ color, width, height }) => (
4 |
16 | );
17 |
--------------------------------------------------------------------------------
/src/assets/icons/unused/circle.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | export default ({ color, width, height }) => (
4 |
7 | );
8 |
--------------------------------------------------------------------------------
/src/assets/icons/upload.svg:
--------------------------------------------------------------------------------
1 |
6 |
--------------------------------------------------------------------------------
/src/assets/logo/README.md:
--------------------------------------------------------------------------------
1 | # Your Favicon Package
2 |
3 | This package was generated with [RealFaviconGenerator](https://realfavicongenerator.net/) [v0.16](https://realfavicongenerator.net/change_log#v0.16)
4 |
5 | ## Install instructions
6 |
7 | To install this package:
8 |
9 | Extract this package in the root of your web site. If your site is http://www.example.com
, you should be able to access a file named http://www.example.com/favicon.ico
.
10 |
11 | Insert the following code in the `head` section of your pages:
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 | *Optional* - Check your favicon with the [favicon checker](https://realfavicongenerator.net/favicon_checker)
--------------------------------------------------------------------------------
/src/assets/logo/android-chrome-192x192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bitprj/bit-frontend/d7450ca1e6c1c2c9b6d77cf806c8e92c81ff8956/src/assets/logo/android-chrome-192x192.png
--------------------------------------------------------------------------------
/src/assets/logo/android-chrome-512x512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bitprj/bit-frontend/d7450ca1e6c1c2c9b6d77cf806c8e92c81ff8956/src/assets/logo/android-chrome-512x512.png
--------------------------------------------------------------------------------
/src/assets/logo/apple-touch-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bitprj/bit-frontend/d7450ca1e6c1c2c9b6d77cf806c8e92c81ff8956/src/assets/logo/apple-touch-icon.png
--------------------------------------------------------------------------------
/src/assets/logo/browserconfig.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | #9acfff
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/src/assets/logo/favicon-16x16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bitprj/bit-frontend/d7450ca1e6c1c2c9b6d77cf806c8e92c81ff8956/src/assets/logo/favicon-16x16.png
--------------------------------------------------------------------------------
/src/assets/logo/favicon-32x32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bitprj/bit-frontend/d7450ca1e6c1c2c9b6d77cf806c8e92c81ff8956/src/assets/logo/favicon-32x32.png
--------------------------------------------------------------------------------
/src/assets/logo/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bitprj/bit-frontend/d7450ca1e6c1c2c9b6d77cf806c8e92c81ff8956/src/assets/logo/favicon.ico
--------------------------------------------------------------------------------
/src/assets/logo/html_code.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/src/assets/logo/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bitprj/bit-frontend/d7450ca1e6c1c2c9b6d77cf806c8e92c81ff8956/src/assets/logo/logo.png
--------------------------------------------------------------------------------
/src/assets/logo/mstile-150x150.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bitprj/bit-frontend/d7450ca1e6c1c2c9b6d77cf806c8e92c81ff8956/src/assets/logo/mstile-150x150.png
--------------------------------------------------------------------------------
/src/assets/logo/safari-pinned-tab.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/assets/logo/site.webmanifest:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Bit Project",
3 | "short_name": "Bit Project",
4 | "icons": [
5 | {
6 | "src": "/android-chrome-192x192.png",
7 | "sizes": "192x192",
8 | "type": "image/png"
9 | },
10 | {
11 | "src": "/android-chrome-512x512.png",
12 | "sizes": "512x512",
13 | "type": "image/png"
14 | }
15 | ],
16 | "theme_color": "#9acfff",
17 | "background_color": "#9acfff",
18 | "display": "standalone"
19 | }
20 |
--------------------------------------------------------------------------------
/src/components/Error/404NotFound.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import styled from 'styled-components'
3 |
4 | const Container = styled.div`
5 | padding: 2em;
6 | text-align: center;
7 | `
8 |
9 | const NotFound = () => (
10 |
11 | You appear to be on a missing page. Oh well!
12 |
13 | (ノ◕ヮ◕)ノ︵ ✧・゚┻━┻*:・゚✧
14 |
15 | )
16 |
17 | export default NotFound
18 |
--------------------------------------------------------------------------------
/src/components/Explore/Content.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import styled from 'styled-components'
3 |
4 | import FeaturedDisplay from './FeaturedDisplay'
5 | import TopicDisplay from './TopicDisplay'
6 |
7 | const Container = styled.div`
8 | padding-top: 2em;
9 | width: calc(100% - 18em);
10 | `
11 |
12 | const HorzScroll = styled.div`
13 | padding: 2em;
14 | padding-right: 2em;
15 | display: flex;
16 | flex-wrap: nowrap;
17 |
18 | > div {
19 | flex-shrink: 0;
20 | margin-right: 2em;
21 | }
22 |
23 | > div:last-child::before {
24 | content: '';
25 | margin-right: 3em;
26 | display: block;
27 | // width: 40em;
28 | height: 0.1px;
29 | }
30 | `
31 |
32 | const Content = props => {
33 | return (
34 |
35 | {props.activeName}
36 |
37 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 | )
52 | }
53 |
54 | export default Content
55 |
--------------------------------------------------------------------------------
/src/components/Explore/Explore.js:
--------------------------------------------------------------------------------
1 | import React, { useState } from 'react'
2 | import styled from 'styled-components'
3 |
4 | import Sidebar from './Sidebar'
5 | import Content from './Content'
6 |
7 | import { sizes } from '../../styles/media'
8 |
9 | const Container = styled.div`
10 | display: flex;
11 | overflow-x: hidden;
12 | font-size: 80%;
13 |
14 | @media screen and (orientation: portrait) and (max-width: ${sizes.bigDesktop}px) {
15 | font-size: 100%;
16 | }
17 | `
18 |
19 | const views = [
20 | {
21 | // name: "👋 For You",
22 | name: 'For You',
23 | topics: [{}, {}]
24 | },
25 | {
26 | // name: "✏ Design",
27 | name: 'Design',
28 | topics: [{}, {}]
29 | },
30 | {
31 | // name: "📈 Statistics",
32 | name: 'Statistics',
33 | topics: [{}, {}]
34 | },
35 | {
36 | // name: "🐍 Python",
37 | name: 'Python',
38 | topics: [{}, {}]
39 | },
40 | {
41 | // name: "💻 Web Development ",
42 | name: 'Web Development ',
43 | topics: [{}, {}]
44 | },
45 | {
46 | // name: "⚛️ Logic and Math",
47 | name: 'Logic and Math',
48 | topics: [{}, {}]
49 | },
50 | {
51 | // name: "📠 Machine Learning",
52 | name: 'Machine Learning',
53 | topics: [{}, {}]
54 | }
55 | ]
56 |
57 | const Explore = () => {
58 | const [activeName, setActiveName] = useState(views[0].name)
59 |
60 | return (
61 |
62 |
67 |
68 |
69 | )
70 | }
71 |
72 | export default Explore
73 |
--------------------------------------------------------------------------------
/src/components/Explore/FeaturedDisplay.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import styled from "styled-components";
3 |
4 | const Featured = styled.div`
5 | padding: 4em 2.5em;
6 | padding-right: 16em;
7 | height: 20em;
8 | width: 40em;
9 | position: relative;
10 | cursor: pointer;
11 |
12 | border-radius: 0.8em;
13 | background: ${props => props.theme.accentVariant} url(${props => props.url});
14 | ${props => (props.bgColor ? `background-color: ${props.bgColor}` : "")};
15 | background-size: 100% auto;
16 | background-position: bottom right;
17 | `;
18 |
19 | const Category = styled.div`
20 | position: absolute;
21 | bottom: 2em;
22 | left: 2.5em;
23 | font-weight: bold;
24 | `;
25 |
26 | const FeaturedDisplay = props => {
27 | return (
28 |
33 | {props.name}
34 | {props.category}
35 |
36 | );
37 | };
38 |
39 | export default FeaturedDisplay;
40 |
--------------------------------------------------------------------------------
/src/components/Explore/Sidebar.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import styled from "styled-components";
3 |
4 | const Container = styled.div`
5 | padding: 2em 3em;
6 | padding-right: 0;
7 | width: 18em;
8 | `;
9 |
10 | const MenuItem = styled.div`
11 | margin: 0.8em 0;
12 | padding: 0.3em 0.8em;
13 | padding-right: 1em;
14 | border-radius: 50em;
15 | width: fit-content;
16 |
17 | cursor: pointer;
18 | transition: ease 0.25s all;
19 |
20 | &:hover {
21 | background-color: #0002;
22 | }
23 |
24 | &.active {
25 | color: #fff;
26 | background-color: #000;
27 | cursor: default;
28 | }
29 | `;
30 |
31 | const Sidebar = props => {
32 | const handleClick = name => {
33 | props.setActiveName(name);
34 | console.log(name)
35 | };
36 |
37 | const sidebarItems = props.views.map((view, index) => {
38 | const className = props.activeName === view.name ? "active" : "";
39 | return (
40 |
47 | );
48 | });
49 |
50 | return (
51 |
52 | Explore
53 | {sidebarItems}
54 |
55 | );
56 | };
57 |
58 | export default Sidebar;
59 |
--------------------------------------------------------------------------------
/src/components/Explore/TopicDisplay.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import styled from 'styled-components'
3 |
4 | import AccessTimeIcon from '@material-ui/icons/AccessTime'
5 | import IconLine from '../shared/low/IconLine'
6 | import Icon from '../shared/low/Icon'
7 |
8 | const Container = styled.div``
9 |
10 | const RenderedActivity = styled.div`
11 | padding: 1.5em 1.5em;
12 | margin: 0.5em 0;
13 | display: flex;
14 | align-items: center;
15 | cursor: pointer;
16 | `
17 |
18 | const AppIcon = styled(Icon)`
19 | margin-right: 1.6em;
20 | border-radius: 1.4em;
21 | `
22 |
23 | const Activity = props => {
24 | return (
25 |
26 |
27 |
28 |
{props.name}
29 | }>{props.time}
30 |
31 |
32 | )
33 | }
34 |
35 | const TopicDisplay = props => {
36 | return (
37 |
38 | {props.name}
39 |
40 |
41 |
42 |
43 |
44 | )
45 | }
46 |
47 | export default TopicDisplay
48 |
--------------------------------------------------------------------------------
/src/components/HOC/WithAuthentication.js:
--------------------------------------------------------------------------------
1 | import { useEffect } from 'react'
2 | import { connect } from 'react-redux'
3 |
4 | import { fetchMetaData } from '../../services/AccountService'
5 | import { authenticate, deauthenticate } from '../../redux/actions/account'
6 |
7 | const WithAuthentication = ({
8 | children,
9 | isVisitor,
10 | onAuthenticate,
11 | onDeauthenticate
12 | }) => {
13 | useEffect(() => {
14 | const storedMeta = localStorage.getItem('meta')
15 | if (storedMeta) return
16 | ;(async () => {
17 | try {
18 | const meta = await fetchMetaData()
19 | onAuthenticate(meta)
20 | } catch (error) {
21 | if (!isVisitor) onDeauthenticate()
22 | console.log('[WithAuthentication] deauthenticated')
23 | // history.push('/') axios instance does it
24 | }
25 | })()
26 | }, [])
27 |
28 | return children
29 | }
30 |
31 | const mapStateToProps = state => ({
32 | isVisitor: !state.account.meta
33 | })
34 |
35 | const mapDispatchToProps = dispatch => ({
36 | onAuthenticate: meta => dispatch(authenticate(meta)),
37 | onDeauthenticate: () => dispatch(deauthenticate())
38 | })
39 |
40 | export default connect(mapStateToProps, mapDispatchToProps)(WithAuthentication)
41 |
--------------------------------------------------------------------------------
/src/components/HOC/WithChakraTheme.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { ThemeProvider, CSSReset, theme } from '@chakra-ui/core'
3 |
4 | export const chakraTheme = {
5 | accent: '#007bed',
6 | accentVariant: '#9acfff', //86c5ff
7 | mild: '#00498c',
8 | bg: '#0a192f',
9 | bgVariant: '#172A45',
10 | bgPage: '#f5faff'
11 | }
12 |
13 | export const customTheme = {
14 | ...theme,
15 | fonts: {
16 | body: 'Open Sans',
17 | heading: 'Poppins',
18 | monospace: 'Source Code Pro'
19 | },
20 | colors: {
21 | ...theme.colors,
22 | theme: chakraTheme
23 | }
24 | }
25 |
26 | const WithChakraTheme = ({ children }) => {
27 | return (
28 |
29 | {/* */}
30 | {children}
31 |
32 | )
33 | }
34 |
35 | export default WithChakraTheme
36 |
--------------------------------------------------------------------------------
/src/components/HOC/WithErrorBoundaries.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 |
3 | // use like so:
4 | // const [, setError] = useState(null)
5 | //
6 | // setError(() => { // trigger rerender and throw error from trycatch
7 | // throw error
8 | // })
9 | class WithErrorBoundaries extends Component {
10 | constructor(props) {
11 | super(props)
12 | this.state = { error: null, errorInfo: null }
13 | }
14 |
15 | static getDerivedStateFromError(error) {
16 | // Update state so the next render will show the fallback UI.
17 | return { error: true, errorInfo: error }
18 | }
19 |
20 | componentDidCatch(error, errorInfo) {
21 | console.log(error)
22 | // Catch errors in any components below and re-render with error message
23 | // this.setState({ error, errorInfo })
24 | // You can also log error messages to an error reporting service here
25 | }
26 |
27 | render() {
28 | if (this.state.error) {
29 | console.log(this.state.error.response)
30 | // Error path
31 | return (
32 | <>
33 | Something went wrong.
34 | {/*
35 | {this.state.error && this.state.error.toString()}
36 |
37 | {this.state.errorInfo.componentStack}
38 | */}
39 | >
40 | )
41 | }
42 |
43 | return this.props.children
44 | }
45 | }
46 |
47 | export default WithErrorBoundaries
48 |
--------------------------------------------------------------------------------
/src/components/HOC/WithGlobalHOC.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | import WithStyledTheme from './WithStyledTheme'
4 | import WithChakraTheme from './WithChakraTheme'
5 | import WithErrorBoundaries from './WithErrorBoundaries'
6 | import WithAuthentication from './WithAuthentication'
7 | import WithUserData from './WithUserData'
8 | import WithNavBar from './WithNavBar'
9 |
10 | const WithGlobalHOC = ({ children }) => (
11 |
12 |
13 |
14 |
15 |
16 | {children}
17 |
18 |
19 |
20 |
21 |
22 | )
23 |
24 | export default WithGlobalHOC
25 |
--------------------------------------------------------------------------------
/src/components/HOC/WithNavBar.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { Switch, Route } from 'react-router-dom'
3 | import styled from 'styled-components'
4 |
5 | import NavBar from '../NavBar/NavBar'
6 |
7 | const Main = styled.main`
8 | background-color: #f9fafa;
9 | position: relative;
10 | `
11 |
12 | const WithNavBar = props => (
13 | <>
14 |
15 |
16 |
17 |
18 |
19 | {props.children}
20 | >
21 | )
22 |
23 | export default WithNavBar
24 |
--------------------------------------------------------------------------------
/src/components/HOC/WithPageSpinner.js:
--------------------------------------------------------------------------------
1 | import React, { useEffect, useContext } from 'react'
2 | import styled, { ThemeContext } from 'styled-components'
3 | import anime from 'animejs'
4 | import Transition from 'react-transition-group/Transition'
5 | import Spinkit from 'react-spinkit'
6 |
7 | import { fadeIn, statusFadeOut } from '../../styles/GlobalAnime'
8 |
9 | const SpinnerWrapper = styled.div`
10 | display: flex;
11 | justify-content: center;
12 | align-items: center;
13 |
14 | position: fixed;
15 | left: 0;
16 | right: 0;
17 | top: 0;
18 | bottom: 0;
19 | z-index: 999;
20 | opacity: 0;
21 |
22 | background-color: ${props => props.theme.bgPage}33;
23 | pointer-events: none;
24 |
25 | div {
26 | width: 10em;
27 | height: 10em;
28 | }
29 | `
30 |
31 | const WithPageSpinner = ({ children, show }) => {
32 | const themeContext = useContext(ThemeContext)
33 |
34 | useEffect(() => {
35 | fadeIn('.learn-i-spin')
36 | }, [])
37 |
38 | return (
39 | <>
40 |
41 | {status => {
42 | statusFadeOut(status, '.learn-i-spin', 1000)
43 | return (
44 |
45 |
51 |
52 | )
53 | }}
54 |
55 | {children}
56 | >
57 | )
58 | }
59 |
60 | export default WithPageSpinner
61 |
--------------------------------------------------------------------------------
/src/components/HOC/WithStyledTheme.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { connect } from 'react-redux'
3 | import { ThemeProvider } from 'styled-components'
4 | import { GlobalStyle, GlobalStyleReset } from '../../styles/GlobalStyles'
5 |
6 | const WithStyledTheme = props => {
7 | return (
8 |
9 |
10 |
11 | {props.children}
12 |
13 | )
14 | }
15 |
16 | const mapStateToProps = state => {
17 | return { theme: state.theme }
18 | }
19 |
20 | export default connect(mapStateToProps)(WithStyledTheme)
21 |
--------------------------------------------------------------------------------
/src/components/HOC/WithUserData.js:
--------------------------------------------------------------------------------
1 | import { useEffect } from 'react'
2 | import { connect } from 'react-redux'
3 | import { initUserData } from '../../redux/actions/account'
4 | import { init as initStudentData } from '../../redux/actions/studentData'
5 |
6 | const WithUserData = ({
7 | children,
8 | userId,
9 | studentId,
10 | onInitUserData,
11 | onInitStudentData
12 | }) => {
13 | useEffect(() => {
14 | if (userId) onInitUserData(userId)
15 | if (studentId) onInitStudentData(studentId)
16 | }, [userId, studentId])
17 |
18 | return children
19 | }
20 |
21 | const mapStateToProps = state => {
22 | const { userId, studentId } = state.account.meta ?? {}
23 | return {
24 | userId,
25 | studentId
26 | }
27 | }
28 |
29 | const mapDispatchToProps = dispatch => {
30 | return {
31 | onInitUserData: userId => dispatch(initUserData(userId)),
32 | onInitStudentData: studentId => dispatch(initStudentData(studentId))
33 | }
34 | }
35 |
36 | export default connect(mapStateToProps, mapDispatchToProps)(WithUserData)
37 |
--------------------------------------------------------------------------------
/src/components/Home.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { Redirect } from 'react-router-dom'
3 | import { connect } from 'react-redux'
4 |
5 | import Visitor from './Visitor/Visitor'
6 |
7 | const Home = ({ meta }) => {
8 | const selectHome = () => {
9 | if (!meta) {
10 | return
11 | }
12 | if (meta.studentId) {
13 | return
14 | }
15 | if (meta.teacherId) {
16 | return null
17 | }
18 |
19 | console.log("[HOME] we shouldn't be here... missing meta?", meta)
20 | return null
21 | }
22 | return <>{selectHome()}>
23 | }
24 |
25 | const mapStateToProps = state => ({
26 | meta: state.account.meta
27 | })
28 |
29 | export default connect(mapStateToProps)(Home)
30 |
--------------------------------------------------------------------------------
/src/components/Learn/Checkpoint/Autograder/CLI.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import styled from 'styled-components'
3 |
4 | import ImgAndContent from '../../../shared/low/ImgAndContent'
5 |
6 | const cliSvg = require('../../../../assets/icons/cli.svg')
7 |
8 | const Container = styled.div`
9 | height: 100%;
10 | display: flex;
11 | justify-content: center;
12 | align-items: center;
13 | font-size: 110%;
14 | `
15 |
16 | const TitleArea = styled(ImgAndContent)`
17 | padding: 0;
18 | cursor: default;
19 | `
20 |
21 | const CodeArea = styled.pre`
22 | padding: 1em 1.5em;
23 | background-color: #2b2b2b;
24 | color: #fff;
25 | white-space: nowrap;
26 | border-radius: 0.5em;
27 | `
28 |
29 | const CLI = ({
30 | description = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud.',
31 | command = 'bit_autograder input -i redit.py'
32 | }) => {
33 | return (
34 |
35 |
36 |
42 | Upload Code Using Terminal
43 |
44 |
{description}
45 |
46 | {command}
47 |
48 |
49 |
50 | )
51 | }
52 |
53 | export default CLI
54 |
--------------------------------------------------------------------------------
/src/components/Learn/Checkpoint/Loading.js:
--------------------------------------------------------------------------------
1 | import React, { useState, useEffect } from 'react'
2 | import styled from 'styled-components'
3 | import { connect } from 'react-redux'
4 |
5 | import Icon from '../../shared/low/Icon'
6 | import { AUTOGRADER } from './Checkpoint'
7 |
8 | import { resetSubmittedCheckpointSuccessful } from '../../../redux/actions/learnData'
9 |
10 | const autograderLoading = require('../../../assets/icons/autograder-loading.svg')
11 |
12 | const Container = styled.div`
13 | height: 100%;
14 | display: flex;
15 | flex-direction: column;
16 | align-items: center;
17 | justify-content: center;
18 | `
19 |
20 | const StyledIcon = styled(Icon)`
21 | margin-right: -2em;
22 | `
23 |
24 | const Title = styled.h1`
25 | margin: 0;
26 | margin-top: 1em;
27 | ${props => (props.error ? `color: ${props.theme.pastel.red}` : '')}
28 | `
29 |
30 | const Caption = styled.p`
31 | margin: 0;
32 | ${props => (props.error ? `color: ${props.theme.pastel.red}` : '')}
33 | `
34 |
35 | const Loading = ({
36 | type,
37 | pushViewAndRemoveIntermediaries,
38 | previousViewAndRemoveIntermediaries,
39 | submittedCheckpointSuccessful,
40 | onResetSubmittedCheckpointSuccessful
41 | }) => {
42 | const [error, setError] = useState(false)
43 |
44 | useEffect(() => {
45 | if (submittedCheckpointSuccessful !== undefined) {
46 | if (submittedCheckpointSuccessful) {
47 | if (type === 'Autograder') {
48 | pushViewAndRemoveIntermediaries(AUTOGRADER)
49 | } else {
50 | previousViewAndRemoveIntermediaries()
51 | }
52 | onResetSubmittedCheckpointSuccessful()
53 | } else if (!submittedCheckpointSuccessful) {
54 | setError(true)
55 | }
56 | }
57 | }, [submittedCheckpointSuccessful])
58 |
59 | return (
60 |
61 |
62 |
63 | {!error ? 'Give us a sec' : 'An error occurred'}
64 |
65 |
66 | {!error ? 'Swapping space and time' : 'Please try again in a bit'}
67 |
68 |
69 | )
70 | }
71 |
72 | const mapStateToProps = state => {
73 | const {
74 | learnData: {
75 | indicators: { submittedCheckpointSuccessful }
76 | }
77 | } = state
78 |
79 | return { submittedCheckpointSuccessful }
80 | }
81 |
82 | const mapDispatchToProps = dispatch => ({
83 | onResetSubmittedCheckpointSuccessful: () =>
84 | dispatch(resetSubmittedCheckpointSuccessful())
85 | })
86 |
87 | export default connect(mapStateToProps, mapDispatchToProps)(Loading)
88 |
--------------------------------------------------------------------------------
/src/components/Learn/Checkpoint/Media/Home.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import styled from 'styled-components'
3 |
4 | import { Card } from '../Autograder/Home'
5 | import MediaLightbox, {
6 | TYPE_IMAGE,
7 | TYPE_VIDEO
8 | } from '../../../shared/low/MediaLightbox'
9 | import TwoPanel from '../../../shared/containers/TwoPanel'
10 | import Button from '../../../shared/low/Button'
11 |
12 | import { UPLOAD } from '../Checkpoint'
13 |
14 | const splitCardsSvg = require('../../../../assets/icons/split-cards.svg')
15 |
16 | const Instruction = styled.div`
17 | padding-top: 1em;
18 | padding-right: 1em;
19 | `
20 |
21 | const StyledTwoPanel = styled(TwoPanel)`
22 | padding: 0 4em 2em;
23 | padding-right: 2em;
24 | `
25 |
26 | const Resubmit = styled(Button)`
27 | position: absolute;
28 | right: 2em;
29 | bottom: 1em;
30 | `
31 |
32 | const Home = ({
33 | pushView,
34 | type: checkpointType,
35 | instruction = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud.',
36 | content
37 | }) => {
38 | return (
39 | {instruction}}
42 | second={
43 | content ? (
44 |
48 | ) : (
49 | pushView(UPLOAD)}
54 | />
55 | )
56 | }
57 | >
58 | {content && (
59 | pushView(UPLOAD)}>
60 | Resubmit
61 |
62 | )}
63 |
64 | )
65 | }
66 |
67 | export default Home
68 |
--------------------------------------------------------------------------------
/src/components/Learn/Checkpoint/MultipleChoice/McCard.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import styled from 'styled-components'
3 |
4 | const Container = styled.div`
5 | display: flex;
6 | justify-content: center;
7 | align-items: center;
8 | border-radius: 1em;
9 |
10 | cursor: pointer;
11 |
12 | &.active {
13 | background-color: ${props => props.theme.accent};
14 | color: ${props => props.theme.fontInvert};
15 | }
16 |
17 | &.wrong {
18 | background-color: ${props => props.theme.pastel.red};
19 | color: ${props => props.theme.fontInvert};
20 | }
21 |
22 | &.correct {
23 | background-color: ${props => props.theme.pastel.green};
24 | color: ${props => props.theme.fontInvert};
25 | }
26 |
27 | :hover {
28 | background-color: ${props => props.theme.accent};
29 | color: ${props => props.theme.fontInvert};
30 | }
31 | `
32 |
33 | const McCard = ({ className, children, ...props }) => {
34 | return (
35 |
39 | {children}
40 |
41 | )
42 | }
43 |
44 | export default McCard
45 |
--------------------------------------------------------------------------------
/src/components/Learn/Checkpoint/ShortAnswer/Home.js:
--------------------------------------------------------------------------------
1 | import React, { useState } from 'react'
2 | import styled from 'styled-components'
3 | import { connect } from 'react-redux'
4 |
5 | import MarkdownArea from '../../../shared/high/MarkdownArea'
6 | import Button from '../../../shared/low/Button'
7 |
8 | import { initSubmitCheckpointProgress } from '../../../../redux/actions/learnData'
9 |
10 | import { LOADING } from '../Checkpoint'
11 |
12 | const Container = styled.div`
13 | height: calc(100% - 3em);
14 | padding: 0 4em;
15 | display: flex;
16 | flex-direction: column;
17 | `
18 |
19 | const InstructionWrapper = styled.div`
20 | margin-bottom: 2em;
21 | min-height: 6em;
22 | display: flex;
23 | align-items: safe center;
24 | `
25 |
26 | const StyledMarkdownArea = styled(MarkdownArea)`
27 | flex: 1;
28 | `
29 |
30 | const Submit = styled(Button)`
31 | position: absolute;
32 | right: 2em;
33 | bottom: 1em;
34 | `
35 |
36 | const Home = ({
37 | activityId,
38 | id,
39 |
40 | type,
41 | instruction,
42 | content,
43 | pushView,
44 |
45 | onInitSubmitCheckpointProgress
46 | }) => {
47 | const [answer, setAnswer] = useState()
48 |
49 | const handleSubmit = () => {
50 | onInitSubmitCheckpointProgress(activityId, id, type, answer)
51 | pushView(LOADING)
52 | }
53 |
54 | return (
55 |
56 |
57 | {instruction}
58 |
59 | setAnswer(contents)}
63 | />
64 |
65 | Submit
66 |
67 |
68 | )
69 | }
70 |
71 | const mapDispatchToProps = dispatch => ({
72 | onInitSubmitCheckpointProgress: (activityId, id, type, content) =>
73 | dispatch(initSubmitCheckpointProgress(activityId, id, type, content))
74 | })
75 |
76 | export default connect(null, mapDispatchToProps)(Home)
77 |
--------------------------------------------------------------------------------
/src/components/Learn/Concept/Concept.js:
--------------------------------------------------------------------------------
1 | import React, { useState } from 'react'
2 | import styled from 'styled-components'
3 | import { connect } from 'react-redux'
4 |
5 | import { Carousel } from 'react-responsive-carousel'
6 | import 'react-responsive-carousel/lib/styles/carousel.min.css'
7 |
8 | import Slide from './Slide'
9 | import Peripheral from '../NextButton/Peripheral'
10 | import DynamicModal from '../../shared/containers/DynamicModal'
11 |
12 | const StyledCarousel = styled(Carousel)`
13 | width: 100%;
14 |
15 | .carousel,
16 | .slider-wrapper,
17 | .slider,
18 | .slide,
19 | .slide > div {
20 | height: 100%;
21 | }
22 |
23 | .carousel .slide {
24 | text-align: left;
25 | background: transparent;
26 | }
27 |
28 | .carousel.carousel-slider .control-arrow {
29 | visibility: hidden;
30 | }
31 | `
32 |
33 | const Concept = ({
34 | STATE_CONCEPT,
35 |
36 | render,
37 | open,
38 | setOpen,
39 |
40 | conceptMetas,
41 | removeAndBroadcastButtonState
42 | }) => {
43 | /**
44 | * Persistent memory of current concept even if user closes concept modal
45 | * # TODO doesn't work rn because carousel onchange is messed
46 | */
47 | const [slideIndex, setSlideIndex] = useState(0)
48 |
49 | const handleClose = () => {
50 | setOpen(false)
51 | removeAndBroadcastButtonState(STATE_CONCEPT)
52 | }
53 |
54 | const slides = conceptMetas?.map((concept, index) => (
55 |
62 | ))
63 |
64 | return (
65 | <>
66 | {render && (
67 | setOpen(true)}
70 | top="65%"
71 | left="65%"
72 | />
73 | )}
74 |
75 | setSlideIndex(slideIndex)}
81 | width="100%"
82 | >
83 | {slides}
84 |
85 |
86 | >
87 | )
88 | }
89 |
90 | const mapStateToProps = state => {
91 | const {
92 | cache: { cachedActivities, cachedCards },
93 | learnData: {
94 | selectedActivity: { id: activityId },
95 | indicators: { currentCardIndex }
96 | }
97 | } = state
98 |
99 | const cardId = cachedActivities[activityId]?.cards[currentCardIndex]?.id
100 |
101 | const conceptMetas = cachedCards[cardId]?.concepts
102 |
103 | return {
104 | conceptMetas
105 | }
106 | }
107 |
108 | export default connect(mapStateToProps)(Concept)
109 |
--------------------------------------------------------------------------------
/src/components/Learn/Content/ContentHeader.js:
--------------------------------------------------------------------------------
1 | import React, { forwardRef } from 'react'
2 | import styled from 'styled-components'
3 | import { connect } from 'react-redux'
4 | import { compose } from 'redux'
5 |
6 | import HeaderShadow from '../../shared/low/HeaderShadow'
7 | import ImgAndContent from '../../shared/low/ImgAndContent'
8 |
9 | import withApiCache, { CACHE_MODULE } from '../../HOC/WithApiCache'
10 |
11 | const Container = styled.div`
12 | position: fixed;
13 | top: 0;
14 | width: 75%;
15 | z-index: 99;
16 | opacity: 0;
17 | `
18 |
19 | const Header = styled(ImgAndContent)`
20 | margin: 0;
21 | padding: 2em 1em 1.5em;
22 | padding-left: 5em;
23 | cursor: auto;
24 | background-color: #fff;
25 |
26 | transition: 0.2s ease padding;
27 |
28 | &.content-minimized {
29 | padding-top: 1em;
30 | padding-bottom: 1em;
31 | }
32 |
33 | @media only screen and (orientation: vertical) {
34 | padding-left: 2.5em;
35 | }
36 | `
37 |
38 | const mapStateToProps = state => ({
39 | id: state.learnData.selectedActivity.moduleId
40 | })
41 | const enhancer = compose(connect(mapStateToProps), withApiCache([CACHE_MODULE]))
42 |
43 | const ModuleName = enhancer(({ wac_data: [modu1e] }) => (
44 |
45 | {modu1e?.name?.toUpperCase()}
46 |
47 | ))
48 |
49 | const ContentHeader = forwardRef(({ containerRef, name }, ref) => {
50 | return (
51 |
65 | )
66 | })
67 |
68 | export default ContentHeader
69 |
--------------------------------------------------------------------------------
/src/components/Learn/Learn.js:
--------------------------------------------------------------------------------
1 | import React, { useEffect } from 'react'
2 | import styled from 'styled-components'
3 | import { connect } from 'react-redux'
4 | import { compose } from 'redux'
5 |
6 | import Toolbar from './Toolbar/Toolbar'
7 | import Sidebar from './Sidebar/Sidebar'
8 | import Content from './Content/Content'
9 | import WithPageSpinner from '../HOC/WithPageSpinner'
10 |
11 | import withApiCache, {
12 | isDataReady,
13 | CACHE_ACTIVITY,
14 | CACHE_ACTIVITY_PROGRESS
15 | } from '../HOC/WithApiCache'
16 | import { init } from '../../redux/actions/learnData'
17 |
18 | const Container = styled.div`
19 | display: flex;
20 | position: relative;
21 | background: #fafafa;
22 |
23 | > :nth-child(1),
24 | > :nth-child(2),
25 | > :nth-child(3) {
26 | height: 100vh;
27 | }
28 |
29 | > :nth-child(1) {
30 | position: relative;
31 | z-index: 3;
32 | }
33 | > :nth-child(2) {
34 | position: relative;
35 | z-index: 2;
36 | }
37 | > :nth-child(3) {
38 | position: relative;
39 | z-index: 1;
40 | }
41 |
42 | @media only screen and (orientation: landscape) {
43 | font-size: 80%;
44 | }
45 | `
46 |
47 | const Learn = ({ wac_data, id, isReady, onInit }) => {
48 | useEffect(() => {
49 | if (id && isDataReady(wac_data)) {
50 | onInit(...wac_data)
51 | }
52 | }, [id, wac_data]) // eslint-disable-line react-hooks/exhaustive-deps
53 |
54 | return (
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 | )
63 | }
64 |
65 | const mapStateToProps = state => {
66 | const {
67 | cache: { cachedActivities, cachedCards },
68 | learnData: {
69 | selectedActivity: { id }
70 | }
71 | } = state
72 |
73 | const cardId = cachedActivities[id]?.cards[0]?.id
74 | const card = cachedCards[cardId]
75 |
76 | const isReady = !!card?.content
77 | return {
78 | isReady,
79 | id
80 | }
81 | }
82 |
83 | const mapDispatchToProps = dispatch => ({
84 | onInit: (activity, activityProgress) =>
85 | dispatch(init(activity, activityProgress))
86 | // onResetToInitialState: () => dispatch(resetToInitialState())
87 | })
88 |
89 | const enhancer = compose(
90 | connect(mapStateToProps, mapDispatchToProps),
91 | withApiCache([CACHE_ACTIVITY, CACHE_ACTIVITY_PROGRESS])
92 | )
93 |
94 | export default enhancer(Learn)
95 |
--------------------------------------------------------------------------------
/src/components/Learn/NextButton/Peripheral.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import styled from 'styled-components'
3 |
4 | import { CentralTemplate } from './Central/Central'
5 |
6 | const Container = styled(CentralTemplate)`
7 | position: absolute;
8 | top: ${props => props.top};
9 | left: ${props => props.left};
10 | font-size: 50%;
11 | z-index: 1;
12 | `
13 |
14 | const Peripheral = ({ top = 0, left = 0, currentButtonState, onClick }) => {
15 | return (
16 |
22 | )
23 | }
24 |
25 | export default Peripheral
26 |
--------------------------------------------------------------------------------
/src/components/Learn/NextButton/SelectState/CentralAnimes.js:
--------------------------------------------------------------------------------
1 | import anime from 'animejs'
2 | import {
3 | STATE_NEXT,
4 | STATE_FINISH,
5 | STATE_CHECKPOINT,
6 | STATE_CONCEPT,
7 | STATE_HINT
8 | } from '../NextButton'
9 |
10 | export default function CentralAnimes(currentButtonState) {
11 | // reset
12 | const targets = '.learn-r-nextbutton, .learn-r-nextarrow'
13 | anime.remove(targets)
14 | document.querySelectorAll(targets).forEach(target => {
15 | target.classList.remove('transition-none')
16 | target.style.transform = '' // remove transform style
17 | })
18 |
19 | switch (currentButtonState) {
20 | case STATE_HINT: {
21 | document
22 | .querySelectorAll('.learn-r-nextbutton, .learn-r-nextarrow')
23 | .forEach(target => target.classList.add('transition-none'))
24 |
25 | const options = {
26 | duration: 500,
27 | direction: 'alternate',
28 | loop: true
29 | }
30 | // bounce
31 | anime({
32 | targets: '.learn-r-nextbutton',
33 | translateY: '-1em',
34 | translateZ: 0,
35 | easing: 'easeOutQuad',
36 | ...options
37 | })
38 | // rotate
39 | anime({
40 | targets: '.learn-r-nextarrow',
41 | rotate: '-90deg',
42 | easing: 'easeOutQuad',
43 | duration: 400
44 | })
45 | // scale
46 | // anime({
47 | // targets: '.learn-r-nextbutton, .learn-r-nextarrow',
48 | // scale: 1.5,
49 | // duration: 1000,
50 | // easing: 'easeOutQuad'
51 | // })
52 |
53 | break
54 | }
55 |
56 | case STATE_CHECKPOINT: {
57 | break
58 | }
59 |
60 | case STATE_CONCEPT: {
61 | break
62 | }
63 |
64 | case STATE_FINISH: {
65 | break
66 | }
67 |
68 | case STATE_NEXT: {
69 | break
70 | }
71 |
72 | default:
73 | if (currentButtonState !== undefined)
74 | console.log(
75 | '[CentralAnimes] error... missing state check?',
76 | currentButtonState
77 | )
78 | break
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/src/components/Learn/NextButton/SelectState/CentralContent.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import styled from 'styled-components'
3 | import RightArrow from '@material-ui/icons/KeyboardArrowRightRounded'
4 | import Clipboard from '@material-ui/icons/AssignmentRounded'
5 | import Flag from '@material-ui/icons/EmojiFlagsRounded'
6 | import Finish from '@material-ui/icons/DoneRounded'
7 |
8 | import {
9 | STATE_NEXT,
10 | STATE_FINISH,
11 | STATE_CONCEPT,
12 | STATE_CHECKPOINT,
13 | STATE_HINT
14 | } from '../NextButton'
15 |
16 | const Container = styled.div`
17 | display: flex;
18 | justify-content: center;
19 | align-items: center;
20 | color: #fff;
21 | font-size: 200%;
22 | `
23 |
24 | export default function CentralContent({ className, currentButtonState }) {
25 | const centralContent = () => {
26 | switch (currentButtonState) {
27 | case STATE_CHECKPOINT:
28 | return
29 |
30 | case STATE_CONCEPT:
31 | return
32 |
33 | case STATE_HINT:
34 | case STATE_NEXT:
35 | return
36 |
37 | case STATE_FINISH:
38 | return
39 |
40 | default:
41 | if (currentButtonState !== undefined)
42 | console.log('[CentralContent] error... missing state check?')
43 | return null
44 | }
45 | }
46 |
47 | return {centralContent()}
48 | }
49 |
--------------------------------------------------------------------------------
/src/components/Learn/NextButton/SelectState/CentralStyles.js:
--------------------------------------------------------------------------------
1 | import styled from 'styled-components'
2 | import {
3 | STATE_NEXT,
4 | STATE_FINISH,
5 | STATE_CHECKPOINT,
6 | STATE_CONCEPT,
7 | STATE_HINT
8 | } from '../NextButton'
9 |
10 | export const createColorTemplate = color => `
11 | background-color: ${color};
12 | box-shadow: 0 4px 14px 0 ${color}88;
13 | `
14 |
15 | const CentralStyles = styled.div`
16 | ${props => {
17 | switch (props.currentButtonState) {
18 | case STATE_HINT:
19 | return createColorTemplate(props.theme.pastel.magenta)
20 |
21 | case STATE_CONCEPT:
22 | return createColorTemplate(props.theme.pastel.yellow)
23 |
24 | case STATE_CHECKPOINT:
25 | return createColorTemplate(props.theme.pastel.green)
26 |
27 | case STATE_FINISH:
28 | case STATE_NEXT:
29 | return createColorTemplate(props.theme.accent)
30 |
31 | default:
32 | if (props.currentButtonState !== undefined)
33 | console.log('[CentralStyles] error... missing state check?')
34 | return `
35 | background-color: #fff;
36 | box-shadow: 0 4px 14px 0 #00000088;
37 | .learn-r-nextarrow {
38 | color: #aaa;
39 | }
40 | `
41 | }
42 | }}
43 | `
44 |
45 | export default CentralStyles
46 |
--------------------------------------------------------------------------------
/src/components/Learn/Sidebar/Sidebar.js:
--------------------------------------------------------------------------------
1 | import React, { useEffect } from 'react'
2 | import styled from 'styled-components'
3 | import { connect } from 'react-redux'
4 |
5 | import SidebarHeader from './SidebarHeader'
6 | import SidebarNav from './SidebarNav'
7 |
8 | import { slideIn, fadeIn } from '../../../styles/GlobalAnime'
9 |
10 | const Container = styled.div`
11 | flex: 0.36;
12 | background: #fafafa;
13 | display: flex;
14 | flex-direction: column;
15 |
16 | @media screen and (orientation: landscape) {
17 | flex: 0.21;
18 | }
19 | `
20 |
21 | const Sidebar = ({ isReady }) => {
22 | useEffect(() => {
23 | if (isReady) {
24 | fadeIn('.learn-i-sidebar')
25 | slideIn('.learn-i-sidebar')
26 | }
27 | }, [isReady])
28 |
29 | return (
30 |
31 | {isReady && (
32 | <>
33 |
34 |
35 | >
36 | )}
37 |
38 | )
39 | }
40 |
41 | const mapStateToProps = state => {
42 | const {
43 | cache: { cachedActivities, cachedCards },
44 | learnData: {
45 | selectedActivity: { id: activityId }
46 | }
47 | } = state
48 |
49 | const cardId = cachedActivities[activityId]?.cards[0].id
50 | const isReady = cachedCards[cardId]?.id
51 |
52 | return {
53 | isReady
54 | }
55 | }
56 |
57 | export default connect(mapStateToProps)(Sidebar)
58 |
--------------------------------------------------------------------------------
/src/components/Learn/Sidebar/SidebarHeader.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import styled from 'styled-components'
3 | import { connect } from 'react-redux'
4 | import { compose } from 'redux'
5 |
6 | import DotRating from '../../shared/low/DotRating'
7 | import { setCurrentCardByIndex } from '../../../redux/actions/learnData'
8 |
9 | import withApiCache, { CACHE_MODULE } from '../../HOC/WithApiCache'
10 |
11 | const Container = styled.div`
12 | padding: 2em;
13 | `
14 |
15 | const SidebarHeader = ({
16 | wac_data: [modu1e],
17 |
18 | name,
19 | cardsLength,
20 | currentCardIndex,
21 | lastCardUnlockedIndex,
22 | onSetCurrentCardByIndex
23 | }) => {
24 | return (
25 |
26 |
27 | {modu1e?.name?.toUpperCase()}
28 |
29 | {name}
30 | {
38 | if (index <= lastCardUnlockedIndex && index !== currentCardIndex)
39 | onSetCurrentCardByIndex(index)
40 | }}
41 | />
42 |
43 | )
44 | }
45 |
46 | const mapStateToProps = state => {
47 | const {
48 | cache: { cachedActivities },
49 | learnData: {
50 | selectedActivity: { id: activityId, moduleId },
51 | indicators: { currentCardIndex, lastCardUnlockedIndex }
52 | }
53 | } = state
54 |
55 | const activity = cachedActivities[activityId]
56 |
57 | return {
58 | id: moduleId,
59 | name: activity?.name,
60 | cardsLength: activity?.cards.length,
61 | currentCardIndex,
62 | lastCardUnlockedIndex
63 | }
64 | }
65 |
66 | const mapDispatchToProps = dispatch => ({
67 | onSetCurrentCardByIndex: cardIndex =>
68 | dispatch(setCurrentCardByIndex(cardIndex))
69 | })
70 |
71 | const enhancer = compose(
72 | connect(mapStateToProps, mapDispatchToProps),
73 | withApiCache([CACHE_MODULE])
74 | )
75 |
76 | export default enhancer(SidebarHeader)
77 |
--------------------------------------------------------------------------------
/src/components/Learn/Toolbar/Settings.js:
--------------------------------------------------------------------------------
1 | import React, { useState } from 'react'
2 | import styled from 'styled-components'
3 | import { connect } from 'react-redux'
4 |
5 | import SettingsIcon from '@material-ui/icons/Settings'
6 |
7 | import QuickAction from '../../shared/high/QuickAction'
8 | import Button from '../../shared/low/Button'
9 |
10 | import { deleteActivityProgress } from '../../../services/LearnService'
11 |
12 | const Container = styled.div`
13 | width: 100%;
14 | display: flex;
15 | flex-direction: column;
16 | align-items: center;
17 | justify-content: center;
18 | `
19 |
20 | const IconWrapper = styled.div`
21 | font-size: 200%;
22 | color: white;
23 | height: 1em;
24 | line-height: 1em;
25 | `
26 |
27 | const StyledButton = styled(Button)`
28 | text-align: right;
29 | `
30 |
31 | const Settings = ({ activityId }) => {
32 | const action = () =>
33 | deleteActivityProgress(activityId).then(res => {
34 | const success =
35 | !res.response?.status &&
36 | (!res.message?.includes('Error') || !res.msg?.includes('Error'))
37 | if (success) {
38 | window.location.replace('/learn/')
39 | }
40 | })
41 |
42 | return process.env.NODE_ENV === 'development' ? (
43 | Reset your progress for this activity?}
47 | >
48 |
49 |
50 |
51 |
52 | ) : null
53 | }
54 |
55 | const mapStateToProps = state => {
56 | const {
57 | learnData: {
58 | selectedActivity: { id: activityId }
59 | }
60 | } = state
61 | return { activityId }
62 | }
63 |
64 | export default connect(mapStateToProps)(Settings)
65 |
--------------------------------------------------------------------------------
/src/components/Learn/Toolbar/Toolbar.js:
--------------------------------------------------------------------------------
1 | import React, { useEffect } from 'react'
2 | import styled from 'styled-components'
3 | import anime from 'animejs'
4 | import { connect } from 'react-redux'
5 | import { Link } from 'react-router-dom'
6 |
7 | import Settings from './Settings'
8 | import Icon from '../../shared/low/Icon'
9 |
10 | const Container = styled.div`
11 | display: flex;
12 | flex-direction: column;
13 | // justify-content: space-around;
14 | // align-items: center;
15 | flex: 0.08;
16 |
17 | background-color: ${props => props.theme.bg};
18 | text-align: center;
19 |
20 | @media screen and (orientation: landscape) {
21 | flex: 0.04;
22 | }
23 | `
24 |
25 | const TopSection = styled.div``
26 | const BottomSection = styled.div``
27 |
28 | const MiddleSection = styled.div`
29 | flex: 1;
30 | display: flex;
31 | flex-direction: column;
32 | justify-content: center;
33 | `
34 |
35 | const Elem = styled.div`
36 | margin: 1em 0;
37 | padding: 10%;
38 | display: flex;
39 | justify-content: center;
40 | align-items: center;
41 |
42 | &.pointer {
43 | cursor: pointer;
44 | }
45 |
46 | @media screen and (orientation: landscape) {
47 | padding: 12%;
48 | }
49 | `
50 |
51 | const Toolbar = ({ gems }) => {
52 | useEffect(() => {
53 | anime({
54 | targets: '.learn-r-gems',
55 | innerText: gems,
56 | easing: 'easeOutQuad',
57 | round: 1
58 | })
59 | }, [gems])
60 |
61 | return (
62 |
63 |
64 |
65 |
66 |
70 |
71 |
72 |
73 |
74 |
75 | 💎
76 |
80 |
81 |
82 |
83 |
84 |
85 | )
86 | }
87 |
88 | const mapStateToProps = state => {
89 | const {
90 | studentData: { gems }
91 | } = state
92 | return { gems }
93 | }
94 |
95 | export default connect(mapStateToProps)(Toolbar)
96 |
--------------------------------------------------------------------------------
/src/components/Learn/unused/Checkpoint/CheckpointModal.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { makeStyles } from '@material-ui/core/styles';
3 | import Modal from '@material-ui/core/Modal';
4 | import Backdrop from '@material-ui/core/Backdrop';
5 | import Fade from '@material-ui/core/Fade';
6 |
7 | import Upload from './Upload';
8 | import Result from './Result';
9 |
10 | const useStyles = makeStyles(theme => ({
11 | modal: {
12 | display: 'flex',
13 | alignItems: 'center',
14 | justifyContent: 'center',
15 | },
16 | }));
17 |
18 | const CheckpointModal = (props) => {
19 | const classes = useStyles();
20 | const [open, setOpen] = React.useState(false);
21 | const [result, setResult] = React.useState({});
22 | const [currentSlide, setSlide] = React.useState('upload');
23 |
24 | const openModal = () => {
25 | setOpen(true);
26 | };
27 |
28 | const closeModal = () => {
29 | setOpen(false);
30 | };
31 |
32 | const switchToResult = () => {
33 | setSlide('checkpoint');
34 | }
35 |
36 | const switchToUpload = () => {
37 | setSlide('upload');
38 | }
39 |
40 | const fillResult = (result) => {
41 | setResult(result);
42 | }
43 |
44 | return (
45 |
46 |
47 |
48 |
57 |
58 |
59 | {currentSlide === 'upload' ?
60 |
61 | :
62 | }
63 |
64 |
65 |
66 | );
67 | }
68 |
69 | export default CheckpointModal;
70 |
--------------------------------------------------------------------------------
/src/components/Learn/unused/Concept/ConceptModal.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import Modal from "@material-ui/core/Modal";
3 | import Backdrop from "@material-ui/core/Backdrop";
4 | import Fade from "@material-ui/core/Fade";
5 |
6 | import Concept from "./Concept";
7 |
8 | class ConceptModal extends React.Component {
9 | state = {
10 | open: true
11 | };
12 |
13 | setOpen = value => {
14 | this.setState({
15 | open: value
16 | });
17 | };
18 |
19 | componentDidUpdate(prevProps) {
20 | if (this.props.conceptID !== prevProps.conceptID) {
21 | this.setOpen(true);
22 | }
23 | }
24 |
25 | render() {
26 | return (
27 |
28 |
31 |
32 | this.setOpen(false)}
36 | BackdropComponent={Backdrop}
37 | BackdropProps={{ timeout: 200 }}
38 | >
39 |
40 |
44 |
45 |
46 |
47 |
54 |
55 | );
56 | }
57 | }
58 |
59 | export default ConceptModal;
60 |
--------------------------------------------------------------------------------
/src/components/Learn/unused/Navigation/NavDropdown.js:
--------------------------------------------------------------------------------
1 | import React, { useState } from 'react';
2 |
3 | // const arrow_style = {
4 | // verticalAlign: 'middle',
5 | // marginRight: 7,
6 | // display: 'inline-block',
7 | // lineHeight: 0
8 | // };
9 |
10 | const NavDropdown = (props) => {
11 | const [dropdown, setDropdown] = useState(false);
12 |
13 | return (
14 |
15 |
setDropdown(!dropdown)}>
18 |
19 | {props.gemBox}
20 | {props.labTitle}
21 | {props.cardTitle}
22 |
23 | {/*
*/}
24 | {/* */}
25 | {/* */}
26 |
27 |
28 |
29 | {props.steps}
30 |
31 |
32 |
72 |
73 | );
74 | }
75 |
76 | export default NavDropdown;
77 |
--------------------------------------------------------------------------------
/src/components/Student/Activity/Activity.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import HelpCard from '../../../components/shared/high/HelpCard';
3 | import ActionCard from '../../../components/shared/high/ActionCard';
4 |
5 | const Activity = (props) => {
6 |
7 | return (
8 | <>
9 |
10 |
11 |
12 |
13 | >
14 | )
15 | }
16 |
17 | export default Activity;
18 |
--------------------------------------------------------------------------------
/src/components/Student/Dashboard/Dashboard.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | import ProgressHero from './DashboardHero'
4 | import Journey from './Journey/Journey'
5 |
6 | const Progress = ({}) => {
7 | return (
8 | <>
9 |
10 |
11 | >
12 | )
13 | }
14 |
15 | export default Progress
16 |
--------------------------------------------------------------------------------
/src/components/Student/Dashboard/Journey/Journey.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import styled from 'styled-components'
3 | import { connect } from 'react-redux'
4 |
5 | import Floating from './Floating'
6 | import Progress, { TYPE_JOURNEY } from './Progress'
7 |
8 | import Scrollable from '../../../shared/containers/Scrollable'
9 | import TwoPanel from '../../../shared/containers/TwoPanel'
10 |
11 | const Container = styled.div``
12 |
13 | const FloatingWrapper = styled.div`
14 | position: sticky;
15 | top: 0;
16 | height: 100vh;
17 | display: flex;
18 | align-items: center;
19 | `
20 |
21 | const ModulesContainer = styled.div`
22 | padding: 2.5em 2em;
23 |
24 | section:not(:last-of-type) {
25 | margin-bottom: 4em;
26 | }
27 | `
28 |
29 | const Journey = ({ hasProgress, inprogressModules }) => {
30 | console.log(inprogressModules)
31 | return (
32 | hasProgress && (
33 |
39 |
42 |
43 | }
44 | second={
45 |
46 | {inprogressModules
47 | ?.slice()
48 | .reverse()
49 | .map(im => {
50 | return (
51 |
57 | )
58 | })}
59 |
60 | }
61 | />
62 | )
63 | )
64 | }
65 |
66 | const mapStateToProps = state => {
67 | const {
68 | studentData: { inprogressModules, completedModules, incompleteModules }
69 | } = state
70 |
71 | const hasProgress =
72 | !!inprogressModules?.length ||
73 | !!completedModules?.length ||
74 | !!incompleteModules?.length
75 |
76 | return { hasProgress, inprogressModules }
77 | }
78 |
79 | export default connect(mapStateToProps)(Journey)
80 |
--------------------------------------------------------------------------------
/src/components/Student/Dashboard/Journey/unused/ActivityCard.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { Link } from 'react-router-dom'
3 | import styled from 'styled-components'
4 |
5 | import StatusIcon from '../../../../shared/low/StatusIcon'
6 |
7 | const ActivityCardWrapper = styled.div`
8 | margin: 4.5em 2.25em;
9 | position: relative;
10 | `
11 |
12 | const RenderActivityCard = styled.div`
13 | padding: 2.25em 2.25em 0;
14 | height: 18em;
15 | border-radius: 1em;
16 | background-color: #fff;
17 | text-align: center;
18 | display: inline-block;
19 | overflow-y: auto;
20 | cursor: pointer;
21 | `
22 | const DottedLine = styled.div`
23 | border-bottom: 3px #eaeaea dashed;
24 | position: absolute;
25 | width: 100%;
26 | left: ${props => (props.isLeft ? '50%' : '-50%')};
27 | bottom: 4.2em;
28 | z-index: -1;
29 | `
30 |
31 | const styledLink = { color: 'black', textDecoration: 'none' }
32 |
33 | const ActivityCard = ({ isLast, isLeft, id, name }) => {
34 | return (
35 |
36 |
37 |
38 |
39 |
40 |
{name}
41 |
42 | {/* Choose a module to learn an interesting tidbit about Python */}
43 |
44 |
45 |
46 |
47 | {!isLast && }
48 |
49 | )
50 | }
51 |
52 | export default ActivityCard
53 |
--------------------------------------------------------------------------------
/src/components/Student/Dashboard/Journey/unused/Details.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import styled from 'styled-components'
3 |
4 | const Container = styled.div`
5 | flex: 3;
6 | `
7 |
8 | const Details = ({}) => {
9 | return (
10 |
11 | lol
12 |
13 | )
14 | }
15 |
16 | export default Details
17 |
--------------------------------------------------------------------------------
/src/components/Student/Dashboard/Journey/unused/Journey.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import styled from 'styled-components'
3 | import { connect } from 'react-redux'
4 |
5 | import ActivityCard from './ActivityCard'
6 |
7 | const Container = styled.div`
8 | padding-bottom: 6em;
9 | flex: 6;
10 | display: flex;
11 | flex-flow: row wrap;
12 | align-items: start;
13 | font-size: 90%;
14 | position: relative;
15 | `
16 |
17 | const ColOne = styled.div`
18 | flex: 1;
19 | `
20 |
21 | const ColTwo = styled.div`
22 | flex: 1;
23 | position: relative;
24 | top: 5em;
25 | `
26 |
27 | const Journey = ({ inprogressModules }) => {
28 | const colOne = inprogressModules
29 | .filter((_, index) => index % 2 === 0)
30 | .map((mod, index) => {
31 | return (
32 |
39 | )
40 | })
41 |
42 | const colTwo = inprogressModules
43 | ?.filter((_, index) => index % 2 === 1)
44 | .map((mod, index) => {
45 | return (
46 |
53 | )
54 | })
55 |
56 | return (
57 |
58 |
59 | {/* */}
60 | {colOne}
61 |
62 | {colTwo}
63 |
64 | )
65 | }
66 |
67 | const mapStateToProps = state => {
68 | const {
69 | studentData: { inprogressModules }
70 | } = state
71 |
72 | return { inprogressModules }
73 | }
74 |
75 | export default connect(mapStateToProps)(Journey)
76 |
--------------------------------------------------------------------------------
/src/components/Student/Dashboard/PickCard.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import styled from 'styled-components'
3 |
4 | import AddIcon from '@material-ui/icons/Add'
5 | import MuiIconFormatter from '../../shared/high/MuiIconFormatter'
6 |
7 | const Container = styled.div`
8 | margin-top: 0;
9 | padding: 2em 3em;
10 | height: 15em;
11 |
12 | display: flex;
13 | align-items: center;
14 |
15 | border-radius: 1em;
16 | background-color: #fff;
17 | text-align: center;
18 | cursor: pointer;
19 | `
20 |
21 | const ButtonContainer = styled(MuiIconFormatter)`
22 | margin-top: 1.5em;
23 | `
24 |
25 | const PickCard = ({}) => {
26 | return (
27 |
28 |
29 |
Pick a Module
30 |
31 | Choose a module and learn an interesting tidbit about python
32 |
33 |
34 |
35 |
36 |
37 |
38 | )
39 | }
40 |
41 | export default PickCard
42 |
--------------------------------------------------------------------------------
/src/components/Student/Dashboard/Suggested.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import styled from 'styled-components'
3 |
4 | import Button from '../../shared/low/Button'
5 |
6 | import withApiCache, { CACHE_ACTIVITY } from '../../HOC/WithApiCache'
7 |
8 | const Container = styled.div`
9 | width: 100%;
10 | height: 100%;
11 |
12 | display: flex;
13 | flex-direction: column;
14 | justify-content: center;
15 | align-items: center;
16 | position: relative;
17 |
18 | color: #fff;
19 | `
20 |
21 | const Background = styled.div`
22 | width: 100%;
23 | height: 100%;
24 | position: absolute;
25 |
26 | background: ${props =>
27 | `${props.theme.bgVariant} url("${props.image ??
28 | 'https://i.imgur.com/J1FzZ1l.png'}")`};
29 | background-position: center;
30 | background-size: auto;
31 |
32 | box-shadow: inset 0 0 69420px 69420px rgba(0, 0, 0, 0.69);
33 |
34 | &:hover {
35 | box-shadow: inset 0 0 69420px 69420px rgba(0, 0, 0, 0.69);
36 | }
37 | `
38 |
39 | const Name = styled.h3`
40 | margin: 0;
41 | margin-bottom: 0.5em;
42 | `
43 |
44 | const Description = styled.p`
45 | font-size: 65%;
46 | `
47 |
48 | const StyledButton = styled(Button)`
49 | margin: 1em 0 0.5em;
50 | padding: 0.4em 3em;
51 | font-size: 69%;
52 | `
53 |
54 | const Suggested = ({
55 | id,
56 | wac_data: [activity],
57 |
58 | loading,
59 | onClickButton
60 | }) => {
61 | const { name, summary, image } = activity ?? {}
62 | const isReady = !!name
63 |
64 | return (
65 |
66 |
67 | {!loading ? (
68 | id ? (
69 | isReady ? (
70 |
71 | {name}
72 | {summary}
73 |
74 | Resume
75 |
76 |
77 | ) : (
78 | . . .
79 | )
80 | ) : (
81 | An activity has not been started yet
82 | )
83 | ) : null}
84 |
85 | )
86 | }
87 |
88 | export default withApiCache([CACHE_ACTIVITY])(Suggested)
89 |
--------------------------------------------------------------------------------
/src/components/Student/Dashboard/unused/ProgressCard.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import styled from 'styled-components'
3 |
4 | import BrickWall from '../../../../assets/icons/unused/brickwall'
5 | import GitHub from '../../../../assets/icons/github'
6 | import Button from '../../../shared/low/Button'
7 |
8 | import withApiCache, { CACHE_ACTIVITY } from '../../../HOC/WithApiCache'
9 |
10 | const Container = styled.div`
11 | padding: 2em;
12 | width: 15em;
13 | height: 18em;
14 | display: flex;
15 | flex-direction: column;
16 |
17 | background-color: ${props => props.theme.bgVariant};
18 | color: #fff;
19 | text-align: center;
20 |
21 | &:hover {
22 | box-shadow: inset 0 0 100px 100px rgba(255, 255, 255, 0.05);
23 | }
24 | `
25 |
26 | const InfoArea = styled.div`
27 | flex-shrink: 1;
28 | `
29 |
30 | const IconWrapper = styled.div`
31 | margin-bottom: 0.75em;
32 | `
33 |
34 | const Name = styled.h3`
35 | margin: 0;
36 | margin-bottom: 0.2em;
37 | `
38 |
39 | const Description = styled.p`
40 | margin: 0.5em 0;
41 | margin-top: 0;
42 | font-size: 65%;
43 | padding: 0 10%;
44 | `
45 |
46 | const ButtonContainer = styled.div`
47 | flex-grow: 1;
48 | display: flex;
49 | justify-content: center;
50 | align-items: flex-end;
51 | `
52 |
53 | const StyledButton = styled(Button)`
54 | margin: 1em 0 0.5em;
55 | padding: 0.6em 2em;
56 | font-size: 75%;
57 | `
58 |
59 | const ActivityCard = ({
60 | wac_data: [activity],
61 |
62 | image,
63 | onClickButton
64 | }) => {
65 | const { name, summary } = activity ?? {}
66 |
67 | const renderAppropriateImage = (imageName, width, height) => {
68 | switch (imageName) {
69 | case 'brickwall':
70 | return
71 |
72 | case 'github':
73 | return
74 |
75 | default:
76 | return null
77 | }
78 | }
79 |
80 | return (
81 |
82 |
83 | {renderAppropriateImage(image, '3em')}
84 | {name}
85 | {summary}
86 |
87 |
88 |
89 |
95 | Resume
96 |
97 |
98 |
99 | )
100 | }
101 |
102 | export default withApiCache([CACHE_ACTIVITY])(ActivityCard)
103 |
--------------------------------------------------------------------------------
/src/components/Student/Profile/Profile.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | const Profile = (props) => {
4 | return (
5 |
6 |
Profile Page
7 |
8 | )
9 | }
10 |
11 | export default Profile;
--------------------------------------------------------------------------------
/src/components/Student/Student.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { Route, Switch, useRouteMatch } from 'react-router-dom'
3 |
4 | import Dashboard from './Dashboard/Dashboard'
5 |
6 | const Student = ({}) => {
7 | const { path } = useRouteMatch()
8 |
9 | return (
10 |
11 |
12 |
13 | )
14 | }
15 |
16 | export default Student
17 |
--------------------------------------------------------------------------------
/src/components/Student/World/World.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import styled from 'styled-components'
3 |
4 | const Container = styled.div``
5 |
6 | const World = ({ className, ...props }) => {
7 | return (
8 |
9 | {/* ... */}
10 |
11 | )
12 | }
13 |
14 | export default World
15 |
--------------------------------------------------------------------------------
/src/components/Student/unused/CurrentTrack.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import styled from "styled-components";
3 | import { connect } from "react-redux";
4 |
5 | const RenderSubject = styled.div``;
6 |
7 | const Title = styled.h1`
8 | margin-top: 0;
9 | margin-bottom: 0.3em;
10 | `;
11 |
12 | const Subtitle = styled.h3`
13 | margin: 0;
14 | `;
15 |
16 | const Description = styled.p`
17 | margin: 0.5em 0 2em;
18 | width: 75%;
19 | `;
20 |
21 | const ProgressBar = styled.div`
22 | width: 93%;
23 | height: 1em;
24 | background-color: #eee;
25 | position: relative;
26 |
27 | &:before {
28 | content: '';
29 | position: absolute;
30 | top: 0;
31 | bottom: 0;
32 | left: 0;
33 | width: ${props => props.progress};
34 | background-color: #4788ff;
35 | }
36 |
37 | &:after {
38 | content: '${props => props.progress}';
39 | line-height: 1em;
40 | position: absolute;
41 | top: 0;
42 | bottom: 0;
43 | right: -3em;
44 | }
45 |
46 | @media only screen and (max-width: 555px) {
47 | width: 90%;
48 | }
49 | `;
50 |
51 | const Subject = props => {
52 | return (
53 |
54 | {props.name}
55 | with emphasis in React.js
56 | {props.description}
57 |
58 | {/* */}
59 |
60 | );
61 | };
62 |
63 | const mapStateToProps = state => {
64 | const studentData = state.studentData; // reference for easy typing
65 |
66 | if (!studentData.current_track) {
67 | return {
68 | name: "",
69 | description: ""
70 | };
71 | }
72 | return {
73 | name: studentData.current_track.name,
74 | subtitle: "I am a static piece text",
75 | description: 'I am a static piece text. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.'
76 | // description: studentData.currentTrack.description
77 | };
78 | };
79 |
80 | export default connect(mapStateToProps)(Subject);
81 |
--------------------------------------------------------------------------------
/src/components/Student/unused/GreetingSection.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import styled from 'styled-components';
3 |
4 | const Greeting = styled.div`
5 | margin: 15vh 20%;
6 | `
7 |
8 | const GreetingSection = (props) => {
9 | const renderedTop = (props.top) ? {props.top}
: null;
10 | const renderedBottom = (props.bottom) ? {props.bottom}
: null;
11 |
12 | return (
13 |
14 | {renderedTop}
15 | {props.title}
16 | {props.subtitle}
17 | {renderedBottom}
18 |
19 | )
20 |
21 | }
22 |
23 | export default GreetingSection;
--------------------------------------------------------------------------------
/src/components/Student/unused/LabVerticalCard.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import styled from "styled-components";
3 |
4 | import Button from "../../shared/Button";
5 |
6 | const NavyCard = styled.div`
7 | padding: 1rem;
8 | max-width: 350px;
9 | min-height: 375px;
10 | border-radius: 7px;
11 | box-shadow: 0 2px 10px rgba(0, 0, 0, 0.12);
12 | background-color: #0b1354;
13 | text-align: center;
14 | `;
15 |
16 | const LabVerticalCard = props => {
17 | return (
18 |
19 |
26 | {props.labTitle}
27 | {props.labDescription}
28 |
40 | );
41 | };
42 |
43 | export default LabVerticalCard;
44 |
--------------------------------------------------------------------------------
/src/components/Student/unused/MenuBar.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import styled from "styled-components";
3 | import { connect } from "react-redux";
4 |
5 | import { setViewStudent } from "../../../redux/actions/viewManager";
6 |
7 | import "./Student.css";
8 |
9 | const Menu = styled.div`
10 | box-shadow: 0 4px 30px 0 rgba(144, 144, 144, 0.2);
11 | flex-wrap: wrap;
12 | display: flex;
13 | text-align: center;
14 | `;
15 |
16 | const Tab = styled.div`
17 | transition: 0.2s ease;
18 | padding: 15px 0;
19 | flex: 1;
20 |
21 | &:hover {
22 | color: #0070f3;
23 | cursor: pointer;
24 | }
25 | `;
26 |
27 | const MenuBar = props => {
28 | const windowTitles = ["Curriculum", "Activity", "Progress", "Profile"];
29 |
30 | const windowPortals = windowTitles.map((title, index) => {
31 | return (
32 | props.onSetViewStudent(title.toUpperCase())}
40 | >
41 | {title}
42 |
43 | );
44 | });
45 |
46 | return ;
47 | };
48 |
49 | const mapDispatchToProps = dispatch => {
50 | return {
51 | onSetViewStudent: viewName => {
52 | dispatch(setViewStudent(viewName));
53 | }
54 | };
55 | };
56 |
57 | export default connect(null, mapDispatchToProps)(MenuBar);
58 |
--------------------------------------------------------------------------------
/src/components/Student/unused/SelectTopic.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import styled from 'styled-components';
3 |
4 | import AtomIcon from '../../../assets/icons/atom';
5 |
6 | const RenderSelectTopic = styled.div`
7 | padding: 3em 10%;
8 | background-color: #205791;
9 | color: #fff;
10 | text-align: center;
11 | border-radius: 0.5em;
12 | `
13 |
14 | const AtomWrapper = styled.div`
15 | transform: scale(-1, 1);
16 | margin: 1em 0;
17 | `
18 |
19 | const Name = styled.h1`
20 | margin: 0 0;
21 | `
22 |
23 | const Description = styled.p`
24 | font-size: 1.5em;
25 | padding: 0 5%;
26 | `
27 |
28 | const SelectTopic = props => {
29 | return (
30 |
31 |
32 |
33 |
34 | Select a Topic to Explore
35 |
36 | There are countless areas of computer science, ready to be explored!
37 |
38 |
39 | )
40 | }
41 |
42 | export default SelectTopic;
43 |
--------------------------------------------------------------------------------
/src/components/Student/unused/TopicInfo.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from "react";
2 | import styled from "styled-components";
3 | import { connect } from "react-redux";
4 |
5 | const TopicInfo = () => {
6 | return <>>;
7 | };
8 |
9 | const mapStateToProps = state => {
10 | ;
11 | }
12 |
13 | export default connect()(TopicInfo);
14 |
--------------------------------------------------------------------------------
/src/components/Teacher/Details/Details.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import styled from 'styled-components'
3 | import { connect } from 'react-redux'
4 |
5 | import DetailsHeader from './DetailsHeader'
6 |
7 | const Container = styled.div`
8 | position: relative;
9 | background: #fafafa;
10 | flex: 0.2;
11 | display: flex;
12 | flex-direction: column;
13 | z-index: 1;
14 | border-left: 1px solid ${props => props.theme.offFont};
15 | `
16 |
17 | const Details = ({}) => {
18 | return (
19 |
20 |
21 |
22 | )
23 | }
24 |
25 | const mapStateToProps = state => state
26 |
27 | export default connect(mapStateToProps)(Details)
28 |
--------------------------------------------------------------------------------
/src/components/Teacher/Details/DetailsHeader.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import styled from 'styled-components'
3 | import { connect } from 'react-redux'
4 |
5 | import { Divider } from '@chakra-ui/core'
6 | import GradeStatus from '../../shared/high/GradeStatus'
7 | import ProfPic from '../../shared/low/ProfPic'
8 | import ClampedDiv from '../../shared/utils/ClampedDiv'
9 |
10 | const Container = styled.div`
11 | padding: 1em 2em;
12 | flex-shrink: 0;
13 | width: 100%;
14 | `
15 |
16 | const AssignmentName = styled(ClampedDiv)`
17 | margin-top: 0;
18 | margin-bottom: 0.5em;
19 | font-weight: bold;
20 | font-size: 120%;
21 | width: 12em;
22 | `
23 |
24 | const DetailsHeader = ({ isReady, activity, user, feedbacksArray }) => {
25 | const isAllowedToSubmit = feedbacksArray?.every(
26 | feedback => feedback?.isPassed !== undefined && feedback?.comment
27 | )
28 |
29 | const hasNotStarted = feedbacksArray?.every(
30 | feedback => feedback?.isPassed === undefined && !feedback?.comment
31 | )
32 |
33 | return (
34 | isReady && (
35 |
36 |
37 | {
39 | if (isAllowedToSubmit) return 'success'
40 | if (hasNotStarted) return 'fatal'
41 | return 'warning'
42 | })()}
43 | >
44 | {(() => {
45 | if (isAllowedToSubmit) return 'READY TO SUBMIT'
46 | if (hasNotStarted) return 'NOT STARTED'
47 | return 'PARTIALLY GRADED'
48 | })()}
49 |
50 |
51 | {activity?.name}
52 |
56 |
57 | )
58 | )
59 | }
60 |
61 | const mapStateToProps = state => {
62 | const {
63 | cache: { cachedActivities, cachedUsers },
64 | teacherData: {
65 | submissions,
66 | indicators: { currentSubmissionIndex },
67 | ram: { feedbacks }
68 | }
69 | } = state
70 |
71 | const { userId, user, studentId, student, activity, checkpoints } =
72 | submissions?.[currentSubmissionIndex] ?? {}
73 |
74 | const feedbacksArray = checkpoints?.map(checkpoint => {
75 | const { id: checkpointId } = checkpoint.checkpoint
76 | return feedbacks[`student${studentId}_checkpoint${checkpointId}`]
77 | })
78 |
79 | return {
80 | isReady: !!submissions.length,
81 | activity: cachedActivities[activity?.id],
82 | user: cachedUsers[userId ?? user?.id],
83 | feedbacksArray
84 | }
85 | }
86 |
87 | export default connect(mapStateToProps)(DetailsHeader)
88 |
--------------------------------------------------------------------------------
/src/components/Teacher/Sidebar/NavItem.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import styled from 'styled-components'
3 |
4 | import ProfPic from '../../shared/low/ProfPic'
5 |
6 | import withApiCache, {
7 | CACHE_ACTIVITY,
8 | CACHE_USER
9 | } from '../../HOC/WithApiCache'
10 |
11 | const Activity = withApiCache([CACHE_ACTIVITY])(({ wac_data: [activity] }) => (
12 | {activity?.name}
13 | ))
14 |
15 | const Student = withApiCache([CACHE_USER])(({ wac_data: [user] }) => (
16 |
17 |
21 | {/*
22 | {activity?.time}
23 | */}
24 |
25 | ))
26 |
27 | const Container = styled.div`
28 | margin: 0.5em;
29 | padding: 1em 1.5em;
30 | border-radius: 0.5em;
31 | color: white;
32 |
33 | cursor: pointer;
34 | transition: 0.2s ease background-color;
35 |
36 | &.active,
37 | :hover {
38 | background-color: ${props => props.theme.accent}44;
39 | }
40 | `
41 |
42 | const NavItem = ({ className, activityId, userId, studentId }) => {
43 | return (
44 |
45 |
46 |
47 |
48 | )
49 | }
50 |
51 | export default NavItem
52 |
--------------------------------------------------------------------------------
/src/components/Teacher/Sidebar/Sidebar.js:
--------------------------------------------------------------------------------
1 | import React, { useEffect } from 'react'
2 | import styled from 'styled-components'
3 | import { connect } from 'react-redux'
4 | import { get } from 'lodash'
5 |
6 | import SidebarHeader from './SidebarHeader'
7 | import SidebarNav from './SidebarNav'
8 |
9 | import { fadeIn } from '../../../styles/GlobalAnime'
10 |
11 | const Container = styled.div`
12 | position: relative;
13 | background: ${props => props.theme.bgVariant};
14 | display: flex;
15 | flex-direction: column;
16 | z-index: 1;
17 | box-shadow: 0 0 1.5em rgba(0, 0, 0, 0.1);
18 |
19 | @media screen and (orientation: landscape) {
20 | flex: 0.21;
21 | }
22 | `
23 |
24 | const Sidebar = ({ isReady }) => {
25 | useEffect(() => {
26 | if (isReady) {
27 | fadeIn('.teacher-i-sidebar')
28 | }
29 | }, [isReady])
30 |
31 | return (
32 |
33 |
34 |
35 |
36 | )
37 | }
38 |
39 | const mapStateToProps = state => ({
40 | isReady: !!get(state, 'teacherData.submissions.length')
41 | })
42 |
43 | export default connect(mapStateToProps)(Sidebar)
44 |
--------------------------------------------------------------------------------
/src/components/Teacher/Sidebar/SidebarHeader.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import styled from 'styled-components'
3 |
4 | import ProfPic from '../../shared/low/ProfPic'
5 |
6 | const Wrapper = styled.div`
7 | background-color: black;
8 | `
9 |
10 | const Container = styled.div`
11 | padding: 2em;
12 | flex-shrink: 0;
13 | opacity: 0;
14 |
15 | background-color: black;
16 | color: ${props => props.theme.offFont};
17 | `
18 |
19 | const SidebarHeader = ({}) => {
20 | return (
21 |
22 |
23 | Grading View
24 |
32 |
33 |
34 | )
35 | }
36 |
37 | export default SidebarHeader
38 |
--------------------------------------------------------------------------------
/src/components/Teacher/Sidebar/SidebarNav.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import styled from 'styled-components'
3 | import { connect } from 'react-redux'
4 |
5 | import NavItem from './NavItem'
6 | import ActiveList from '../../shared/containers/ActiveList'
7 | import ClampedDiv from '../../shared/utils/ClampedDiv'
8 |
9 | import { setCurrentSubmissionByIndex } from '../../../redux/actions/teacherData'
10 |
11 | const StyledActiveList = styled(ActiveList)`
12 | font-size: 85%;
13 | flex-grow: 1;
14 | `
15 |
16 | const SidebarNav = ({
17 | submissions,
18 | currentSubmissionIndex,
19 | onSetCurrentSubmissionByIndex
20 | }) => {
21 | return (
22 | {
27 | if (index !== currentSubmissionIndex)
28 | onSetCurrentSubmissionByIndex(index)
29 | }}
30 | >
31 | {(submission, index) => (
32 |
37 | )}
38 |
39 | )
40 | }
41 |
42 | const mapStateToProps = state => {
43 | const {
44 | teacherData: {
45 | submissions,
46 | indicators: { currentSubmissionIndex }
47 | }
48 | } = state
49 |
50 | return {
51 | submissions,
52 | currentSubmissionIndex
53 | }
54 | }
55 |
56 | const mapDispatchToProps = dispatch => ({
57 | onSetCurrentSubmissionByIndex: submissionIndex =>
58 | dispatch(setCurrentSubmissionByIndex(submissionIndex))
59 | })
60 |
61 | export default connect(mapStateToProps, mapDispatchToProps)(SidebarNav)
62 |
--------------------------------------------------------------------------------
/src/components/Teacher/Teacher.js:
--------------------------------------------------------------------------------
1 | import React, { useEffect, useRef } from 'react'
2 | import styled from 'styled-components'
3 | import { connect } from 'react-redux'
4 | import { get } from 'lodash'
5 |
6 | import Toolbar from './Toolbar/Toolbar'
7 | import Sidebar from './Sidebar/Sidebar'
8 | import Content from './Content/Content'
9 | import Details from './Details/Details'
10 |
11 | import WithPageSpinner from '../HOC/WithPageSpinner'
12 | import { init } from '../../redux/actions/teacherData'
13 |
14 | const Container = styled.div`
15 | display: flex;
16 | position: relative;
17 | background: #fafafa;
18 |
19 | > :nth-child(1),
20 | > :nth-child(2),
21 | > :nth-child(3),
22 | > :nth-child(4) {
23 | height: 100vh;
24 | }
25 |
26 | @media only screen and (orientation: landscape) {
27 | font-size: 80%;
28 | }
29 | `
30 |
31 | const Teacher = ({ teacherId, isReady, onInit }) => {
32 | const containerRef = useRef(null)
33 |
34 | useEffect(() => {
35 | if (teacherId) onInit(teacherId)
36 | }, [])
37 |
38 | return (
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 | )
48 | }
49 |
50 | const mapStateToProps = state => {
51 | const {
52 | account: { meta },
53 | teacherData: { submissions }
54 | } = state
55 | return {
56 | isReady: !!submissions?.length,
57 | teacherId: meta?.teacherId
58 | }
59 | }
60 |
61 | const mapDispatchToProps = dispatch => ({
62 | onInit: teacherId => dispatch(init(teacherId))
63 | })
64 |
65 | export default connect(mapStateToProps, mapDispatchToProps)(Teacher)
66 |
--------------------------------------------------------------------------------
/src/components/Teacher/Toolbar/Toolbar.js:
--------------------------------------------------------------------------------
1 | import React, { useEffect } from 'react'
2 | import styled from 'styled-components'
3 | import anime from 'animejs'
4 | import { connect } from 'react-redux'
5 | import { Link } from 'react-router-dom'
6 |
7 | import Icon from '../../shared/low/Icon'
8 |
9 | const Container = styled.div`
10 | position: relative;
11 | display: flex;
12 | flex-direction: column;
13 | justify-content: space-around;
14 | align-items: center;
15 | flex: 0.08;
16 |
17 | background-color: ${props => props.theme.bg};
18 | text-align: center;
19 |
20 | @media screen and (orientation: landscape) {
21 | flex: 0.04;
22 | }
23 | `
24 |
25 | const Elem = styled(Link)`
26 | padding: 10%;
27 | display: flex;
28 | justify-content: center;
29 | align-items: center;
30 | cursor: pointer;
31 |
32 | @media screen and (orientation: landscape) {
33 | padding: 12%;
34 | }
35 | `
36 |
37 | const Toolbar = ({}) => {
38 | useEffect(() => {}, [])
39 |
40 | return (
41 |
42 |
43 |
44 |
45 |
46 | )
47 | }
48 |
49 | const mapStateToProps = state => state
50 |
51 | export default connect(mapStateToProps)(Toolbar)
52 |
--------------------------------------------------------------------------------
/src/components/Teacher/unused/Grade/EntryBox.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import styled from 'styled-components';
3 | import Grid from '@material-ui/core/Grid';
4 |
5 | const EntryWrapper = styled.div`
6 | padding: 25px 10px;
7 | border-bottom: 1.5px solid lightgrey;
8 | transition: transform .2s;
9 | background: ${props => (props.index === props.current) ? 'rgba(0, 118, 255, 0.1)' : 'white'};
10 |
11 | &:hover {
12 | transform: translateX(1px);
13 | color: #0070f3;
14 | background: rgba(0, 118, 255, 0.1);
15 | }
16 | `
17 |
18 | const StudentName = styled.div`
19 | color: green;
20 | font-size: 14px;
21 | `
22 |
23 | const Time = styled.div`
24 | color: grey;
25 | font-size: 14px;
26 | `
27 |
28 | const EntryBox = props => (
29 | props.switchSubmission(props.index)}>
30 |
31 |
32 | o
33 |
34 |
35 |
36 | {props.activityName}
37 |
38 |
39 |
40 | {props.studentName}
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 | )
50 |
51 | export default EntryBox;
52 |
--------------------------------------------------------------------------------
/src/components/Teacher/unused/Teacher.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 |
3 | import TeacherHero from './TeacherHero'
4 | import TeacherContent from './TeacherContent'
5 |
6 | class Teacher extends Component {
7 | constructor() {
8 | super()
9 | this.state = {
10 | teacherName: '',
11 | pendingAssignments: 4,
12 | submittedAssignments: 4
13 | }
14 | // this.service = new TeacherService();
15 | }
16 |
17 | render() {
18 | return (
19 |
20 |
24 |
25 |
26 | )
27 | }
28 | }
29 |
30 | export default Teacher
31 |
--------------------------------------------------------------------------------
/src/components/Teacher/unused/TeacherHero.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import styled from 'styled-components';
3 |
4 | const Hero = styled.div`
5 | background-color: #0b1330;
6 | color: white;
7 | margin: 0 -8px;
8 | padding: 40px 40px 10px 40px;
9 | `
10 |
11 | const MainArea = styled.div`
12 | display: flex;
13 | `
14 |
15 | const StatusSquare = styled.div`
16 | width: 50px;
17 | height: 50px;
18 | border-radius: 10px;
19 | margin-right: 1.5rem;
20 | box-shadow: 0 4px 30px 0 #ff6726;
21 | background-color: #ff6726;
22 | `
23 |
24 | const SubmittedSquare = styled.div`
25 | display: inline-block;
26 | width: 20px;
27 | height: 20px;
28 | border-radius: 5px;
29 | background-color: #007bed;
30 | margin: 0 0 0 5px;
31 | padding: 4px;
32 | text-align: center;
33 | `
34 |
35 | const Title = styled.div`
36 | font-size: 20px;
37 | font-weight: bold;
38 | `
39 |
40 | const SubmittedBar = styled.div`
41 | margin-top: 40px;
42 | `
43 |
44 | const TeacherHero = (props) => {
45 | return (
46 |
47 |
48 |
49 |
50 |
Grade Assignments
51 |
{props.pending} assignments pending
52 |
53 |
54 |
55 |
56 | Submitted Assignments
57 | {props.submitted}
58 |
59 |
60 | )
61 | }
62 |
63 | export default TeacherHero;
64 |
--------------------------------------------------------------------------------
/src/components/Visitor/Visitor.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | const Visitor = () => {
4 | return <>>
5 | }
6 |
7 | export default Visitor
8 |
--------------------------------------------------------------------------------
/src/components/shared/containers/DynamicModal.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import styled from 'styled-components'
3 |
4 | import Modal from '@material-ui/core/Modal'
5 | import Backdrop from '@material-ui/core/Backdrop'
6 | import Fade from '@material-ui/core/Fade'
7 |
8 | import { sizes } from '../../../styles/media'
9 |
10 | export const Container = styled.div`
11 | margin: 0 2em;
12 | border-radius: 5px;
13 | flex: 1;
14 | outline: 0;
15 | overflow-y: auto;
16 | font-size: 125%;
17 |
18 | display: flex;
19 | background-color: #fff;
20 |
21 | @media screen and (orientation: landscape) {
22 | font-size: 100%;
23 | }
24 |
25 | // SIZING
26 |
27 | max-width: calc(45em * ${props => props.scaleX});
28 | height: calc(36em * ${props => props.scaleY}); // ipad vertical
29 |
30 | @media screen and (orientation: landscape) and (max-height: ${sizes.tablet}px) {
31 | height: calc((100% - 4em) * ${props => props.scaleY});
32 | ${props => props.heightAuto && 'height: auto;'}
33 | }
34 |
35 | // target vertical phone
36 | @media screen and (orientation: portrait) and (max-width: ${sizes.thone}px) {
37 | height: calc((100% - 10em) * ${props => props.scaleY});
38 | ${props => props.heightAuto && 'height: auto;'}
39 | }
40 |
41 | ${props =>
42 | props.heightAuto &&
43 | `height: auto;
44 | `}
45 | `
46 |
47 | const DynamicModal = ({
48 | className,
49 | children,
50 |
51 | open,
52 | closed,
53 | custom,
54 | scaleX = 1,
55 | scaleY = 1,
56 | heightAuto
57 | }) => {
58 | return (
59 |
75 |
76 | {!custom ? (
77 |
83 | {children}
84 |
85 | ) : (
86 | children
87 | )}
88 |
89 |
90 | )
91 | }
92 |
93 | export default DynamicModal
94 |
--------------------------------------------------------------------------------
/src/components/shared/containers/PostModal.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import styled from 'styled-components'
3 |
4 | import DynamicModal, { Container } from './DynamicModal'
5 |
6 | const CustomContainer = styled(Container)`
7 | display: flex;
8 | flex-direction: column;
9 | `
10 | const Header = styled.div`
11 | flex: ${props => props.ratio};
12 | background-color: black;
13 | color: white;
14 | `
15 | const Content = styled.div`
16 | flex: ${props => 1 - props.ratio};
17 | `
18 |
19 | const PostModal = ({
20 | open,
21 | closed,
22 | scaleX = 1,
23 | scaleY = 1,
24 | header,
25 | content,
26 | children,
27 | ratio = 0.5
28 | }) => (
29 |
35 |
36 |
39 |
40 | {content}
41 |
42 | {children}
43 |
44 |
45 | )
46 |
47 | export default PostModal
48 |
--------------------------------------------------------------------------------
/src/components/shared/containers/Scrollable.js:
--------------------------------------------------------------------------------
1 | import React, { useRef } from 'react'
2 | import styled from 'styled-components'
3 |
4 | import HeaderShadow from '../low/HeaderShadow'
5 |
6 | const Container = styled.div`
7 | height: ${props => props.height};
8 | `
9 |
10 | const Scrollable = ({
11 | idName,
12 | className,
13 |
14 | children,
15 | topType,
16 | topCallback,
17 | bottomType,
18 | bottomCallback,
19 |
20 | height = 'auto',
21 | arrowNav = true
22 | }) => {
23 | const containerRef = useRef(null)
24 |
25 | return (
26 | {
32 | if (!arrowNav) {
33 | switch (e.key) {
34 | case 'ArrowUp':
35 | case 'ArrowLeft':
36 | case 'ArrowRight':
37 | case 'ArrowDown':
38 | case ' ':
39 | e.preventDefault()
40 | break
41 | default:
42 | break
43 | }
44 | }
45 | }}
46 | >
47 |
52 | {children}
53 |
59 |
60 | )
61 | }
62 |
63 | export default Scrollable
64 |
--------------------------------------------------------------------------------
/src/components/shared/containers/TwoPanel.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import styled from 'styled-components'
3 |
4 | const Container = styled.div`
5 | ${props => {
6 | if (props.orientation.toUpperCase() !== 'VERTICAL') {
7 | return `
8 | ${props.fullSizeAxis ? 'width: 100%;' : ''}
9 | ${props.fullSizeOffAxis ? 'height: 100%;' : ''}
10 | `
11 | }
12 | return `
13 | ${props.fullSizeAxis ? 'height: 100%;' : ''}
14 | ${props.fullSizeOffAxis ? 'width: 100%;' : ''}
15 | `
16 | }}
17 | display: flex;
18 | flex-direction: ${props =>
19 | props.orientation.toUpperCase() !== 'VERTICAL' ? 'row' : 'column'};
20 | `
21 |
22 | const centering = props => `
23 | ${
24 | props.centerX || props.centerY || props.centerBoth
25 | ? `display: flex;
26 | flex-direction: column;`
27 | : ''
28 | }
29 | ${props.centerX || props.centerBoth ? 'align-items: center;' : ''}
30 | ${props.centerY || props.centerBoth ? 'justify-content: center;' : ''}
31 | `
32 |
33 | const LeftPanelWrapper = styled.div`
34 | flex: ${props => props.ratio || 0.5};
35 | ${props => centering(props)}
36 | position: relative;
37 | `
38 |
39 | const RightPanelWrapper = styled.div`
40 | flex: ${props => 1 - props.ratio || 0.5};
41 | ${props => centering(props)}
42 | position: relative;
43 | `
44 |
45 | const TwoPanel = ({
46 | className,
47 |
48 | ratio,
49 | orientation = 'horizontal',
50 |
51 | fullSizeAxis,
52 | fullSizeOffAxis,
53 |
54 | first,
55 | firstStyle,
56 | firstCenterX,
57 | firstCenterY,
58 | firstCenterBoth,
59 |
60 | second,
61 | secondStyle,
62 | secondCenterX,
63 | secondCenterY,
64 | secondCenterBoth,
65 |
66 | children
67 | }) => (
68 |
74 |
82 | {first}
83 |
84 |
92 | {second}
93 |
94 | {children}
95 |
96 | )
97 |
98 | export default TwoPanel
99 |
--------------------------------------------------------------------------------
/src/components/shared/high/ActionCard.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import styled from 'styled-components';
3 | import resumeIcon from '../../../assets/icons/resume-pie.svg';
4 | import startIcon from '../../../assets/icons/start-flag.svg';
5 | import Icon from '../low/Icon.js';
6 |
7 | const BlueCard = styled.div`
8 | text-align: center;
9 | background-color: rgba(44,114,223, 0.85);
10 | border-radius: 8px;
11 | margin: 1em 1em;
12 | padding: 4em 1em 1em;
13 | max-width: 600px;
14 | `
15 |
16 | const WhiteIcon = styled.div`
17 | color: white;
18 | margin: 2em 1em 1em 1em;
19 | padding: 0.25em 1em;
20 | `
21 |
22 | const CardText = styled.h1`
23 | color: white;
24 | margin: 1em 1em;
25 | padding: 0.25em 1em;
26 | `
27 |
28 | const ActionCard = ({
29 | type = 'issue',
30 | onClick,
31 | className
32 | }) => {
33 | var text, icon, size='4em';
34 | if (type === 'resume'){
35 | text = 'Resume Activity';
36 | icon = resumeIcon;
37 | } else if (type === 'start') {
38 | text = 'Start Activity';
39 | icon = startIcon;
40 | } else {
41 | // Invalid style property, you should not be here.
42 | }
43 |
44 | return (
45 |
46 |
47 | {text}
48 |
49 | )
50 | }
51 |
52 | export default ActionCard;
53 |
--------------------------------------------------------------------------------
/src/components/shared/high/AvatarGroup.js:
--------------------------------------------------------------------------------
1 | import React, { cloneElement } from 'react'
2 | import { Stack } from '@chakra-ui/core'
3 | import styled from '@emotion/styled'
4 | import { v4 as uuid } from 'uuid'
5 |
6 | const StyleProvider = styled.div`
7 | .sb-avatar__text {
8 | border: 0.12em solid ${props => props.borderColor};
9 | }
10 | `
11 |
12 | const AvatarGroup = ({
13 | size = '1.6em',
14 | borderColor = '#ffffff',
15 | max,
16 | spacing = '-0.8em',
17 | children,
18 | textSizeRatio = 2.5,
19 | ...props
20 | }) => {
21 | const clones = children?.map((a, i) => {
22 | if (max && i > max) return null
23 |
24 | return cloneElement(a, {
25 | key: uuid(),
26 | style: {
27 | marginLeft: i === 0 ? 0 : spacing,
28 | zIndex: children.length - i
29 | },
30 | size,
31 | round: true,
32 | textSizeRatio
33 | })
34 | })
35 |
36 | return (
37 |
38 |
39 | {clones}
40 |
41 |
42 | )
43 | }
44 |
45 | export default AvatarGroup
46 |
--------------------------------------------------------------------------------
/src/components/shared/high/GoBack.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import styled from 'styled-components'
3 | import { withRouter } from 'react-router-dom'
4 |
5 | import BackIcon from '@material-ui/icons/KeyboardArrowLeftRounded'
6 |
7 | import IconLine from '../low/IconLine'
8 |
9 | const Container = styled.div`
10 | margin-bottom: 0.2em;
11 | width: fit-content;
12 | color: ${props => props.theme.offFont};
13 | cursor: pointer;
14 |
15 | &:hover {
16 | color: ${props => props.theme.accentVariant};
17 | }
18 | `
19 |
20 | const GoBack = ({ history, className, text = 'Back', hardcodedUrl }) => (
21 | {
26 | history.push(hardcodedUrl)
27 | }
28 | : history.goBack
29 | }
30 | >
31 | }>{text}
32 |
33 | )
34 |
35 | export default withRouter(GoBack)
36 |
--------------------------------------------------------------------------------
/src/components/shared/high/GradeStatus.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import styled from 'styled-components'
3 | import Dot from '@material-ui/icons/FiberManualRecord'
4 |
5 | import IconLine from '../low/IconLine'
6 |
7 | const selectColor = props => {
8 | switch (props.status) {
9 | case 'success':
10 | return props.theme.pastel.green
11 | case 'fatal':
12 | return props.theme.pastel.red
13 | case 'warning':
14 | return props.theme.muted.yellow
15 | case 'none':
16 | return '#aaaaaa'
17 | default:
18 | return props.theme.accent
19 | }
20 | }
21 |
22 | const Container = styled(IconLine)`
23 | margin-top: 0;
24 | font-weight: bold;
25 | color: ${props => selectColor(props)};
26 | `
27 |
28 | const GradeStatus = ({ className, status = '', children }) => {
29 | return (
30 | }
33 | gap={'0.5em'}
34 | status={status.toLowerCase()}
35 | >
36 | {children}
37 |
38 | )
39 | }
40 |
41 | export default GradeStatus
42 |
--------------------------------------------------------------------------------
/src/components/shared/high/HelpCard.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import styled from 'styled-components';
3 | import Button from '../low/Button.js';
4 |
5 | import ExclamationIcon from '../../../assets/icons/exclamation'
6 |
7 | const WhiteCard = styled.div`
8 | display: flex;
9 | text-align: left;
10 | background-color: white;
11 | margin: 1em 1em;
12 | padding: 0.25em 1em;
13 | max-width: 600px;
14 | background: #FFFFFF;
15 | border: 3px solid rgba(210, 210, 210, 0.2);
16 | box-sizing: border-box;
17 | border-radius: 5px;
18 | `
19 | const LeftColumn = styled.div`
20 | flex: 33%;
21 | padding: 0.25em 1em;
22 | text-align: center;
23 | `
24 | const RightColumn = styled.div`
25 | flex: 67%;
26 | padding: 0.25em 1em;
27 | `
28 | const CardText = styled.p`
29 | font-size: 1.1em;
30 | color: black;
31 | margin: 1em 0em;
32 | padding: 0.25em 0em;
33 | `
34 | const IconWrapper = styled.div`
35 | margin: 2em auto;
36 | width: 4em;
37 | height: 4em;
38 | color: ${props => props.color};
39 | `
40 |
41 | const HelpCard = ({
42 | type = 'issue',
43 | onClick,
44 | className
45 | }) => {
46 | var title, description, btnText, color, icon;
47 | if (type === 'issue'){
48 | title = 'Raise an Issue';
49 | description = 'Is something unclear? Could be explained better? Raise an issue to improve the curriculum.';
50 | btnText = 'Raise an Issue';
51 | color = 'rgba(86,171,104, 1.0)';
52 | } else if (type === 'feedback') {
53 | title = 'Feedback';
54 | description = 'Have feedback to provide? Send feedback to improve this product.';
55 | btnText = 'Provide Feedback';
56 | color = 'rgba(238,112,60, 1.0)';
57 | } else {
58 | // Invalid style property, you should not be here.
59 | }
60 |
61 | return (
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 | {title}
70 | {description}
71 |
72 |
73 |
74 | )
75 | }
76 |
77 | export default HelpCard;
78 |
--------------------------------------------------------------------------------
/src/components/shared/high/IconWithProgress.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import styled from 'styled-components'
3 |
4 | import ProgressCircle from '../low/ProgressCircle'
5 |
6 | const defaultIcon = require('../../../assets/icons/cards.svg')
7 |
8 | const Container = styled.div`
9 | width: ${props => props.size};
10 | height: ${props => props.size};
11 | position: relative;
12 | border-radius: 50%;
13 | flex-shrink: 0;
14 | `
15 |
16 | const FullAbsolute = styled.div`
17 | position: absolute;
18 | top: 0;
19 | left: 0;
20 | right: 0;
21 | bottom: 0;
22 | display: flex;
23 | justify-content: center;
24 | align-items: center;
25 | `
26 |
27 | const Icon = styled.img`
28 | width: 100%;
29 | height: 100%;
30 | padding: calc(${props => props.size} / 5);
31 | `
32 |
33 | const IconWithProgress = ({
34 | iconUrl = defaultIcon,
35 | size,
36 | thickness = 3,
37 | midValue,
38 | value,
39 | color,
40 |
41 | ...props
42 | }) => {
43 | return (
44 |
45 |
46 |
52 |
53 |
54 |
55 | )
56 | }
57 |
58 | export default IconWithProgress
59 |
--------------------------------------------------------------------------------
/src/components/shared/high/MarkdownArea.js:
--------------------------------------------------------------------------------
1 | import React, { useEffect } from 'react'
2 | import styled from 'styled-components'
3 | import TurndownService from 'turndown'
4 |
5 | import ReactQuill from 'react-quill'
6 | import 'react-quill/dist/quill.bubble.css'
7 |
8 | const turndownService = new TurndownService({
9 | headingStyle: 'atx',
10 | fence: '```'
11 | })
12 |
13 | const MarkdownArea = styled(ReactQuill)`
14 | border-radius: 0.5em;
15 | width: 100%;
16 |
17 | .ql-editor {
18 | padding: 1.5em;
19 | // min-height: 9em;
20 | // max-height: 18em;
21 | }
22 | `
23 |
24 | const MarkdownAreaInput = ({
25 | className,
26 | placeholder = 'Comments...',
27 | initialValue,
28 | onChange
29 | }) => {
30 | const convertHtmlToMarkdown = html => {
31 | return turndownService.turndown(html)
32 | }
33 |
34 | useEffect(() => {
35 | const className = 'ql-editor'
36 | Array.from(document.getElementsByClassName(className)).forEach(mai => {
37 | if (!mai.classList.contains('low-profile-scrollbar')) {
38 | mai.classList.add('low-profile-scrollbar', 'only-hover')
39 | }
40 | })
41 | }, [])
42 |
43 | return (
44 | {
51 | const markdown = convertHtmlToMarkdown(contents)
52 | onChange(markdown)
53 | }}
54 | />
55 | )
56 | }
57 |
58 | export default MarkdownAreaInput
59 |
--------------------------------------------------------------------------------
/src/components/shared/high/MuiIconFormatter.js:
--------------------------------------------------------------------------------
1 | import styled from 'styled-components'
2 |
3 | const MuiIconFormatter = styled.div.attrs(props => ({
4 | width: props.size ?? props.width
5 | }))`
6 | margin: 0 auto;
7 | padding: calc(${props => props.width} / 7.5);
8 | width: ${props => props.width};
9 | height: ${props => props.width};
10 | ${props =>
11 | props.circle
12 | ? 'border-radius: 50%;'
13 | : `border-radius: calc(${props.width} / 7.5);`}
14 |
15 | background-color: ${props => props.theme.accent};
16 | color: ${props => props.theme.fontInvert};
17 |
18 | > svg {
19 | width: 100%;
20 | height: 100%;
21 | }
22 | `
23 |
24 | export default MuiIconFormatter
25 |
--------------------------------------------------------------------------------
/src/components/shared/high/QuickAction.js:
--------------------------------------------------------------------------------
1 | import React, { useState } from 'react'
2 | import styled from 'styled-components'
3 |
4 | import ActionsIcon from '@material-ui/icons/RedoRounded'
5 |
6 | import DynamicModal from '../containers/DynamicModal'
7 | import Button from '../low/Button'
8 |
9 | const Container = styled.div`
10 | width: 100%;
11 | display: flex;
12 | flex-direction: column;
13 | align-items: center;
14 | justify-content: center;
15 | `
16 |
17 | const IconWrapper = styled.div`
18 | font-size: 200%;
19 | color: white;
20 | height: 1em;
21 | line-height: 1em;
22 | `
23 |
24 | const StyledButton = styled(Button)`
25 | font-size: 90%;
26 | padding: 0.6em 2em;
27 | `
28 |
29 | const QuickAction = ({
30 | action,
31 | title = 'Action',
32 | field = Would you like to perform this action?
,
33 | buttonText = 'Confirm',
34 | children
35 | }) => {
36 | const [open, setOpen] = useState(false)
37 | const [waiting, setWaiting] = useState(false) // waiting for response
38 |
39 | const triggerAction = async () => {
40 | try {
41 | setWaiting(true)
42 | await action()
43 | } catch (e) {
44 | console.log(e)
45 | } finally {
46 | setWaiting(false)
47 | }
48 | }
49 |
50 | return (
51 | <>
52 | setOpen(true)}>
53 | {children || (
54 |
55 |
56 |
57 | )}
58 |
59 | setOpen(false)}
62 | scaleX={0.6}
63 | scaleY={0.6}
64 | >
65 |
66 |
67 |
{title}
68 | {field}
69 |
70 | {buttonText}
71 |
72 |
73 |
74 |
75 | >
76 | )
77 | }
78 |
79 | export default QuickAction
80 |
--------------------------------------------------------------------------------
/src/components/shared/low/Button.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import styled from 'styled-components'
3 |
4 | const RenderedButton = styled.button.attrs(props => {
5 | if (props.disabled) {
6 | if (props.invert) {
7 | props.dark = '#666666'
8 | props.light = '#aaaaaa'
9 | } else {
10 | props.dark = '#aaaaaa'
11 | }
12 | }
13 | return {
14 | dark: props.dark || props.theme.accent
15 | }
16 | })`
17 | display: inline-block;
18 | position: relative;
19 | margin: ${props => (props.fullWidth ? '' : '0.5em')};
20 | padding: 0.75em 1.5em;
21 |
22 | ${props => (props.fullWidth ? 'width: 100%;' : '')}
23 | ${props =>
24 | props.rounder ? 'border-radius: 0.5em;' : 'border-radius: 0.25em;'}
25 |
26 | ${props =>
27 | !props.noOutline
28 | ? `border: ${props.dark} solid 0.1em;`
29 | : 'border: transparent;'}
30 |
31 | ${props => {
32 | if (props.invert) {
33 | return `
34 | background-color: ${props.dark}};
35 | color: ${props.light || '#fff'};
36 | ${props.disabled ? '' : `box-shadow: 0 4px 14px 0 ${props.dark}77;`}`
37 | } else {
38 | return `
39 | background-color: ${props.light || 'transparent'};
40 | color: ${props.dark};`
41 | }
42 | }}
43 |
44 | text-align: center;
45 | outline: none;
46 | white-space: nowrap;
47 | transition: 0.2s ease all;
48 | font-size: inherit;
49 | user-select: none;
50 |
51 | ${props => (props.disabled ? 'pointer-events: none;' : '')}
52 |
53 | &:hover {
54 | ${props => {
55 | if (props.invert) return 'filter: brightness(110%);'
56 | return `box-shadow: inset 0 0 100em 100em ${
57 | props.dark ? props.dark + '16' : props.theme.accent + '16'
58 | }`
59 | }}
60 | }
61 |
62 | &:active {
63 | ${props => {
64 | if (props.invert) return 'filter: brightness(120%);'
65 | return `box-shadow: inset 0 0 100em 100em ${
66 | props.dark ? props.dark + '32' : props.theme.accent + '32'
67 | }`
68 | }}
69 | }
70 | `
71 |
72 | /**
73 | * THICC BUTTON
74 | *
75 | * limitations: specify all colors with hexcode 6 digits
76 | * @param {*} props
77 | */
78 | const Button = props => {
79 | return (
80 |
92 | {props.children}
93 |
94 | )
95 | }
96 |
97 | export default Button
98 |
--------------------------------------------------------------------------------
/src/components/shared/low/ConfirmCancel.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import styled from 'styled-components'
3 |
4 | import Button from './Button'
5 |
6 | const Container = styled.div`
7 | margin: 0 auto;
8 | `
9 |
10 | const CancelButton = styled(Button)`
11 | border: 0;
12 | padding: 0.5em 1em 0.5em 0.5em;
13 | `
14 |
15 | const ConfirmButton = styled(Button)`
16 | padding: 0.5em 2em;
17 | `
18 |
19 | const ConfirmCancel = ({
20 | className,
21 | cancelText = 'Cancel',
22 | confirmText = 'Confirm',
23 | cancelProps,
24 | confirmProps,
25 | cancelOnClick,
26 | confirmOnClick
27 | }) => {
28 | return (
29 |
30 |
31 | {cancelText}
32 |
33 |
34 | {confirmText}
35 |
36 |
37 | )
38 | }
39 |
40 | export default ConfirmCancel
41 |
--------------------------------------------------------------------------------
/src/components/shared/low/DotIndicator.js:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bitprj/bit-frontend/d7450ca1e6c1c2c9b6d77cf806c8e92c81ff8956/src/components/shared/low/DotIndicator.js
--------------------------------------------------------------------------------
/src/components/shared/low/DotRating.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import styled from 'styled-components'
3 | import { isFunction } from 'lodash'
4 |
5 | const Container = styled.div`
6 | line-height: 1em;
7 | height: 1em;
8 |
9 | ${props =>
10 | props.fullWidth
11 | ? `display: flex;
12 | align-items: center;`
13 | : ''}
14 | `
15 |
16 | const Dot = styled.div`
17 | display: inline-block;
18 |
19 | &:not(:last-child) {
20 | margin-right: ${props => props.gap};
21 | }
22 |
23 | width: ${props => props.dotSize};
24 | height: ${props => props.dotSize};
25 | vertical-align: middle;
26 | ${props => (props.type !== 'SQUARE' ? 'border-radius: 0.3em' : '')}
27 | ${props => (props.fullWidth ? 'flex: 1;' : '')}
28 | ${props =>
29 | props.callback
30 | ? props.filled || props.offFilled
31 | ? 'cursor: pointer;'
32 | : 'cursor: default;'
33 | : ''}
34 |
35 | background-color: ${props => {
36 | if (props.filled) return props.filledColor || props.theme.accent
37 | if (props.offFilled)
38 | return props.offFilledColor || props.theme.accentVariant
39 | return props.offColor || props.theme.offFont
40 | }}
41 | `
42 |
43 | /**
44 | *
45 | * @param {filled} props
46 | */
47 | const Rating = ({
48 | style,
49 | className,
50 | type,
51 | fullWidth,
52 | rating = 3,
53 | offRating,
54 | outOf = 5,
55 | upTo = true,
56 | filledColor,
57 | offFilledColor,
58 | offColor,
59 | dotSize = '0.3em',
60 | gap = '0.3em',
61 | callback
62 | }) => {
63 | const renderedDots = [...Array(outOf)].map((_, index) => (
64 | isFunction(callback) && callback(index)}
77 | />
78 | ))
79 |
80 | return (
81 |
82 | {renderedDots}
83 |
84 | )
85 | }
86 |
87 | export default Rating
88 |
--------------------------------------------------------------------------------
/src/components/shared/low/Hero.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import styled from 'styled-components'
3 |
4 | import TwoPanel from '../containers/TwoPanel'
5 |
6 | const StyledTwoPanel = styled(TwoPanel)`
7 | background-color: ${props => props.theme.bgVariant};
8 | `
9 |
10 | const LeftPanel = styled.div`
11 | color: white;
12 | font-size: 85%;
13 | `
14 |
15 | const Hero = ({
16 | className,
17 | leftStyle,
18 |
19 | above,
20 | title,
21 | description,
22 | below,
23 |
24 | ratio,
25 | children
26 | }) => (
27 |
33 | {above}
34 | {title}
35 | {description}
36 | {below}
37 |
38 | }
39 | firstCenterBoth
40 | // firstStyle={{ overflow: 'visible' }}
41 | second={children}
42 | secondCenterX
43 | />
44 | )
45 |
46 | export default Hero
47 |
--------------------------------------------------------------------------------
/src/components/shared/low/Icon.js:
--------------------------------------------------------------------------------
1 | import styled from 'styled-components'
2 |
3 | const Icon = styled.img`
4 | ${props => {
5 | if (!props.sizeAuto) {
6 | return `width: ${props.width || '5em'};
7 | ${
8 | props.height
9 | ? `height: ${props.height}`
10 | : !props.width && !props.height
11 | ? 'height: 5em;'
12 | : ''
13 | }`
14 | }
15 | }}
16 |
17 |
18 | ${props => (props.center ? 'margin: 0 auto;' : '')}
19 |
20 | ${props => {
21 | if (props.sharp) {
22 | return 'border-radius: 0;'
23 | } else if (props.circle) {
24 | return 'border-radius: 50%;'
25 | } else {
26 | return 'border-radius: 1em;'
27 | }
28 | }}
29 |
30 | ${props =>
31 | props.noDefault || props.src
32 | ? ''
33 | : `background-color: ${props.theme.accentVariant};`}
34 | `
35 |
36 | export default Icon
37 |
--------------------------------------------------------------------------------
/src/components/shared/low/IconArea.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import styled from 'styled-components'
3 |
4 | const VerticalAlign = styled.div`
5 | display: flex;
6 | align-items: center;
7 | ${props => (props.reverse ? 'flex-direction: column-reverse;' : '')}
8 | `
9 |
10 | const IconWrapper = styled.div`
11 | ${props =>
12 | !props.reverse
13 | ? `margin-right: ${props.gap};`
14 | : `margin-left: ${props.gap};`}
15 | `
16 |
17 | const IconArea = ({
18 | className,
19 | children,
20 | icon,
21 | gap = '0.5em',
22 | reverse,
23 | onClick
24 | }) => {
25 | return (
26 |
27 |
28 | {icon}
29 |
30 | {children}
31 |
32 | )
33 | }
34 |
35 | export default IconArea
36 |
--------------------------------------------------------------------------------
/src/components/shared/low/IconLine.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import styled from 'styled-components'
3 |
4 | const Container = styled.span`
5 | line-height: 1em;
6 | `
7 |
8 | const IconWrapper = styled.div`
9 | ${props =>
10 | props.reverse
11 | ? `
12 | margin-left: ${props.gap || '0.2em'};`
13 | : `
14 | margin-right: ${props.gap || '0.2em'};`}
15 |
16 | display: inline-flex;
17 | align-self: center;
18 |
19 | > svg,
20 | > img {
21 | width: 1em;
22 | height: 1em;
23 | top: 0.125em;
24 | position: relative;
25 | font-size: inherit;
26 | ${props => (!props.noTransition ? 'transition: 0.1s ease all;' : '')}
27 | }
28 | `
29 |
30 | const IconLine = ({
31 | className,
32 | children,
33 | icon,
34 | gap = "0.5em",
35 | reverse,
36 | noTransition
37 | }) => {
38 | return (
39 |
40 | {!reverse ? (
41 | <>
42 |
43 | {icon}
44 |
45 | {children}
46 | >
47 | ) : (
48 | <>
49 | {children}
50 |
51 | {icon}
52 |
53 | >
54 | )}
55 |
56 | )
57 | }
58 |
59 | export default IconLine
60 |
--------------------------------------------------------------------------------
/src/components/shared/low/Interactive.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { PseudoBox } from '@chakra-ui/core'
3 | import styled from 'styled-components'
4 |
5 | const Rendered = styled(PseudoBox)`
6 | transition: 250ms all;
7 | cursor: pointer;
8 | outline: 0;
9 |
10 | &.interactive:focus {
11 | box-shadow: 0 0 0 3px ${props => props.theme.accentVariant};
12 | z-index: 999;
13 | }
14 | `
15 |
16 | const Interactive = ({ className, onKeyDown, onClick, ...props }) => {
17 | return (
18 | {
22 | switch (e.key) {
23 | case ' ':
24 | case 'Enter':
25 | e.preventDefault()
26 | onClick()
27 | default:
28 | break
29 | }
30 | }}
31 | onClick={onClick}
32 | {...props}
33 | />
34 | )
35 | }
36 |
37 | export default Interactive
38 |
--------------------------------------------------------------------------------
/src/components/shared/low/MediaLightbox.js:
--------------------------------------------------------------------------------
1 | import React, { useState, useRef, useEffect, useMemo, useCallback } from 'react'
2 | import styled from 'styled-components'
3 | import Lightbox, { Modal, ModalGateway } from 'react-images'
4 | import AspectRatio from 'react-aspect-ratio'
5 | import 'react-aspect-ratio/aspect-ratio.css'
6 |
7 | export const TYPE_IMAGE = 'image'
8 | export const TYPE_VIDEO = 'video'
9 |
10 | const Image = styled.img`
11 | max-height: 98%;
12 | `
13 |
14 | const Video = styled.video`
15 | max-height: 98%;
16 | `
17 |
18 | const MediaLightbox = ({
19 | type = TYPE_IMAGE,
20 | className,
21 | src,
22 |
23 | ratio
24 | }) => {
25 | const [open, setOpen] = useState(false)
26 |
27 | const mediaRef = useRef(null)
28 |
29 | const [mediaMaxWidth, setMediaMaxWidth] = useState()
30 | const [mediaAspectRatio, setMediaAspectRatio] = useState()
31 |
32 | useEffect(() => {
33 | if (ratio === undefined) return
34 |
35 | if (type === TYPE_IMAGE) {
36 | if (!mediaMaxWidth) {
37 | const width = mediaRef.current.node.clientWidth
38 | const mediaMaxWidth = width / ratio
39 | setMediaMaxWidth(mediaMaxWidth)
40 | }
41 |
42 | if (!setMediaAspectRatio) {
43 | const child = mediaRef.current.node.children[0]
44 | const { naturalWidth, naturalHeight } = child
45 | const mediaAspectRatio = naturalWidth / naturalHeight
46 | setMediaAspectRatio(mediaAspectRatio)
47 | }
48 | } else {
49 | if (!mediaMaxWidth) {
50 | setMediaMaxWidth('100%')
51 | }
52 | if (!mediaAspectRatio) {
53 | setMediaAspectRatio(ratio)
54 | }
55 | }
56 | })
57 |
58 | const selectMedia = () => {
59 | switch (type) {
60 | case TYPE_VIDEO:
61 | return (
62 |
65 | )
66 |
67 | case TYPE_IMAGE:
68 | default:
69 | return (
70 | setOpen(true)}
74 | />
75 | )
76 | }
77 | }
78 |
79 | return (
80 | <>
81 | {ratio ? (
82 |
90 | {selectMedia()}
91 |
92 | ) : (
93 | selectMedia()
94 | )}
95 |
96 | {open ? (
97 | setOpen(false)}>
98 |
103 |
104 | ) : null}
105 |
106 | >
107 | )
108 | }
109 |
110 | export default MediaLightbox
111 |
--------------------------------------------------------------------------------
/src/components/shared/low/ProfPic.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import Avatar from 'react-avatar'
3 | import { Stack, Box, Text } from '@chakra-ui/core'
4 |
5 | const ProfPic = ({ name, src, size = '1.6em' }) => {
6 | return (
7 |
8 |
9 |
10 |
11 |
12 | {name}
13 |
14 |
15 | )
16 | }
17 |
18 | export default ProfPic
19 |
--------------------------------------------------------------------------------
/src/components/shared/low/ProgressBar.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { Box } from '@chakra-ui/core'
3 |
4 | const ProgressBar = ({
5 | value = 69,
6 | midValue,
7 | color = 'theme.accent',
8 | midColor = 'theme.accentVariant',
9 | orientation = 'horizontal',
10 | reverse,
11 | ...props
12 | }) => {
13 | const isHorz = orientation === 'horizontal'
14 |
15 | const cappedValue = value > 100 ? 100 : value
16 | const cappedMidValue = midValue > 100 ? 100 : midValue
17 | return (
18 |
27 | {/* Mid */}
28 |
40 |
41 | {/* Load */}
42 |
54 |
55 | )
56 | }
57 |
58 | export default ProgressBar
59 |
--------------------------------------------------------------------------------
/src/components/shared/low/ProgressCircle.js:
--------------------------------------------------------------------------------
1 | import React, { useContext } from 'react'
2 | import styled, { ThemeContext } from 'styled-components'
3 |
4 | import CircularProgress from '@material-ui/core/CircularProgress'
5 |
6 | const Container = styled.div`
7 | width: ${props => props.size};
8 | height: ${props => props.size};
9 | `
10 |
11 | const Back = styled.div`
12 | position: absolute;
13 | z-index: 97;
14 | `
15 |
16 | const Mid = styled.div`
17 | position: absolute;
18 | z-index: 98;
19 | `
20 |
21 | const Front = styled.div`
22 | position: absolute;
23 | z-index: 99;
24 | `
25 |
26 | const ProgressCircle = ({
27 | className,
28 | size = '1em',
29 | value = 32,
30 | midValue = 69,
31 | thickness = 5,
32 | ...props
33 | }) => {
34 | const themeContext = useContext(ThemeContext)
35 | const parsedThickness = thickness === '100%' ? 22 : thickness
36 |
37 | return (
38 |
39 |
40 |
47 |
48 |
49 |
50 |
57 |
58 |
59 |
60 |
67 |
68 |
69 | )
70 | }
71 |
72 | export default ProgressCircle
73 |
--------------------------------------------------------------------------------
/src/components/shared/low/StatusIcon.js:
--------------------------------------------------------------------------------
1 | import React, { useContext } from 'react'
2 | import styled from 'styled-components'
3 | import { ThemeContext } from 'styled-components'
4 |
5 | import CheckIcon from '@material-ui/icons/CheckRounded'
6 | import ThreeDotsIcon from '@material-ui/icons/MoreHoriz'
7 | import LockIcon from '@material-ui/icons/LockRounded'
8 |
9 | import MuiIconFormatter from '../high/MuiIconFormatter'
10 |
11 | const selectColor = props => {
12 | switch (props.type) {
13 | case 'complete':
14 | return props.theme.pastel.green
15 | case 'incomplete':
16 | return props.theme.pastel.yellow
17 | case 'locked':
18 | return props.theme.pastel.red
19 |
20 | default:
21 | return null
22 | }
23 | }
24 |
25 | const Container = styled(MuiIconFormatter)`
26 | background-color: ${props => selectColor(props)};
27 | `
28 |
29 | /**
30 | * Helper Class to choose Status Icon appropriately
31 | * @param {status} props
32 | */
33 | const StatusIcon = ({ type, width = '2.25em' }) => {
34 | const themeContext = useContext(ThemeContext)
35 |
36 | const selectIcon = () => {
37 | switch (type) {
38 | case 'complete':
39 | return
40 | case 'incomplete':
41 | return
42 | case 'locked':
43 | return
44 |
45 | default:
46 | return null
47 | }
48 | }
49 |
50 | return (
51 |
52 | {selectIcon()}
53 |
54 | )
55 | }
56 |
57 | export default StatusIcon
58 |
--------------------------------------------------------------------------------
/src/components/shared/low/ThreeCheckbox.js:
--------------------------------------------------------------------------------
1 | import React, { useState, useEffect, useRef } from 'react'
2 | import styled from 'styled-components'
3 | import anime from 'animejs'
4 |
5 | import Pass from '@material-ui/icons/Check'
6 | import Fail from '@material-ui/icons/Close'
7 | import None from '@material-ui/icons/CheckBoxOutlineBlank'
8 |
9 | const selectColor = props => {
10 | switch (props.state) {
11 | case true: {
12 | return props.theme.pastel.green
13 | }
14 | case false: {
15 | return props.theme.pastel.red
16 | }
17 | default:
18 | return props.theme.offFont
19 | }
20 | }
21 |
22 | const Container = styled.button`
23 | padding: 0.3em;
24 | border-radius: 50%;
25 |
26 | display: flex;
27 | justify-content: center;
28 | align-items: center;
29 |
30 | color: white;
31 | border: 0.1em solid transparent;
32 | background-color: ${props => selectColor(props)};
33 | box-shadow: 0 4px 1.5em ${props => selectColor(props)}88;
34 | cursor: pointer;
35 | font-size: 170%;
36 | outline: 0;
37 |
38 | transition: background-color 0.2s ease, box-shadow 0.2s ease;
39 |
40 | :focus {
41 | border: 0.1em solid
42 | ${props => {
43 | if (props.state === 'NONE') return '#00000001'
44 | return `${selectColor(props)}66`
45 | }};
46 | }
47 | `
48 |
49 | /**
50 | * Uses three states as opposed to the traditional two-state checkbox
51 | * - true, false, undefined
52 | *
53 | * @param {*} param0
54 | */
55 | const ThreeCheckbox = ({
56 | size,
57 | initialState,
58 | onChange = v => console.log(v)
59 | }) => {
60 | const containerRef = useRef(null)
61 |
62 | const [state, setState] = useState(initialState)
63 |
64 | useEffect(() => {
65 | anime({
66 | targets: containerRef.current,
67 | scale: [0.8, 1],
68 | easing: 'easeOutElastic()',
69 | duration: 750
70 | })
71 | onChange(state)
72 | }, [state])
73 |
74 | const handleNextState = () => {
75 | switch (state) {
76 | case true: {
77 | setState(false)
78 | break
79 | }
80 | case false: {
81 | setState(undefined)
82 | break
83 | }
84 | default:
85 | setState(true)
86 | break
87 | }
88 | }
89 |
90 | const selectState = () => {
91 | switch (state) {
92 | case true: {
93 | return
94 | }
95 | case false: {
96 | return
97 | }
98 | default:
99 | return
100 | }
101 | }
102 |
103 | return (
104 |
110 | {selectState()}
111 |
112 | )
113 | }
114 |
115 | export default ThreeCheckbox
116 |
--------------------------------------------------------------------------------
/src/components/shared/unused/Button.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import styled from "styled-components";
3 |
4 | import CheckIcon from "../../../assets/icons/check";
5 |
6 | const RenderedButton = styled.div`
7 | display: inline-block;
8 | cursor: pointer;
9 | text-decoration: none;
10 | padding: 0.5em 1em;
11 | margin: 1em 0.5em;
12 | border-radius: 7px;
13 | color: #0070f3;
14 | background-color: transparent;
15 | border: none;
16 | font-size: inherit;
17 | line-height: inherit;
18 | transition: background 0.2s ease, color 0.2s ease, box-shadow 0.2s ease;
19 | text-align: center;
20 | border: 1px solid transparent;
21 |
22 | &:hover {
23 | color: #0070f3;
24 | background: rgba(0, 118, 255, 0.1);
25 | }
26 |
27 | &.invert {
28 | border-radius: 7px;
29 | background-color: #0070f3;
30 | box-shadow: 0 4px 14px 0 rgba(0, 118, 255, 0.39);
31 | color: white;
32 | }
33 |
34 | &.invert:hover {
35 | background: rgba(0, 118, 255, 0.9);
36 | box-shadow: 0 6px 20px rgba(0, 118, 255, 0.23);
37 | }
38 |
39 | &.invert:active {
40 | background: #006ae6;
41 | }
42 |
43 | &.outline {
44 | border: 1px #0070f3 solid;
45 | }
46 |
47 | &.less-round {
48 | border-radius: 3.5px;
49 | }
50 | `;
51 |
52 | const Button = props => {
53 | switch (props.buttonState) {
54 | case "Check":
55 | return ;
56 | case "NextHint":
57 | return (
58 | props.click(props.index)}
61 | >
62 | >
63 |
64 | );
65 | case "PrevCard":
66 | const prev = "< Prev";
67 | return (
68 | props.moveToPrev(-1)}
71 | >
72 | {prev}
73 |
74 | );
75 | case "NextCard":
76 | return (
77 | props.moveToNext(1)}
80 | >
81 | Next >
82 |
83 | );
84 | case "":
85 | return true;
86 |
87 | default:
88 | return (
89 |
90 | {props.buttonState}
91 |
92 | );
93 | }
94 | };
95 |
96 | export default Button;
97 |
--------------------------------------------------------------------------------
/src/components/shared/unused/Card.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import styled from 'styled-components';
3 |
4 | const RenderedCard = styled.div`
5 | position: relative;
6 | display: flex;
7 | flex-direction: column;
8 | justify-content: space-between;
9 | padding: 1rem;
10 | border-radius: 7px;
11 | box-shadow: 0 2px 10px rgba(0, 0, 0, 0.12);
12 | overflow: hidden;
13 | `
14 |
15 | const Card = (props) => {
16 | return (
17 |
18 | {props.content}
19 |
20 | )
21 | }
22 |
23 | export default Card;
--------------------------------------------------------------------------------
/src/components/shared/utils/ClampedDiv.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import styled from 'styled-components'
3 |
4 | const Container = styled.div`
5 | ${props => (props.inline ? 'display: inline-block;' : '')}
6 | width: ${props => props.width || 'fit-to-content'};
7 | white-space: nowrap;
8 | overflow: hidden;
9 | text-overflow: ellipsis;
10 | `
11 |
12 | const ClampedDiv = ({ children, className, width, inline }) => {
13 | return (
14 |
15 | {children}
16 |
17 | )
18 | }
19 |
20 | export default ClampedDiv
21 |
--------------------------------------------------------------------------------
/src/components/shared/utils/ClampedText.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import styled from 'styled-components'
3 |
4 | const Container = styled.span`
5 | display: -webkit-box;
6 | -webkit-box-orient: vertical;
7 | -webkit-line-clamp: ${props => props.clamp || 2};
8 | overflow: hidden;
9 | `
10 |
11 | const ClampedText = ({ children, className, clamp }) => {
12 | return (
13 |
14 | {children}
15 |
16 | )
17 | }
18 |
19 | export default ClampedText
20 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import ReactDOM from 'react-dom'
3 | import { Provider as ReduxProvider } from 'react-redux'
4 |
5 | import store from './redux/store'
6 | import App from './App'
7 |
8 | ReactDOM.render(
9 |
10 |
11 | ,
12 | document.getElementById('root')
13 | )
14 |
--------------------------------------------------------------------------------
/src/redux/actionTypes.js:
--------------------------------------------------------------------------------
1 | /**
2 | * account
3 | */
4 | export const AUTHENTICATE = 'AUTHENTICATE'
5 | export const DEAUTHENTICATE = 'DEAUTHENTICATE'
6 | export const SET_USER_DATA = 'SET_USER_DATA'
7 |
8 | /**
9 | * cache
10 | */
11 | export const SAVE_TO_CACHE = 'SAVE_TO_CACHE'
12 |
13 | /**
14 | * studentData
15 | */
16 | export const RESET_STUDENT_DATA = 'RESET_STUDENT_DATA'
17 | export const SAVE_TO_PROGRESS = 'SAVE_TO_PROGRESS'
18 | export const SET_STUDENT_DATA = 'SET_STUDENT_DATA'
19 | export const SET_CURRENT_TRACK = 'SET_CURRENT_TRACK'
20 | export const SET_CURRENT_TOPIC = 'SET_CURRENT_TOPIC'
21 | export const SET_SUGGESTED_ACTIVITY = 'SET_SUGGESTED_ACTIVITY'
22 |
23 | export const INCREMENT_GEMS_BY = 'INCREMENT_GEMS_BY'
24 |
25 | /**
26 | * teacherData
27 | */
28 | /* initialization processes */
29 | export const SET_SUBMISSIONS = 'SET_SUBMISSIONS'
30 |
31 | /* runtime processes */
32 | export const UPDATE_FEEDBACKS = 'UPDATE_FEEDBACKS'
33 | export const SET_CURRENT_CLASSROOM_BY_INDEX = 'SET_CURRENT_CLASSROOM_BY_INDEX'
34 | export const SET_CURRENT_SUBMISSION_BY_INDEX = 'SET_CURRENT_SUBMISSION_BY_INDEX'
35 |
36 | /**
37 | * learnData
38 | */
39 | export const RESET_LEARN_DATA = 'RESET_LEARN_DATA'
40 | export const SET_SELECTED_ACTIVITY = 'SET_SELECTED_ACTIVITY'
41 | export const SET_INDICATORS = 'SET_INDICATORS'
42 |
43 | export const SET_CURRENT_CARD_BY_INDEX = 'SET_CURRENT_CARD_BY_INDEX'
44 | export const INCREMENT_CURRENT_CARD_INDEX = 'INCREMENT_CURRENT_CARD_INDEX'
45 | export const SET_LAST_CARD_UNLOCKED_INDEX_BY_ID =
46 | 'SET_LAST_CARD_UNLOCKED_INDEX_BY_ID'
47 | export const INCREMENT_LAST_CARD_UNLOCKED_INDEX =
48 | 'INCREMENT_LAST_CARD_UNLOCKED_INDEX'
49 |
50 | export const BROADCAST_BUTTON_STATE = 'BROADCAST_BUTTON_STATE'
51 | export const SCHEDULE_BUTTON_STATE = 'SCHEDULE_BUTTON_STATE'
52 | export const RESET_BUTTON_STATE_SCHEDULE = 'RESET_BUTTON_STATE_SCHEDULE'
53 |
54 | /**
55 | * theme
56 | */
57 | export const SET_THEME = 'SET_THEME'
58 |
--------------------------------------------------------------------------------
/src/redux/actions/account.js:
--------------------------------------------------------------------------------
1 | import { AUTHENTICATE, DEAUTHENTICATE, SET_USER_DATA } from '../actionTypes'
2 | import { fetchUserData } from '../../services/AccountService'
3 |
4 | export const initUserData = userId => async dispatch => {
5 | const userData = await fetchUserData(userId)
6 |
7 | const validatedName =
8 | userData.name === 'None' ? userData.githubUsername : userData.name
9 |
10 | dispatch({
11 | type: SET_USER_DATA,
12 | userData: { ...userData, name: validatedName }
13 | })
14 | }
15 |
16 | export const authenticate = meta => {
17 | localStorage.setItem('meta', JSON.stringify(meta))
18 | return {
19 | type: AUTHENTICATE,
20 | meta
21 | }
22 | }
23 |
24 | export const deauthenticate = () => {
25 | localStorage.removeItem('meta')
26 |
27 | if (window.location.pathname !== '/') {
28 | console.log('ok')
29 | window.location.replace('/')
30 | }
31 |
32 | return {
33 | type: DEAUTHENTICATE
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/src/redux/actions/cache.js:
--------------------------------------------------------------------------------
1 | import { SAVE_TO_CACHE } from '../actionTypes'
2 |
3 | /**
4 | *
5 | * @param {string} cacheType
6 | * @param {object} newLoads { [id1]: {...}, [id2]: {...} }
7 | * @param {object} options { overwrite: bool, ... }
8 | */
9 | export const saveToCache = (cacheType, newLoads, options) => {
10 | const initialOptions = {
11 | merge: false // if false, newLoads will not overwrite prexisting ones
12 | }
13 |
14 | return {
15 | type: SAVE_TO_CACHE,
16 | cacheType,
17 | newLoads,
18 | options: options ?? initialOptions
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/src/redux/actions/studentData.js:
--------------------------------------------------------------------------------
1 | import {
2 | RESET_STUDENT_DATA,
3 | SET_STUDENT_DATA,
4 | INCREMENT_GEMS_BY
5 | } from '../actionTypes'
6 |
7 | import {
8 | fetchStudentData,
9 | setChosenProjects
10 | } from '../../services/StudentService'
11 | import { setSelectedActivity } from './learnData'
12 | import { saveToCache } from './cache'
13 | import { CACHE_MODULE_PROGRESS } from '../../components/HOC/WithApiCache'
14 |
15 | /* ===== INITIALIZATION */
16 |
17 | export const init = studentId => async dispatch => {
18 | dispatch({
19 | type: RESET_STUDENT_DATA
20 | })
21 |
22 | const studentData = await fetchStudentData(studentId)
23 |
24 | /**
25 | * TODO TEMPORARY CODE TEMP WARN
26 | */
27 | let inprogressModules = [...studentData.inprogressModules]
28 | if (studentData.incompleteModules.length) {
29 | inprogressModules = inprogressModules.concat(studentData.incompleteModules)
30 | }
31 |
32 | dispatch(setStudentData({ ...studentData, inprogressModules }))
33 |
34 | // external
35 | dispatch(setSelectedActivity(studentData.suggestedActivity))
36 | }
37 |
38 | const setStudentData = studentData => ({
39 | type: SET_STUDENT_DATA,
40 | studentData
41 | })
42 |
43 | /**
44 | *
45 | * @param {int} moduleId
46 | * @param {int} id
47 | * @param {string} actionType inprogress | completed
48 | */
49 | export const updateModuleActivityProgress = (moduleId, id, actionType) => ({
50 | type: 'UPDATE_MODULE_ACTIVITY_PROGRESS',
51 | moduleId,
52 | id,
53 | actionType
54 | })
55 |
56 | // ===== RUNTIME
57 |
58 | export const chooseProjects = (moduleId, projects) => dispatch => {
59 | dispatch(
60 | saveToCache(
61 | CACHE_MODULE_PROGRESS,
62 | { [moduleId]: { chosenProjects: projects } },
63 | { merge: true }
64 | )
65 | )
66 |
67 | setChosenProjects(moduleId, projects).then(_ => console.log(_.message))
68 | }
69 |
70 | export const incrementGemsBy = gemAmount => ({
71 | type: INCREMENT_GEMS_BY,
72 | gemAmount
73 | })
74 |
--------------------------------------------------------------------------------
/src/redux/actions/teacherData.js:
--------------------------------------------------------------------------------
1 | import {
2 | SET_SUBMISSIONS,
3 | UPDATE_FEEDBACKS,
4 | SET_CURRENT_CLASSROOM_BY_INDEX,
5 | SET_CURRENT_SUBMISSION_BY_INDEX
6 | } from '../actionTypes'
7 |
8 | import {
9 | fetchTeacherData,
10 | fetchClassroom,
11 | fetchSubmissionsAll
12 | } from '../../services/TeacherService'
13 |
14 | /* ===== INITIALIZATION */
15 | export const init = teacherId => async dispatch => {
16 | const teacherData = await fetchTeacherData(teacherId)
17 | const classroomId = teacherData.classrooms[0].id
18 |
19 | const [classroom, submissions] = await Promise.all([
20 | fetchClassroom(classroomId),
21 | fetchSubmissionsAll(classroomId)
22 | ])
23 |
24 | console.log(classroom, submissions)
25 | dispatch(setSubmissions(submissions))
26 | }
27 |
28 | const setSubmissions = submissions => ({
29 | type: SET_SUBMISSIONS,
30 | submissions
31 | })
32 |
33 | /* ===== RUNTIME */
34 | export const updateFeedbacks = (studentId, checkpointId, feedbackChanges) => ({
35 | type: UPDATE_FEEDBACKS,
36 | studentId,
37 | checkpointId,
38 | feedbackChanges
39 | })
40 |
41 | export const setCurrentClassroomByIndex = classroomIndex => ({
42 | type: SET_CURRENT_CLASSROOM_BY_INDEX,
43 | classroomIndex
44 | })
45 |
46 | export const setCurrentSubmissionByIndex = submissionIndex => ({
47 | type: SET_CURRENT_SUBMISSION_BY_INDEX,
48 | submissionIndex
49 | })
50 |
--------------------------------------------------------------------------------
/src/redux/actions/theme.js:
--------------------------------------------------------------------------------
1 | import { SET_THEME } from "../actionTypes";
2 |
3 | export const setTheme = theme => {
4 | return { type: SET_THEME, theme };
5 | };
6 |
7 | export default setTheme;
8 |
--------------------------------------------------------------------------------
/src/redux/reducers/account.js:
--------------------------------------------------------------------------------
1 | import { AUTHENTICATE, DEAUTHENTICATE, SET_USER_DATA } from '../actionTypes'
2 |
3 | const storedMeta = localStorage.getItem('meta')
4 |
5 | const initialState = {
6 | meta: JSON.parse(storedMeta),
7 | user: null
8 | }
9 |
10 | const reducer = (state = initialState, action) => {
11 | switch (action.type) {
12 | case AUTHENTICATE:
13 | return {
14 | ...state,
15 | meta: action.meta
16 | }
17 |
18 | case DEAUTHENTICATE:
19 | return {}
20 |
21 | case SET_USER_DATA:
22 | return {
23 | ...state,
24 | user: action.userData
25 | }
26 |
27 | default:
28 | return state
29 | }
30 | }
31 |
32 | export default reducer
33 |
--------------------------------------------------------------------------------
/src/redux/reducers/cache.js:
--------------------------------------------------------------------------------
1 | import { cloneDeep, mergeWith as mergeDeep } from 'lodash'
2 | import { SAVE_TO_CACHE } from '../actionTypes'
3 | import * as cacheTypes from '../../components/HOC/WithApiCache'
4 |
5 | const initialState = {
6 | [cacheTypes.CACHE_TOPIC]: {},
7 | [cacheTypes.CACHE_MODULE]: {},
8 | [cacheTypes.CACHE_ACTIVITY]: {},
9 | [cacheTypes.CACHE_CARD]: {},
10 | [cacheTypes.CACHE_CHECKPOINT]: {},
11 | [cacheTypes.CACHE_CONCEPT]: {},
12 | [cacheTypes.CACHE_HINT]: {},
13 |
14 | [cacheTypes.CACHE_USER]: {},
15 | [cacheTypes.CACHE_STUDENT]: {},
16 | [cacheTypes.CACHE_MODULE_PROGRESS]: {},
17 | [cacheTypes.CACHE_ACTIVITY_PROGRESS]: {},
18 | [cacheTypes.CACHE_HINT_PROGRESS]: {},
19 | [cacheTypes.CACHE_CHECKPOINTS_PROGRESS]: {}
20 | }
21 |
22 | const reducer = (state = initialState, action) => {
23 | switch (action.type) {
24 | case SAVE_TO_CACHE: {
25 | const { merge } = action.options
26 |
27 | return {
28 | ...state,
29 | [action.cacheType]: merge
30 | ? mergeDeep(
31 | cloneDeep(state[action.cacheType]),
32 | cloneDeep(action.newLoads),
33 | (objValue, srcValue) => {
34 | if (Array.isArray(objValue) && Array.isArray(srcValue)) {
35 | return srcValue
36 | }
37 | }
38 | )
39 | : {
40 | ...action.newLoads,
41 | ...state[action.cacheType]
42 | }
43 | }
44 | }
45 |
46 | case 'UPDATE_MODULE_ACTIVITY_PROGRESS': {
47 | const modu1e = state[cacheTypes.CACHE_MODULE_PROGRESS][action.moduleId]
48 | if (!modu1e) return state
49 |
50 | const {
51 | incompleteActivities,
52 | inprogressActivities,
53 | completedActivities
54 | } = modu1e
55 |
56 | let final = modu1e
57 | if (action.actionType === 'inprogress') {
58 | final = {
59 | ...final,
60 | incompleteActivities: incompleteActivities?.filter(
61 | i => i.id !== action.id
62 | ),
63 | inprogressActivities: (inprogressActivities ?? []).concat([
64 | { id: action.id }
65 | ])
66 | }
67 | } else if (action.actionType === 'completed') {
68 | final = {
69 | ...final,
70 | inprogressActivities: inprogressActivities.filter(
71 | i => i.id !== action.id
72 | ),
73 | completedActivities: (completedActivities ?? []).concat([
74 | { id: action.id }
75 | ])
76 | }
77 | }
78 |
79 | return {
80 | ...state,
81 | [cacheTypes.CACHE_MODULE_PROGRESS]: {
82 | ...state[cacheTypes.CACHE_MODULE_PROGRESS],
83 | [action.moduleId]: final
84 | }
85 | }
86 | }
87 |
88 | default:
89 | return state
90 | }
91 | }
92 |
93 | export default reducer
94 |
--------------------------------------------------------------------------------
/src/redux/reducers/studentData.js:
--------------------------------------------------------------------------------
1 | import {
2 | RESET_STUDENT_DATA,
3 | SAVE_TO_PROGRESS,
4 | SET_STUDENT_DATA,
5 | SET_CURRENT_TRACK,
6 | SET_CURRENT_TOPIC,
7 | SET_SUGGESTED_ACTIVITY,
8 | INCREMENT_GEMS_BY
9 | } from '../actionTypes'
10 | import * as cacheTypes from '../../components/HOC/WithApiCache'
11 |
12 | const initialState = {
13 | suggestedActivity: null,
14 | inprogressModules: [],
15 | inprogressTopics: [],
16 | gems: 860,
17 |
18 | [cacheTypes.CACHE_MODULE_PROGRESS]: {},
19 | [cacheTypes.CACHE_ACTIVITY_PROGRESS]: {},
20 | [cacheTypes.CACHE_HINT_PROGRESS]: {},
21 | [cacheTypes.CACHE_CHECKPOINTS_PROGRESS]: {}
22 | }
23 |
24 | const reducer = (state = initialState, action) => {
25 | switch (action.type) {
26 | case RESET_STUDENT_DATA: {
27 | return initialState
28 | }
29 |
30 | case SAVE_TO_PROGRESS: {
31 | return state
32 | }
33 |
34 | case SET_STUDENT_DATA: {
35 | return {
36 | ...initialState,
37 | ...action.studentData
38 | }
39 | }
40 |
41 | case SET_CURRENT_TRACK: {
42 | return {
43 | ...state,
44 | current_track: { ...action.currentTrack }
45 | }
46 | }
47 |
48 | case SET_CURRENT_TOPIC: {
49 | return {
50 | ...state,
51 | current_topic: { ...action.currentTopic }
52 | }
53 | }
54 |
55 | case SET_SUGGESTED_ACTIVITY: {
56 | return {
57 | ...state,
58 | suggested_activity: { ...action.suggestedActivity }
59 | }
60 | }
61 |
62 | case INCREMENT_GEMS_BY: {
63 | return {
64 | ...state,
65 | gems: state.gems + action.gemAmount
66 | }
67 | }
68 |
69 | default:
70 | return state
71 | }
72 | }
73 |
74 | export default reducer
75 |
--------------------------------------------------------------------------------
/src/redux/reducers/teacherData.js:
--------------------------------------------------------------------------------
1 | import {
2 | SET_SUBMISSIONS,
3 | UPDATE_FEEDBACKS,
4 | SET_CURRENT_CLASSROOM_BY_INDEX,
5 | SET_CURRENT_SUBMISSION_BY_INDEX
6 | } from '../actionTypes'
7 |
8 | const initialState = {
9 | indicators: {
10 | currentClassroomIndex: 0,
11 | currentSubmissionIndex: 1
12 | },
13 | submissions: [],
14 |
15 | ram: {
16 | feedbacks: {
17 | // student1_checkpoint23: {
18 | // checkpoint_id: '69',
19 | // is_passed: 'true',
20 | // comment: 'I am a comment'
21 | // }
22 | }
23 | }
24 | }
25 |
26 | const reducer = (state = initialState, action) => {
27 | switch (action.type) {
28 | case SET_SUBMISSIONS: {
29 | return {
30 | ...state,
31 | submissions: action.submissions
32 | }
33 | }
34 |
35 | case UPDATE_FEEDBACKS: {
36 | const { studentId, checkpointId, feedbackChanges } = action
37 | const feedbackKey = `student${studentId}_checkpoint${checkpointId}`
38 |
39 | return {
40 | ...state,
41 | ram: {
42 | ...state.ram,
43 | feedbacks: {
44 | ...state.ram.feedbacks,
45 | [feedbackKey]: {
46 | ...state.ram.feedbacks?.[feedbackKey],
47 | ...feedbackChanges
48 | }
49 | }
50 | }
51 | }
52 | }
53 |
54 | case SET_CURRENT_CLASSROOM_BY_INDEX: {
55 | return {
56 | ...state,
57 | indicators: {
58 | ...state.indiciators,
59 | currentClassroomIndex: action.classroomIndex
60 | }
61 | }
62 | }
63 |
64 | case SET_CURRENT_SUBMISSION_BY_INDEX: {
65 | return {
66 | ...state,
67 | indicators: {
68 | ...state.indicators,
69 | currentSubmissionIndex: action.submissionIndex
70 | }
71 | }
72 | }
73 |
74 | default:
75 | return state
76 | }
77 | }
78 |
79 | export default reducer
80 |
--------------------------------------------------------------------------------
/src/redux/reducers/theme.js:
--------------------------------------------------------------------------------
1 | import { SET_THEME } from '../actionTypes'
2 | import defaultTheme from '../../styles/theme'
3 |
4 | const initialState = defaultTheme
5 |
6 | const theme = (state = initialState, action) => {
7 | if (action.type === SET_THEME) {
8 | return action.theme
9 | }
10 | return state
11 | }
12 |
13 | export default theme
14 |
--------------------------------------------------------------------------------
/src/redux/rootReducer.js:
--------------------------------------------------------------------------------
1 | import { combineReducers } from 'redux'
2 | import account from './reducers/account'
3 | import learnData from './reducers/learnData'
4 | import cache from './reducers/cache'
5 | import studentData from './reducers/studentData'
6 | import teacherData from './reducers/teacherData'
7 | import theme from './reducers/theme'
8 |
9 | export default combineReducers({
10 | account,
11 | learnData,
12 | cache,
13 | studentData,
14 | teacherData,
15 | theme
16 | })
17 |
--------------------------------------------------------------------------------
/src/redux/store.js:
--------------------------------------------------------------------------------
1 | import { createStore, applyMiddleware, compose } from 'redux'
2 | import thunk from 'redux-thunk'
3 |
4 | import rootReducer from './rootReducer'
5 |
6 | import * as accountActions from './actions/account'
7 | import * as learnDataActions from './actions/learnData'
8 | import * as cacheActions from './actions/cache'
9 | import * as teacherDataActions from './actions/teacherData'
10 | import * as studentDataActions from './actions/studentData'
11 | import * as themeActions from './actions/theme'
12 | const actionCreators = {
13 | ...accountActions,
14 | ...learnDataActions,
15 | ...cacheActions,
16 | ...teacherDataActions,
17 | ...studentDataActions,
18 | ...themeActions
19 | }
20 |
21 | const configureStore = initialState => {
22 | const composeEnhancers =
23 | typeof window === 'object' && window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__
24 | ? window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({
25 | actionCreators,
26 | trace: true,
27 | traceLimit: 25
28 | })
29 | : compose
30 |
31 | const middleware = [thunk]
32 |
33 | console.log(process.env.NODE_ENV)
34 | if (process.env.NODE_ENV !== 'production')
35 | middleware.unshift(
36 | require('redux-immutable-state-invariant').default({
37 | ignore: ['learnData.currentCardUnnestedUnlockedHintRefs']
38 | })
39 | )
40 |
41 | const store = createStore(
42 | rootReducer,
43 | initialState,
44 | composeEnhancers(applyMiddleware(...middleware))
45 | )
46 |
47 | // Enable Webpack hot module replacement for reducers
48 | // if (module.hot) {
49 | // module.hot.accept('./reducers', () =>
50 | // store.replaceReducer(require('./reducers'))
51 | // )
52 | // }
53 |
54 | return store
55 | }
56 |
57 | const store = configureStore()
58 |
59 | export default store
60 |
--------------------------------------------------------------------------------
/src/services/AccountService.js:
--------------------------------------------------------------------------------
1 | import { baseUrl, backend } from './AxiosInstances'
2 |
3 | export const fetchMetaData = () => {
4 | const endpoint = '/meta'
5 | return backend.get(endpoint)
6 | }
7 |
8 | export const fetchUserData = userId => {
9 | const endpoint = `/users/${userId}`
10 | return backend.get(endpoint)
11 | }
12 |
13 | export const login = () => {
14 | window.location.href = `${baseUrl}/login`
15 | // const endpoint = '/login'
16 | // return backend.get(endpoint)
17 | }
18 |
19 | export const logout = () => {
20 | const endpoint = '/logout'
21 | return backend.get(endpoint)
22 | }
23 |
--------------------------------------------------------------------------------
/src/services/Auth0Service.js:
--------------------------------------------------------------------------------
1 | import auth0 from 'auth0-js'
2 |
3 | class Auth {
4 | constructor() {
5 | this.auth0 = new auth0.WebAuth({
6 | domain: '',
7 | audience: 'https:///userinfo',
8 | clientID: '',
9 | redirectUri: 'http://localhost:3000/callback',
10 | responseType: 'id_token',
11 | scope: 'openid profile'
12 | })
13 |
14 | this.getProfile = this.getProfile.bind(this)
15 | this.handleAuthentication = this.handleAuthentication.bind(this)
16 | this.isAuthenticated = this.isAuthenticated.bind(this)
17 | this.signIn = this.signIn.bind(this)
18 | this.signOut = this.signOut.bind(this)
19 | }
20 |
21 | getProfile() {
22 | return this.profile
23 | }
24 |
25 | getIdToken() {
26 | return this.idToken
27 | }
28 |
29 | isAuthenticated() {
30 | return new Date().getTime() < this.expiresAt
31 | }
32 |
33 | signIn() {
34 | this.auth0.authorize()
35 | }
36 |
37 | handleAuthentication() {
38 | return new Promise((resolve, reject) => {
39 | this.auth0.parseHash((err, authResult) => {
40 | if (err) return reject(err)
41 | if (!authResult || !authResult.idToken) {
42 | return reject(err)
43 | }
44 | this.idToken = authResult.idToken
45 | this.profile = authResult.idTokenPayload
46 | // set the time that the id token will expire at
47 | this.expiresAt = authResult.idTokenPayload.exp * 1000
48 | resolve()
49 | })
50 | })
51 | }
52 |
53 | signOut() {
54 | // clear id token, profile, and expiration
55 | this.idToken = null
56 | this.profile = null
57 | this.expiresAt = null
58 | }
59 | }
60 |
61 | const auth0Client = new Auth()
62 |
63 | export default auth0Client
64 |
--------------------------------------------------------------------------------
/src/services/AxiosInstances.js:
--------------------------------------------------------------------------------
1 | import axios from 'axios'
2 | import camelCase from 'camelcase-keys-deep'
3 |
4 | import { deauthenticate } from '../redux/actions/account'
5 | import store from '../redux/store'
6 |
7 | /** GENERAL BACKEND (mainly for GET) */
8 |
9 | const backendResponseInterceptor = error => {
10 | if (!error.response) {
11 | throw Error(
12 | 'A so called "CORS" error likely occurred. The request bounced.'
13 | )
14 | }
15 | const {
16 | status,
17 | statusText,
18 | config: { method, url },
19 | data: { message, msg }
20 | } = error.response
21 |
22 | if (status === 401) {
23 | store.dispatch(deauthenticate())
24 |
25 | // [WithAuthentication] continue error chain to deauthenticate
26 | if (!localStorage.getItem('meta')) throw error
27 |
28 | return error
29 | }
30 |
31 | if (message !== 'Card already unlocked')
32 | alert(`${method.toUpperCase()} ${url}
33 | ${status} (${statusText})
34 | ${message ?? msg ?? ''}`)
35 | return error
36 | }
37 |
38 | export const baseUrl = 'https://wongband.pythonanywhere.com/'
39 | // export const baseUrl = 'https://bit-backend-staging.herokuapp.com/'
40 | // const baseUrl = 'https://darlene-backend.herokuapp.com/'
41 | // export const baseUrl = 'https://214509c7.ngrok.io'
42 | // const baseUrl = 'http://localhost:5000/'
43 |
44 | export const backend = axios.create({
45 | baseURL: baseUrl,
46 | withCredentials: true
47 | })
48 | backend.interceptors.response.use(
49 | response => camelCase(response.data, { deep: true }),
50 | error => backendResponseInterceptor(error)
51 | )
52 |
53 | /** BACKEND_SAVES (with CSRF, mainly for PUT, POST, DELETE) */
54 |
55 | // window.onbeforeunload = e => {
56 | // e.preventDefault()
57 | // e.returnValue('Changes may not be saved. Continue?')
58 | // return 'Changes may not be saved. Continue?'
59 | // }
60 | export const backendSaves = axios.create({
61 | baseURL: baseUrl,
62 | withCredentials: true
63 | })
64 | backendSaves.interceptors.response.use(
65 | response => camelCase(response.data, { deep: true }),
66 | error => backendResponseInterceptor(error)
67 | )
68 |
69 | const graderBaseURL = 'https://darlene-autograder.herokuapp.com/'
70 | export const grader = axios.create({
71 | baseURL: graderBaseURL
72 | // withCredentials: true
73 | })
74 | grader.interceptors.response.use(response =>
75 | camelCase(response.data, { deep: true })
76 | )
77 |
78 | const cdnBaseUrl = 'https://d36nt3c422j20i.cloudfront.net/'
79 | export const cdn = axios.create({
80 | baseURL: cdnBaseUrl
81 | })
82 | cdn.interceptors.response.use(response =>
83 | camelCase(response.data, { deep: true })
84 | )
85 |
--------------------------------------------------------------------------------
/src/services/ContentService.js:
--------------------------------------------------------------------------------
1 | import { backend, cdn } from './AxiosInstances'
2 |
3 | const FETCH_FROM_CDN = true
4 |
5 | export const autoFetch = async (id, cacheType) => {
6 | const [type, params] = cacheType
7 | .replace('cached', '')
8 | .split(/(?=[A-Z])/)
9 | .map(w => w.toLowerCase())
10 |
11 | /**
12 | * TEMP SPECIAL
13 | */
14 | if (type === 'users') {
15 | return backend.get(`/users/${id}`)
16 | }
17 | if (type === 'students') {
18 | return backend.get(`/students/${id}`)
19 | }
20 |
21 | const endpoint = `/${type}/${id}` + (params ? `/${params}` : '')
22 |
23 | if (!params && FETCH_FROM_CDN) {
24 | try {
25 | const data = await cdn.get(`${endpoint}/data.json`)
26 | return data
27 | } catch (e) {
28 | console.log(
29 | '[ContentService] Error fetching from CDN. Moving to servers...'
30 | )
31 | if (e.response?.status === 404)
32 | console.log(
33 | '[ContentService] The above error was due to missing content'
34 | )
35 | }
36 | }
37 | return backend.get(endpoint)
38 | }
39 |
--------------------------------------------------------------------------------
/src/services/ExploreService.js:
--------------------------------------------------------------------------------
1 | import { backend } from './AxiosInstances'
2 |
--------------------------------------------------------------------------------
/src/services/LearnService.js:
--------------------------------------------------------------------------------
1 | import { backend, backendSaves, grader } from './AxiosInstances'
2 |
3 | /* ===== RUNTIME */
4 |
5 | export const deleteActivityProgress = activityId => {
6 | const endpoint = `/activities/${activityId}/progress`
7 | return backendSaves.delete(endpoint)
8 | }
9 |
10 | export const unlockCard = (activityId, cardId) => {
11 | const endpoint = `/activities/${activityId}/cards/${cardId}`
12 | return backendSaves.put(endpoint)
13 | }
14 |
15 | export const unlockHint = (activityId, hintId) => {
16 | const endpoint = `/activities/${activityId}/hints/${hintId}`
17 | return backendSaves.put(endpoint)
18 | }
19 |
20 | export const submitCheckpointProgress = (
21 | activityId,
22 | checkpointId,
23 | type,
24 | content
25 | ) => {
26 | const backendEndpoint = `checkpoints/${checkpointId}/progress`
27 |
28 | const formData = new FormData()
29 | formData.append('content', content)
30 |
31 | if (type !== 'Multiple Choice') {
32 | formData.append('comment', 'null')
33 | }
34 |
35 | if (type === 'Autograder') {
36 | formData.append('activity_id', activityId)
37 | formData.append('checkpoint_id', checkpointId)
38 | formData.append('username', 'Student@example.com')
39 | return grader.post('/uploader', formData)
40 | }
41 | return backendSaves.put(backendEndpoint, formData)
42 | }
43 |
--------------------------------------------------------------------------------
/src/services/PusherService.js:
--------------------------------------------------------------------------------
1 | import Pusher from 'pusher-js'
2 | import { setPusherClient } from 'react-pusher'
3 |
4 | const pusherClient = new Pusher(process.env.PUSHER_KEY, {
5 | clustor: process.env.PUSHER_CLUSTER,
6 | authEndpoint: 'https://darlene-autograder.herokuapp.com/uploader/cli',
7 | forceTLS: true
8 | })
9 |
10 | setPusherClient(pusherClient)
11 |
--------------------------------------------------------------------------------
/src/services/StudentService.js:
--------------------------------------------------------------------------------
1 | import { backend, backendSaves } from './AxiosInstances'
2 |
3 | /**
4 | * GET request for getting Student data
5 | */
6 | export const fetchStudentData = studentId => {
7 | const endpoint = `/students/${studentId}`
8 | return backend.get(endpoint)
9 | }
10 |
11 | /** ===== RUNTIME */
12 |
13 | export const joinClassroom = classCode => {
14 | const endpoint = '/students/classrooms'
15 | return backendSaves.put(endpoint, { class_code: classCode })
16 | }
17 |
18 | export const setSuggestedActivity = (studentId, id, moduleId) =>
19 | updateStudentData(studentId, {
20 | suggested_activity: {
21 | id,
22 | module_id: moduleId
23 | }
24 | })
25 |
26 | export const setChosenProjects = (moduleId, chosenProjects) =>
27 | updateModuleProgress(moduleId, {
28 | chosen_project_ids: chosenProjects.map(p => p.id)
29 | })
30 |
31 | const updateStudentData = (studentId, updates) => {
32 | const endpoint = `/students/${studentId}`
33 | return backendSaves.put(endpoint, updates)
34 | }
35 |
36 | const updateModuleProgress = (id, updates) => {
37 | const endpoint = `/modules/${id}/progress`
38 | return backendSaves.put(endpoint, updates)
39 | }
40 |
41 | //
42 |
43 | //@unused
44 | /**
45 | * GET request for getting track data
46 | * @param {String} trackID
47 | */
48 | export const fetchTrack = trackID => {
49 | const endpoint = `tracks/${trackID}`
50 | return backend.get(endpoint)
51 | }
52 |
53 | export const fetchTrackProgress = trackID => {
54 | const endpoint = `tracks/${trackID}/progress`
55 | return backend.get(endpoint)
56 | }
57 |
58 | /**
59 | * GET request for getting topic data
60 | * @param {String} topicID
61 | */
62 | export const fetchTopic = topicID => {
63 | const endpoint = `topics/${topicID}`
64 | return backend.get(endpoint)
65 | }
66 |
--------------------------------------------------------------------------------
/src/services/TestService.js:
--------------------------------------------------------------------------------
1 | import { backend } from './AxiosInstances'
2 |
3 | export const ping = async () => {
4 | // await backend.get('/auth').catch(() => {})
5 |
6 | const startTime = new Date()
7 | const ping = (await backend.get('ping')).message
8 | const endTime = new Date()
9 | console.log(ping + ':', endTime - startTime + 'ms')
10 | }
11 |
--------------------------------------------------------------------------------
/src/styles/GlobalAnime.js:
--------------------------------------------------------------------------------
1 | import anime from 'animejs'
2 |
3 | export const fadeIn = (targets, options) =>
4 | anime({
5 | targets,
6 | opacity: [0, 1],
7 | easing: 'easeOutQuad',
8 | duration: 500,
9 | ...options
10 | })
11 |
12 | export const slideIn = (targets, options) =>
13 | anime({
14 | targets,
15 | translateX: ['-1em', 0],
16 | easing: 'easeOutQuad',
17 | duration: 750,
18 | ...options
19 | })
20 |
21 | /**
22 | * STATUS-BASED ANIMATIONS
23 | * - works closely with react-transition-group
24 | */
25 |
26 | export const statusFadeOut = (status, targets, duration = 750) => {
27 | return anime({
28 | targets,
29 | opacity: () => {
30 | switch (status) {
31 | case 'exiting':
32 | return 0
33 | default:
34 | return 1
35 | }
36 | },
37 | easing: 'easeInQuad',
38 | duration
39 | })
40 | }
41 |
--------------------------------------------------------------------------------
/src/styles/media.js:
--------------------------------------------------------------------------------
1 | import { css } from 'styled-components'
2 |
3 | export const sizes = {
4 | massive: '1920',
5 | thicc: '1680',
6 | giant: '1440',
7 | bigDesktop: '1200',
8 | desktop: '1000',
9 | tablet: '768',
10 | thone: '600',
11 | phablet: '480',
12 | phone: '376',
13 | tiny: '330'
14 | }
15 |
16 | // iterate through the sizes and create a media template
17 | const media = Object.keys(sizes).reduce((accumulator, label) => {
18 | // use em in breakpoints to work properly cross-browser and support users
19 | // changing their browsers font-size: https://zellwk.com/blog/media-query-units/
20 | const emSize = sizes[label] / 16
21 | accumulator[label] = (...args) => css`
22 | @media (max-width: ${emSize}em) {
23 | ${css(...args)};
24 | }
25 | `
26 | return accumulator
27 | }, {})
28 |
29 | export default media
30 |
--------------------------------------------------------------------------------
/src/styles/theme.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Make all colors normal hex (6 letters) for consistent
3 | * transparency usage across the app
4 | *
5 | * ex ${accent}77
6 | */
7 |
8 | const constant = {
9 | font: '#000000',
10 | offFont: '#ebebeb',
11 | fontInvert: '#ffffff',
12 | // itemHoverShadow: "0px 4px 25px rgba(0, 0, 0, 0.15)"
13 |
14 | pastel: {
15 | red: '#e57373',
16 | orange: '#ffb74d',
17 | yellow: '#ffe14d',
18 | green: '#81c784',
19 | blue: '#64b5f6',
20 | purple: '#b6aaf0',
21 | magenta: '#bb91eb',
22 | pink: '#f2ace7',
23 | cyan: '#aaf0d7'
24 | },
25 | muted: {
26 | red: '#ab5757',
27 | orange: '#c78f3e',
28 | yellow: '#f5d63d',
29 | green: '#609663',
30 | blue: '#4780ad',
31 | purple: '#7971a3',
32 | magenta: '#8164a3',
33 | pink: '#c484ba',
34 | cyan: '#5aada2'
35 | }
36 | }
37 |
38 | export const bitblue = {
39 | ...constant,
40 | accent: '#007bed',
41 | accentVariant: '#9acfff', //86c5ff
42 | bg: '#0a192f',
43 | bgVariant: '#172A45',
44 | bgPage: '#f5faff'
45 | }
46 |
47 | export const chakraTheme = {
48 | ...constant,
49 | accent: '#007bed',
50 | accentVariant: '#9acfff', //86c5ff
51 | bg: '#0a192f',
52 | bgVariant: '#172A45',
53 | bgPage: '#f5faff'
54 | }
55 |
56 | export const palepink = {
57 | ...constant,
58 | accent: '#db7093',
59 | accentVariant: '#ffb5cd',
60 | bg: '#320b18',
61 | bgVariant: '#451726',
62 | bgPage: '#fff5ff'
63 | }
64 |
65 | export const orange = {
66 | ...constant,
67 | accent: '#ED6800',
68 | accentVariant: '#FFC69A',
69 | bg: '#2F190A',
70 | bgVariant: '#462B18',
71 | bgPage: '#FFF9F4'
72 | }
73 |
74 | export const black = {
75 | ...constant,
76 | accent: '#565656',
77 | accentVariant: '#787878',
78 | bg: '#121212',
79 | bgVariant: '#232323',
80 | bgPage: '#efefef'
81 | }
82 |
83 | export default bitblue
84 |
--------------------------------------------------------------------------------
/src/utils/DataStructures.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Implemented using arrays, use case is simple so who needs efficiency
3 | * @param {array} stack
4 | */
5 | export function SafeStack(stack) {
6 | this.copy = () => new SafeStack([...stack])
7 | this.push = item => {
8 | if (item && !this.has(item)) stack.push(item)
9 | return this
10 | }
11 | this.pop = item => {
12 | if (!this.isEmpty()) {
13 | if (this.peek() === item) stack.pop()
14 | else return stack.filter(i => i !== item)
15 | }
16 | return this
17 | }
18 | this.has = item => stack.includes(item)
19 | this.peek = () => (this.isEmpty() ? 'empty stack' : stack[stack.length - 1])
20 | this.isEmpty = () => stack.length === 0
21 | this.get = () => stack // use only for testing
22 | }
23 |
24 | /**
25 | * Implemented using arrays, same as above
26 | * @param {array} queue
27 | */
28 | export function SafeQueue(queue) {
29 | this.copy = () => new SafeQueue([...queue])
30 | this.enqueue = item => {
31 | if (item && !this.has(item)) queue.push(item)
32 | return this
33 | }
34 | this.dequeue = item => {
35 | if (!this.isEmpty()) {
36 | if (this.front() === item) queue.shift()
37 | else return queue.filter(i => i !== item)
38 | }
39 | return this
40 | }
41 | this.has = item => queue.includes(item)
42 | this.front = () => (this.isEmpty() ? 'empty queue' : queue[0])
43 | this.isEmpty = () => queue.length === 0
44 | this.get = () => queue // use only for testing
45 | }
46 |
--------------------------------------------------------------------------------
/src/utils/customHooks.js:
--------------------------------------------------------------------------------
1 | import { useEffect, useRef } from 'react'
2 |
3 | export const useDidUpdateEffect = (fn, dependencies, cleanup) => {
4 | const didMountRef = useRef(false)
5 |
6 | useEffect(() => {
7 | if (didMountRef.current) fn()
8 | else didMountRef.current = true
9 | return cleanup
10 | }, dependencies) // eslint-disable-line react-hooks/exhaustive-deps
11 | }
12 |
13 | // modified from https://stackoverflow.com/questions/41004631/trace-why-a-react-component-is-re-rendering
14 | export const useTraceUpdate = props => {
15 | const prev = usePreviousSafe(props)
16 | useEffect(() => {
17 | const changedProps = Object.entries(props).reduce((ps, [k, v]) => {
18 | if (prev[k] !== v) {
19 | ps[k] = [prev[k], v]
20 | }
21 | return ps
22 | }, {})
23 | if (Object.keys(changedProps).length > 0) {
24 | console.log('Changed props:', changedProps)
25 | }
26 | })
27 | }
28 |
29 | export const usePureTraceUpdate = props => {
30 | const prev = usePreviousSafe(props)
31 | const changedProps = Object.entries(props).reduce((ps, [k, v]) => {
32 | if (prev[k] !== v) {
33 | ps[k] = [prev[k], v]
34 | }
35 | return ps
36 | }, {})
37 | console.log('Changed props:', changedProps)
38 | }
39 |
40 | export const usePrevious = value => {
41 | const ref = useRef()
42 | useEffect(() => {
43 | ref.current = value
44 | })
45 | return ref.current
46 | }
47 |
48 | export const usePreviousSafe = value => {
49 | const ref = useRef(value)
50 | useEffect(() => {
51 | ref.current = value
52 | })
53 | return ref.current
54 | }
55 |
--------------------------------------------------------------------------------
/src/utils/objUtils.js:
--------------------------------------------------------------------------------
1 | export const objectArrayToObject = array =>
2 | array.reduce((acc, item) => ({ ...acc, ...item }), {})
3 |
4 | export const objArrayClone = objArray => {
5 | return objArray.map((_, i) => {
6 | return { ...objArray[i] }
7 | })
8 | }
9 |
10 | // export const iterateNodes = (obj, callback, nestLevel = Infinity) => {
11 | // if (!obj) return undefined
12 | // if (nestLevel === 0) return
13 |
14 | // for (let property in obj) {
15 | // if (obj.hasOwnProperty(property) && obj[property] != null) {
16 | // if (obj[property].constructor === Object) {
17 | // iterateNodes(obj[property], callback, nestLevel - 1)
18 | // callback(obj[property])
19 | // } else if (obj[property].constructor === Array) {
20 | // for (let i = 0; i < obj[property].length; i++) {
21 | // iterateNodes(obj[property][i], callback, nestLevel - 1)
22 | // callback(obj[property][i])
23 | // }
24 | // } else {
25 | // // console.log(obj[property])
26 | // }
27 | // }
28 | // }
29 | // return obj
30 | // }
31 |
--------------------------------------------------------------------------------