├── .eslintignore
├── amplify
├── backend
│ ├── storage
│ │ └── lmsBucket
│ │ │ ├── storage-params.json
│ │ │ └── parameters.json
│ ├── api
│ │ └── lmsappamplify
│ │ │ ├── parameters.json
│ │ │ ├── transform.conf.json
│ │ │ ├── resolvers
│ │ │ └── README.md
│ │ │ ├── stacks
│ │ │ └── CustomResources.json
│ │ │ └── schema.graphql
│ ├── tags.json
│ ├── hosting
│ │ └── amplifyhosting
│ │ │ └── amplifyhosting-template.json
│ ├── backend-config.json
│ └── auth
│ │ └── lmsappamplify77733d32
│ │ └── parameters.json
├── README.md
├── .config
│ └── project-config.json
└── team-provider-info.json
├── src
├── learn.png
├── assests
│ ├── lms.png
│ ├── lms2.png
│ └── hero.svg
├── models
│ ├── schema.d.ts
│ ├── index.js
│ └── index.d.ts
├── context
│ ├── UserContext.js
│ └── CourseContext.js
├── routes
│ ├── isLoggedIn.js
│ ├── PrivateRoute.js
│ ├── ProtectedRoute.js
│ └── Routes.js
├── index.js
├── components
│ ├── account
│ │ ├── Label.js
│ │ ├── Title.js
│ │ ├── MessageAlert.js
│ │ ├── Account.js
│ │ ├── Profile.js
│ │ ├── UserDetails.js
│ │ ├── UneditableDetails.js
│ │ └── IsEducatorSwitch.js
│ ├── landingPage
│ │ ├── LandingPage.js
│ │ ├── Logo.js
│ │ ├── Features.js
│ │ ├── Benefit.js
│ │ ├── Hero.js
│ │ └── Footer.js
│ ├── course
│ │ ├── ContinueButton.js
│ │ ├── Courses.js
│ │ ├── CourseStatus.js
│ │ ├── EditButton.js
│ │ ├── PublishButton.js
│ │ ├── DeleteButton.js
│ │ ├── EditCourse.js
│ │ ├── ShareLinkModal.js
│ │ ├── CourseCard.js
│ │ ├── CourseList.js
│ │ ├── EnrollButton.js
│ │ ├── Navlinks.js
│ │ ├── NewCourse.js
│ │ ├── CourseDetails.js
│ │ └── CourseIntro.js
│ ├── IsEducatorChip.js
│ ├── calendar
│ │ ├── CalendarView.js
│ │ ├── appointments
│ │ │ └── appointments.js
│ │ └── Calendar.js
│ ├── Connect.js
│ ├── SignoutButton.js
│ ├── Breadcrumb.js
│ ├── Welcome.js
│ ├── dashboard
│ │ ├── Dashboard.js
│ │ ├── StudentDashboard.js
│ │ ├── EducatorDashboard.js
│ │ ├── Drafts.js
│ │ ├── Published.js
│ │ └── AllCourses.js
│ ├── Home.js
│ ├── Error404.js
│ ├── Navigation.js
│ └── Details.js
├── theme.js
├── pages
│ ├── discussions
│ │ ├── CommentForm.js
│ │ ├── AddComment.js
│ │ ├── DisplayComments.js
│ │ ├── DisplayDiscussions.js
│ │ ├── Comment.js
│ │ ├── Discussions.js
│ │ ├── NewDiscussion.js
│ │ └── Discussion.js
│ ├── students
│ │ ├── StudentCard.js
│ │ ├── StudentDetails.js
│ │ └── Students.js
│ ├── assignments
│ │ ├── Assignment.js
│ │ ├── DisplayAssignments.js
│ │ ├── Assignments.js
│ │ └── AddAssignment.js
│ ├── syllabus
│ │ ├── PreviewSyllabus.js
│ │ ├── DisplaySyllabus.js
│ │ ├── Syllabus.js
│ │ └── NewSyllabus.js
│ ├── announcements
│ │ ├── EditAnnouncement.js
│ │ ├── Announcements.js
│ │ ├── DisplayAnnouncements.js
│ │ ├── Edit.js
│ │ └── NewAnnouncement.js
│ └── lessons
│ │ ├── Lessons.js
│ │ ├── LessonsList.js
│ │ └── NewLesson.js
├── customHook
│ └── useCourses.js
├── App.js
├── utils
│ └── useStyles.js
├── index.css
└── auth
│ └── SignIn.js
├── public
├── logo.png
├── robots.txt
├── favicon.ico
├── manifest.json
├── index.html
└── hero.svg
├── .vscode
└── settings.json
├── .graphqlconfig.yml
├── .gitignore
├── package.json
└── CodeSnippets.md
/.eslintignore:
--------------------------------------------------------------------------------
1 | src\models
--------------------------------------------------------------------------------
/amplify/backend/storage/lmsBucket/storage-params.json:
--------------------------------------------------------------------------------
1 | {}
--------------------------------------------------------------------------------
/src/learn.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/palakgupta2712/lms-app-amplify/HEAD/src/learn.png
--------------------------------------------------------------------------------
/public/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/palakgupta2712/lms-app-amplify/HEAD/public/logo.png
--------------------------------------------------------------------------------
/public/robots.txt:
--------------------------------------------------------------------------------
1 | # https://www.robotstxt.org/robotstxt.html
2 | User-agent: *
3 | Disallow:
4 |
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/palakgupta2712/lms-app-amplify/HEAD/public/favicon.ico
--------------------------------------------------------------------------------
/src/assests/lms.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/palakgupta2712/lms-app-amplify/HEAD/src/assests/lms.png
--------------------------------------------------------------------------------
/src/assests/lms2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/palakgupta2712/lms-app-amplify/HEAD/src/assests/lms2.png
--------------------------------------------------------------------------------
/src/models/schema.d.ts:
--------------------------------------------------------------------------------
1 | import { Schema } from '@aws-amplify/datastore';
2 |
3 | export declare const schema: Schema;
--------------------------------------------------------------------------------
/src/context/UserContext.js:
--------------------------------------------------------------------------------
1 | import { createContext } from "react";
2 |
3 | export const UserContext = createContext(null);
4 |
--------------------------------------------------------------------------------
/src/context/CourseContext.js:
--------------------------------------------------------------------------------
1 | import { createContext } from "react";
2 |
3 | export const CourseContext = createContext(null);
4 |
--------------------------------------------------------------------------------
/src/routes/isLoggedIn.js:
--------------------------------------------------------------------------------
1 | export const isLoggedIn = () => {
2 | if (localStorage.getItem("auth")) return true;
3 | return false;
4 | };
5 |
--------------------------------------------------------------------------------
/amplify/backend/api/lmsappamplify/parameters.json:
--------------------------------------------------------------------------------
1 | {
2 | "AppSyncApiName": "lmsappamplify",
3 | "DynamoDBBillingMode": "PAY_PER_REQUEST",
4 | "DynamoDBEnableServerSideEncryption": false
5 | }
--------------------------------------------------------------------------------
/amplify/backend/tags.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "Key": "user:Stack",
4 | "Value": "{project-env}"
5 | },
6 | {
7 | "Key": "user:Application",
8 | "Value": "{project-name}"
9 | }
10 | ]
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import ReactDOM from "react-dom";
3 | import "./index.css";
4 | import App from "./App";
5 |
6 | ReactDOM.render( , document.getElementById("root"));
7 |
--------------------------------------------------------------------------------
/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "EduCouch LMS",
3 | "name": "EduCouch Learning Management System",
4 | "start_url": ".",
5 | "display": "standalone",
6 | "theme_color": "#000000",
7 | "background_color": "#ffffff"
8 | }
9 |
--------------------------------------------------------------------------------
/amplify/backend/api/lmsappamplify/transform.conf.json:
--------------------------------------------------------------------------------
1 | {
2 | "Version": 5,
3 | "ElasticsearchWarning": true,
4 | "ResolverConfig": {
5 | "project": {
6 | "ConflictHandler": "AUTOMERGE",
7 | "ConflictDetection": "VERSION"
8 | }
9 | }
10 | }
--------------------------------------------------------------------------------
/amplify/backend/api/lmsappamplify/resolvers/README.md:
--------------------------------------------------------------------------------
1 | Any resolvers that you add in this directory will override the ones automatically generated by Amplify CLI and will be directly copied to the cloud.
2 | For more information, visit [https://docs.amplify.aws/cli/graphql-transformer/resolvers](https://docs.amplify.aws/cli/graphql-transformer/resolvers)
--------------------------------------------------------------------------------
/src/components/account/Label.js:
--------------------------------------------------------------------------------
1 | import { Box } from "@material-ui/core";
2 | import React from "react";
3 |
4 | function Label({ text }) {
5 | return (
6 |
7 |
8 | {text}
9 |
10 |
11 | );
12 | }
13 |
14 | export default Label;
15 |
--------------------------------------------------------------------------------
/src/theme.js:
--------------------------------------------------------------------------------
1 | import { createMuiTheme } from "@material-ui/core/styles";
2 |
3 | const theme = createMuiTheme({
4 | palette: {
5 | primary: {
6 | main: "#12355B",
7 | contrastText: "#fff",
8 | },
9 | secondary: {
10 | main: "#DDB92A",
11 | contrastText: "#000",
12 | },
13 | },
14 | });
15 | export default theme;
16 |
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "files.exclude": {
3 | "amplify/.config": true,
4 | "amplify/**/*-parameters.json": true,
5 | "amplify/**/amplify.state": true,
6 | "amplify/**/transform.conf.json": true,
7 | "amplify/#current-cloud-backend": true,
8 | "amplify/backend/amplify-meta.json": true,
9 | "amplify/backend/awscloudformation": true
10 | }
11 | }
--------------------------------------------------------------------------------
/amplify/README.md:
--------------------------------------------------------------------------------
1 | # Getting Started with Amplify CLI
2 | This directory was generated by [Amplify CLI](https://docs.amplify.aws/cli).
3 |
4 | Helpful resources:
5 | - Amplify documentation: https://docs.amplify.aws
6 | - Amplify CLI documentation: https://docs.amplify.aws/cli
7 | - More details on this folder & generated files: https://docs.amplify.aws/cli/reference/files
8 | - Join Amplify's community: https://amplify.aws/community/
9 |
--------------------------------------------------------------------------------
/src/components/account/Title.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { Typography } from "@material-ui/core";
3 |
4 | function Title() {
5 | return (
6 |
7 |
11 | My Account
12 |
13 |
14 | );
15 | }
16 |
17 | export default Title;
18 |
--------------------------------------------------------------------------------
/amplify/.config/project-config.json:
--------------------------------------------------------------------------------
1 | {
2 | "projectName": "lmsappamplify",
3 | "version": "3.1",
4 | "frontend": "javascript",
5 | "javascript": {
6 | "framework": "react",
7 | "config": {
8 | "SourceDir": "src",
9 | "DistributionDir": "build",
10 | "BuildCommand": "npm.cmd run-script build",
11 | "StartCommand": "npm.cmd run-script start"
12 | }
13 | },
14 | "providers": [
15 | "awscloudformation"
16 | ]
17 | }
--------------------------------------------------------------------------------
/src/components/landingPage/LandingPage.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import Logo from "./Logo";
3 | import Hero from "./Hero";
4 | import Benefit from "./Benefit";
5 | import Features from "./Features";
6 | import Footer from "./Footer";
7 |
8 | function LandingPage() {
9 | return (
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 | );
18 | }
19 |
20 | export default LandingPage;
21 |
--------------------------------------------------------------------------------
/.graphqlconfig.yml:
--------------------------------------------------------------------------------
1 | projects:
2 | lmsappamplify:
3 | schemaPath: amplify/backend/api/lmsappamplify/build/schema.graphql
4 | includes:
5 | - src/graphql/**/*.js
6 | excludes:
7 | - ./amplify/**
8 | extensions:
9 | amplify:
10 | codeGenTarget: javascript
11 | generatedFileName: ''
12 | docsFilePath: src/graphql
13 | region: ap-south-1
14 | apiId: null
15 | maxDepth: 2
16 | extensions:
17 | amplify:
18 | version: 3
19 |
--------------------------------------------------------------------------------
/src/pages/discussions/CommentForm.js:
--------------------------------------------------------------------------------
1 | import { Button, TextField } from "@material-ui/core";
2 | import React from "react";
3 |
4 | function CommentForm() {
5 | return (
6 | <>
7 |
8 |
14 | Add
15 |
16 | >
17 | );
18 | }
19 |
20 | export default CommentForm;
21 |
--------------------------------------------------------------------------------
/src/customHook/useCourses.js:
--------------------------------------------------------------------------------
1 | import { DataStore } from "aws-amplify";
2 | import { useEffect, useState } from "react";
3 | import { Course } from "../models";
4 |
5 | function useCourses(id) {
6 | const [courses, setCourses] = useState([]);
7 | useEffect(() => {
8 | async function getCourses() {
9 | const models = await DataStore.query(Course, id);
10 | setCourses(models);
11 | }
12 | getCourses();
13 | }, [id]);
14 |
15 | return courses;
16 | }
17 |
18 | export default useCourses;
19 |
--------------------------------------------------------------------------------
/src/App.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { CssBaseline, ThemeProvider } from "@material-ui/core";
3 | import { BrowserRouter as Router } from "react-router-dom";
4 | import theme from "./theme";
5 | import Routes from "./routes/Routes";
6 | export default function App() {
7 | return (
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 | );
17 | }
18 |
--------------------------------------------------------------------------------
/src/routes/PrivateRoute.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { Route, Redirect } from "react-router-dom";
3 | import { isLoggedIn } from "./isLoggedIn";
4 |
5 | const PrivateRoute = ({ component: Component, ...rest }) => (
6 | // Show the component only when the user is logged in
7 | // Otherwise, redirect the user to /signin page
8 |
11 | isLoggedIn() ? :
12 | }
13 | />
14 | );
15 |
16 | export default PrivateRoute;
17 |
--------------------------------------------------------------------------------
/src/models/index.js:
--------------------------------------------------------------------------------
1 | // @ts-check
2 | import { initSchema } from '@aws-amplify/datastore';
3 | import { schema } from './schema';
4 |
5 | const CourseStatus = {
6 | "PUBLISHED": "PUBLISHED",
7 | "DRAFT": "DRAFT"
8 | };
9 |
10 | const { AssignmentModel, SyllabusModel, CommentModel, User, CourseUser, Course, Lesson, AnnouncementsModel, PostModel } = initSchema(schema);
11 |
12 | export {
13 | AssignmentModel,
14 | SyllabusModel,
15 | CommentModel,
16 | User,
17 | CourseUser,
18 | Course,
19 | Lesson,
20 | AnnouncementsModel,
21 | PostModel,
22 | CourseStatus
23 | };
--------------------------------------------------------------------------------
/src/components/account/MessageAlert.js:
--------------------------------------------------------------------------------
1 | import { Snackbar } from "@material-ui/core";
2 | import { Alert } from "@material-ui/lab";
3 | import React from "react";
4 |
5 | function MessageAlert({ open, handleClose }) {
6 | return (
7 |
8 |
9 |
15 |
16 | Successfully updated
17 |
18 |
19 |
20 | ;
21 |
22 | );
23 | }
24 |
25 | export default MessageAlert;
26 |
--------------------------------------------------------------------------------
/src/pages/students/StudentCard.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { Card, CardHeader } from "@material-ui/core";
3 |
4 | import Avatar from "boring-avatars";
5 |
6 | function StudentCard({ user }) {
7 | return (
8 |
9 |
10 |
18 | }
19 | title={user.name}
20 | fontWeight="900"
21 | subheader={"@" + user.username}
22 | />
23 |
24 |
25 | );
26 | }
27 |
28 | export default StudentCard;
29 |
--------------------------------------------------------------------------------
/src/components/course/ContinueButton.js:
--------------------------------------------------------------------------------
1 | import { Button } from "@material-ui/core";
2 | import React from "react";
3 | import { Link } from "react-router-dom";
4 |
5 | function ContinueButton({ course }) {
6 | return (
7 |
8 |
13 |
22 | Continue
23 |
24 |
25 |
26 | );
27 | }
28 |
29 | export default ContinueButton;
30 |
--------------------------------------------------------------------------------
/src/components/landingPage/Logo.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { makeStyles } from "@material-ui/core";
3 |
4 | function Logo() {
5 | const classes = useStyles();
6 |
7 | return (
8 |
11 | );
12 | }
13 |
14 | export default Logo;
15 |
16 | const useStyles = makeStyles((theme) => ({
17 | container: {
18 | display: "flex",
19 | justifyContent: "center",
20 | alignItems: "center",
21 | width: "100%",
22 | backgroundColor: theme.palette.primary.main,
23 | color: "white",
24 | fontSize: "18px",
25 | height: "80px",
26 | flexWrap: "wrap",
27 | },
28 | logo: {
29 | border: " 2px solid #DDB92A",
30 | padding: "10px",
31 | letterSpacing: "6px",
32 | },
33 | }));
34 |
--------------------------------------------------------------------------------
/src/components/IsEducatorChip.js:
--------------------------------------------------------------------------------
1 | import { Chip } from "@material-ui/core";
2 | import React, { useContext } from "react";
3 | import { UserContext } from "../context/UserContext";
4 |
5 | function IsEducatorChip() {
6 | const user = useContext(UserContext);
7 | return (
8 |
9 | {user.isEducator ? (
10 |
17 | ) : (
18 |
25 | )}
26 |
27 | );
28 | }
29 |
30 | export default IsEducatorChip;
31 |
--------------------------------------------------------------------------------
/src/utils/useStyles.js:
--------------------------------------------------------------------------------
1 | import { makeStyles } from "@material-ui/core";
2 |
3 | export const useStyles = makeStyles((theme) => ({
4 | sidebar: {
5 | height: "100%",
6 | background: theme.palette.primary.main,
7 | [theme.breakpoints.up("md")]: {
8 | height: "100vh",
9 | position: "fixed",
10 | },
11 | },
12 | menuButton: {
13 | color: "white",
14 | position: "fixed",
15 | background: theme.palette.primary.main,
16 | padding: "10px",
17 | width: "100%",
18 | [theme.breakpoints.up("sm")]: {
19 | display: "none",
20 | },
21 | },
22 | nav: {
23 | [theme.breakpoints.down("sm")]: {
24 | marginTop: "50px",
25 | },
26 | },
27 | root: {
28 | width: "100% ",
29 | [theme.breakpoints.up("md")]: {
30 | marginLeft: "100px",
31 | },
32 | },
33 | }));
34 |
--------------------------------------------------------------------------------
/src/components/calendar/CalendarView.js:
--------------------------------------------------------------------------------
1 | import * as React from "react";
2 | import Paper from "@material-ui/core/Paper";
3 | import { ViewState } from "@devexpress/dx-react-scheduler";
4 | import {
5 | Scheduler,
6 | MonthView,
7 | Toolbar,
8 | DateNavigator,
9 | Appointments,
10 | TodayButton,
11 | } from "@devexpress/dx-react-scheduler-material-ui";
12 | import { appointments } from "./appointments/appointments";
13 |
14 | export default function Demo() {
15 | const currentDate = new Date();
16 |
17 | return (
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 | );
29 | }
30 |
--------------------------------------------------------------------------------
/src/components/Connect.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { makeStyles } from "@material-ui/core/styles";
3 | import Grid from "@material-ui/core/Grid";
4 | import Navigation from "./Navigation";
5 | import Details from "./Details";
6 |
7 | export default function Connect() {
8 | const classes = useStyles();
9 |
10 | return (
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 | );
22 | }
23 |
24 | const useStyles = makeStyles((theme) => ({
25 | sidebar: {
26 | height: "100%",
27 | background: theme.palette.primary.main,
28 | [theme.breakpoints.up("md")]: {
29 | height: "100vh",
30 | },
31 | },
32 | }));
33 |
--------------------------------------------------------------------------------
/src/components/account/Account.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { makeStyles } from "@material-ui/core/styles";
3 | import Grid from "@material-ui/core/Grid";
4 | import Navigation from "../Navigation";
5 | import Profile from "./Profile";
6 |
7 | export default function Account() {
8 | const classes = useStyles();
9 |
10 | return (
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 | );
22 | }
23 |
24 | const useStyles = makeStyles((theme) => ({
25 | sidebar: {
26 | height: "100%",
27 | background: theme.palette.primary.main,
28 | [theme.breakpoints.up("md")]: {
29 | height: "100vh",
30 | },
31 | },
32 | }));
33 |
--------------------------------------------------------------------------------
/.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.local
17 | .env.development.local
18 | .env.test.local
19 | .env.production.local
20 |
21 | npm-debug.log*
22 | yarn-debug.log*
23 | yarn-error.log*
24 |
25 | #amplify
26 | amplify/\#current-cloud-backend
27 | amplify/.config/local-*
28 | amplify/logs
29 | amplify/mock-data
30 | amplify/backend/amplify-meta.json
31 | amplify/backend/awscloudformation
32 | amplify/backend/.temp
33 | amplify/team-provider-info.json
34 | build/
35 | dist/
36 | node_modules/
37 | aws-exports.js
38 | awsconfiguration.json
39 | amplifyconfiguration.json
40 | amplifyconfiguration.dart
41 | amplify-build-config.json
42 | amplify-gradle-config.json
43 | amplifytools.xcconfig
44 | .secret-*
--------------------------------------------------------------------------------
/src/components/SignoutButton.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import Auth from "@aws-amplify/auth";
3 | import { Box, Hidden, Typography } from "@material-ui/core";
4 | import ExitToAppIcon from "@material-ui/icons/ExitToApp";
5 | import { useHistory } from "react-router";
6 |
7 | function SignoutButton() {
8 | let history = useHistory();
9 |
10 | return (
11 |
12 | {
16 | Auth.signOut();
17 | window.location.reload();
18 | localStorage.removeItem("auth");
19 | window.location.href = "/";
20 | }}
21 | style={{ cursor: "pointer" }}
22 | >
23 |
24 |
25 |
26 | Logout
27 |
28 |
29 | );
30 | }
31 |
32 | export default SignoutButton;
33 |
--------------------------------------------------------------------------------
/src/components/Breadcrumb.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { Breadcrumbs } from "@material-ui/core";
3 | import { useParams } from "react-router";
4 | import { Link } from "react-router-dom";
5 | import useCourses from "../customHook/useCourses";
6 |
7 | function Breadcrumb() {
8 | let { id } = useParams();
9 | const course = useCourses(id);
10 | let pathCapitalized = "";
11 |
12 | const pathArray = window.location.pathname.split("/");
13 | if (pathArray.length === 4) {
14 | pathCapitalized =
15 | pathArray[3].charAt(0).toUpperCase() + pathArray[3].slice(1);
16 | }
17 |
18 | return (
19 |
20 |
21 | Courses
22 | {course.title}
23 | {pathCapitalized}
24 |
25 |
26 | );
27 | }
28 |
29 | export default Breadcrumb;
30 |
--------------------------------------------------------------------------------
/src/components/account/Profile.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { makeStyles, Container, Grid } from "@material-ui/core";
3 | import Title from "./Title";
4 | import UserDetails from "./UserDetails";
5 | import UneditableDetails from "./UneditableDetails";
6 | import IsEducatorSwitch from "./IsEducatorSwitch";
7 |
8 | export default function Profile() {
9 | const classes = useStyles();
10 | return (
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 | );
22 | }
23 |
24 | const useStyles = makeStyles((theme) => ({
25 | container: {
26 | backgroundColor: "white",
27 | height: "100vh",
28 | display: "flex",
29 | flexDirection: "column",
30 | alignItems: "center",
31 | },
32 | }));
33 |
--------------------------------------------------------------------------------
/src/components/calendar/appointments/appointments.js:
--------------------------------------------------------------------------------
1 | export const appointments = [
2 | {
3 | title: "Customer Workshop",
4 | startDate: new Date(2021, 5, 17, 10, 23),
5 | endDate: new Date(),
6 | },
7 | {
8 | title: "Prepare Test - 1",
9 | startDate: new Date(2021, 5, 26, 11, 0),
10 | endDate: new Date(2021, 5, 26, 13, 30),
11 | },
12 |
13 | {
14 | title: "Submit assignment 1",
15 | startDate: new Date(2021, 5, 16, 12, 0),
16 | endDate: new Date(2018, 5, 16, 13, 0),
17 | },
18 | {
19 | title: "Complete CourseWork - Javascript",
20 | startDate: new Date(2021, 5, 17, 15, 45),
21 | endDate: new Date(2021, 5, 18, 12, 15),
22 | },
23 | {
24 | title: "Prepare Test - 2",
25 | startDate: new Date(2021, 6, 16, 11, 0),
26 | endDate: new Date(2021, 6, 16, 13, 30),
27 | },
28 | {
29 | title: "Prepare Test - 3",
30 | startDate: new Date(2021, 6, 25, 11, 0),
31 | endDate: new Date(2021, 6, 25, 13, 30),
32 | },
33 | ];
34 |
--------------------------------------------------------------------------------
/amplify/team-provider-info.json:
--------------------------------------------------------------------------------
1 | {
2 | "dev": {
3 | "awscloudformation": {
4 | "AuthRoleName": "amplify-lmsappamplify-dev-224500-authRole",
5 | "UnauthRoleArn": "arn:aws:iam::920079206663:role/amplify-lmsappamplify-dev-224500-unauthRole",
6 | "AuthRoleArn": "arn:aws:iam::920079206663:role/amplify-lmsappamplify-dev-224500-authRole",
7 | "Region": "ap-southeast-1",
8 | "DeploymentBucketName": "amplify-lmsappamplify-dev-224500-deployment",
9 | "UnauthRoleName": "amplify-lmsappamplify-dev-224500-unauthRole",
10 | "StackName": "amplify-lmsappamplify-dev-224500",
11 | "StackId": "arn:aws:cloudformation:ap-southeast-1:920079206663:stack/amplify-lmsappamplify-dev-224500/f7e3f390-18a3-11ec-b29e-0a4a11a5d598",
12 | "AmplifyAppId": "d33qe5i8dfgdw8"
13 | },
14 | "categories": {
15 | "auth": {
16 | "lmsappamplify77733d32": {}
17 | },
18 | "hosting": {
19 | "amplifyhosting": {
20 | "appId": "d33qe5i8dfgdw8",
21 | "type": "manual"
22 | }
23 | }
24 | }
25 | }
26 | }
--------------------------------------------------------------------------------
/amplify/backend/hosting/amplifyhosting/amplifyhosting-template.json:
--------------------------------------------------------------------------------
1 | {
2 | "AWSTemplateFormatVersion": "2010-09-09",
3 | "Description": "Branch stack creation for AWS Amplify Console",
4 | "Parameters": {
5 | "env": {
6 | "Type": "String"
7 | },
8 | "appId": {
9 | "Type": "String"
10 | },
11 | "type": {
12 | "Type": "String"
13 | }
14 | },
15 | "Conditions": {
16 | "isManual": {
17 | "Fn::Equals": [
18 | {
19 | "Ref": "type"
20 | },
21 | "manual"
22 | ]
23 | }
24 | },
25 | "Resources": {
26 | "AmplifyBranch": {
27 | "Condition": "isManual",
28 | "Type": "AWS::Amplify::Branch",
29 | "Properties": {
30 | "BranchName": {
31 | "Ref": "env"
32 | },
33 | "AppId": {
34 | "Ref": "appId"
35 | }
36 | }
37 | }
38 | }
39 | }
--------------------------------------------------------------------------------
/src/components/course/Courses.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { makeStyles } from "@material-ui/core/styles";
3 | import Grid from "@material-ui/core/Grid";
4 | import Navigation from "../Navigation";
5 | import CourseList from "./CourseList";
6 |
7 | export default function Courses() {
8 | const classes = useStyles();
9 | return (
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 | );
23 | }
24 |
25 | const useStyles = makeStyles((theme) => ({
26 | sidebar: {
27 | height: "100%",
28 |
29 | background: theme.palette.primary.main,
30 | [theme.breakpoints.up("md")]: {
31 | height: "100vh",
32 | position: "fixed",
33 | },
34 | },
35 | grid: {
36 | [theme.breakpoints.up("md")]: {
37 | marginLeft: "100px",
38 | },
39 | },
40 | }));
41 |
--------------------------------------------------------------------------------
/src/components/course/CourseStatus.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import LabelIcon from "@material-ui/icons/Label";
3 | import { Chip } from "@material-ui/core";
4 |
5 | import CheckCircleIcon from "@material-ui/icons/CheckCircle";
6 |
7 | function CourseStatus({ course }) {
8 | return (
9 |
10 | {course.status === "PUBLISHED" ? (
11 | }
13 | label={course.status}
14 | variant="outlined"
15 | style={{
16 | border: "1px solid rgba(34, 208, 36)",
17 | background: "rgba(34, 208, 36, 0.2)",
18 | }}
19 | />
20 | ) : (
21 | }
23 | label={course.status}
24 | variant="outlined"
25 | style={{
26 | border: "1px solid rgb(0, 123, 255)",
27 | background: "rgb(0, 123, 255, 0.2)",
28 | }}
29 | />
30 | )}
31 |
32 | );
33 | }
34 |
35 | export default CourseStatus;
36 |
--------------------------------------------------------------------------------
/src/components/Welcome.js:
--------------------------------------------------------------------------------
1 | import React, { useContext } from "react";
2 | import { Typography } from "@material-ui/core";
3 | import Avatar from "boring-avatars";
4 |
5 | import { UserContext } from "../context/UserContext";
6 |
7 | function Welcome() {
8 | const user = useContext(UserContext);
9 |
10 | return (
11 |
12 |
23 |
27 | Welcome! {user.name}
28 | {" "}
35 |
36 |
37 |
38 | );
39 | }
40 |
41 | export default Welcome;
42 |
--------------------------------------------------------------------------------
/amplify/backend/backend-config.json:
--------------------------------------------------------------------------------
1 | {
2 | "auth": {
3 | "lmsappamplify77733d32": {
4 | "service": "Cognito",
5 | "providerPlugin": "awscloudformation",
6 | "dependsOn": [],
7 | "customAuth": false
8 | }
9 | },
10 | "api": {
11 | "lmsappamplify": {
12 | "service": "AppSync",
13 | "providerPlugin": "awscloudformation",
14 | "output": {
15 | "authConfig": {
16 | "defaultAuthentication": {
17 | "authenticationType": "API_KEY",
18 | "apiKeyConfig": {
19 | "apiKeyExpirationDays": 30,
20 | "description": "api key description"
21 | }
22 | },
23 | "additionalAuthenticationProviders": [
24 | {
25 | "authenticationType": "AWS_IAM"
26 | }
27 | ]
28 | }
29 | }
30 | }
31 | },
32 | "storage": {
33 | "lmsBucket": {
34 | "service": "S3",
35 | "providerPlugin": "awscloudformation"
36 | }
37 | },
38 | "hosting": {
39 | "amplifyhosting": {
40 | "service": "amplifyhosting",
41 | "providerPlugin": "awscloudformation",
42 | "type": "manual"
43 | }
44 | }
45 | }
--------------------------------------------------------------------------------
/src/components/calendar/Calendar.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { makeStyles } from "@material-ui/core/styles";
3 | import Grid from "@material-ui/core/Grid";
4 | import Navigation from "../Navigation";
5 | import { Container } from "@material-ui/core";
6 | import CalendarView from "./CalendarView";
7 |
8 | export default function Calendar() {
9 | const classes = useStyles();
10 |
11 | return (
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 | );
25 | }
26 |
27 | const useStyles = makeStyles((theme) => ({
28 | sidebar: {
29 | height: "100%",
30 |
31 | background: theme.palette.primary.main,
32 | [theme.breakpoints.up("md")]: {
33 | height: "100vh",
34 | position: "fixed",
35 | },
36 | },
37 | grid: {
38 | [theme.breakpoints.up("md")]: {
39 | marginLeft: "100px",
40 | },
41 | },
42 | }));
43 |
--------------------------------------------------------------------------------
/src/routes/ProtectedRoute.js:
--------------------------------------------------------------------------------
1 | import Auth from "@aws-amplify/auth";
2 | import { DataStore } from "@aws-amplify/datastore";
3 | import React, { useEffect, useState } from "react";
4 | import { Redirect, Route } from "react-router-dom";
5 | import Error404 from "../components/Error404";
6 | import { User } from "../models";
7 | import { isLoggedIn } from "./isLoggedIn";
8 |
9 | function ProtectedRoute({ component: Component, ...rest }) {
10 | const [currentUser, setCurrentUser] = useState([]);
11 | useEffect(() => {
12 | getUser();
13 | }, []);
14 | async function getUser() {
15 | const user = await Auth.currentAuthenticatedUser();
16 | const currentUser = (await DataStore.query(User)).filter(
17 | (c) => c.username === user.username
18 | );
19 | setCurrentUser(currentUser);
20 | }
21 |
22 | return isLoggedIn() ? (
23 | <>
24 | {currentUser.map((user, index) => (
25 |
26 |
29 | user.isEducator ? :
30 | }
31 | />
32 |
33 | ))}
34 | >
35 | ) : (
36 |
37 | );
38 | }
39 | export default ProtectedRoute;
40 |
--------------------------------------------------------------------------------
/src/pages/assignments/Assignment.js:
--------------------------------------------------------------------------------
1 | import React, { useEffect, useState } from "react";
2 | import DescriptionIcon from "@material-ui/icons/Description";
3 | import { Link } from "@material-ui/core";
4 | import GetAppIcon from "@material-ui/icons/GetApp";
5 | import { Storage } from "aws-amplify";
6 |
7 | function Assignment({ assignment }) {
8 | const [url, setUrl] = useState();
9 |
10 | useEffect(() => {
11 | loadFileUrl(assignment);
12 | }, [assignment]);
13 |
14 | const loadFileUrl = async (assignment) => {
15 | await Storage.get(assignment.S3Key).then((res) => {
16 | setUrl(res);
17 | console.log(res);
18 | });
19 | };
20 | return (
21 |
29 |
30 |
31 | {assignment.S3Key.split("/")[1]}
32 |
33 |
38 |
39 |
40 |
41 | );
42 | }
43 |
44 | export default Assignment;
45 |
--------------------------------------------------------------------------------
/src/components/account/UserDetails.js:
--------------------------------------------------------------------------------
1 | import React, { useContext } from "react";
2 | import Avatar from "boring-avatars";
3 | import { UserContext } from "../../context/UserContext";
4 | import { Box, Typography } from "@material-ui/core";
5 | import IsEducatorChip from "../IsEducatorChip";
6 |
7 | function UserDetails() {
8 | const user = useContext(UserContext);
9 |
10 | return (
11 |
12 |
19 |
22 |
28 |
29 |
30 |
33 | {user.name}
34 | @{user.username}
35 |
36 |
37 |
38 |
39 | );
40 | }
41 |
42 | export default UserDetails;
43 |
--------------------------------------------------------------------------------
/src/components/account/UneditableDetails.js:
--------------------------------------------------------------------------------
1 | import React, { useContext } from "react";
2 | import { Grid, TextField, makeStyles } from "@material-ui/core";
3 | import { UserContext } from "../../context/UserContext";
4 | import Label from "./Label";
5 |
6 | function UneditableDetails() {
7 | const user = useContext(UserContext);
8 | const classes = useStyles();
9 |
10 | return (
11 |
12 |
13 |
14 |
15 |
22 |
23 |
24 |
25 |
26 |
27 |
34 |
35 |
36 |
37 | );
38 | }
39 |
40 | export default UneditableDetails;
41 | const useStyles = makeStyles((theme) => ({
42 | grid: { marginBottom: "10px", padding: "5px" },
43 | textField: {
44 | width: "100% ",
45 | [theme.breakpoints.up("sm")]: {
46 | width: "75% ",
47 | },
48 | },
49 | }));
50 |
--------------------------------------------------------------------------------
/amplify/backend/storage/lmsBucket/parameters.json:
--------------------------------------------------------------------------------
1 | {
2 | "bucketName": "lmsbucket",
3 | "authPolicyName": "s3_amplify_1f6d5d10",
4 | "unauthPolicyName": "s3_amplify_1f6d5d10",
5 | "authRoleName": {
6 | "Ref": "AuthRoleName"
7 | },
8 | "unauthRoleName": {
9 | "Ref": "UnauthRoleName"
10 | },
11 | "selectedGuestPermissions": [
12 | "s3:PutObject",
13 | "s3:GetObject",
14 | "s3:ListBucket",
15 | "s3:DeleteObject"
16 | ],
17 | "selectedAuthenticatedPermissions": [
18 | "s3:PutObject",
19 | "s3:GetObject",
20 | "s3:ListBucket",
21 | "s3:DeleteObject"
22 | ],
23 | "s3PermissionsAuthenticatedPublic": "s3:PutObject,s3:GetObject,s3:DeleteObject",
24 | "s3PublicPolicy": "Public_policy_66272107",
25 | "s3PermissionsAuthenticatedUploads": "s3:PutObject",
26 | "s3UploadsPolicy": "Uploads_policy_66272107",
27 | "s3PermissionsAuthenticatedProtected": "s3:PutObject,s3:GetObject,s3:DeleteObject",
28 | "s3ProtectedPolicy": "Protected_policy_662520b3",
29 | "s3PermissionsAuthenticatedPrivate": "s3:PutObject,s3:GetObject,s3:DeleteObject",
30 | "s3PrivatePolicy": "Private_policy_662520b3",
31 | "AuthenticatedAllowList": "ALLOW",
32 | "s3ReadPolicy": "read_policy_66272107",
33 | "s3PermissionsGuestPublic": "s3:PutObject,s3:GetObject,s3:DeleteObject",
34 | "s3PermissionsGuestUploads": "s3:PutObject",
35 | "GuestAllowList": "ALLOW",
36 | "triggerFunction": "NONE"
37 | }
--------------------------------------------------------------------------------
/src/components/dashboard/Dashboard.js:
--------------------------------------------------------------------------------
1 | import React, { useContext } from "react";
2 | import { makeStyles } from "@material-ui/core/styles";
3 | import Grid from "@material-ui/core/Grid";
4 | import Navigation from "../Navigation";
5 | import Welcome from "../Welcome";
6 | import EducatorDashboard from "./EducatorDashboard";
7 | import StudentDashboard from "./StudentDashboard";
8 | import { UserContext } from "../../context/UserContext";
9 | import NewCourse from "../course/NewCourse";
10 |
11 | export default function Dashboard() {
12 | const user = useContext(UserContext);
13 | const classes = useStyles();
14 |
15 | return (
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 | {/* New Course button removed from courses */}
24 | {user.isEducator && }
25 | {user.isEducator ? : }
26 |
27 |
28 |
29 | );
30 | }
31 |
32 | const useStyles = makeStyles((theme) => ({
33 | sidebar: {
34 | height: "100%",
35 |
36 | background: theme.palette.primary.main,
37 | [theme.breakpoints.up("md")]: {
38 | height: "100vh",
39 | position: "fixed",
40 | },
41 | },
42 | grid: {
43 | [theme.breakpoints.up("md")]: {
44 | marginLeft: "100px",
45 | },
46 | },
47 | }));
48 |
--------------------------------------------------------------------------------
/src/components/Home.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { Button, Grid, makeStyles } from "@material-ui/core";
3 | import Navigation from "./Navigation";
4 | import LandingPage from "./landingPage/LandingPage";
5 | import { isLoggedIn } from "../routes/isLoggedIn";
6 | import home from "../assests/home.svg";
7 | import { Link } from "react-router-dom";
8 |
9 | export default function Account() {
10 | const classes = useStyles();
11 |
12 | return !isLoggedIn() ? (
13 |
14 | ) : (
15 |
16 |
17 |
18 |
19 |
20 |
21 |
29 |
30 |
31 |
32 | EXPLORE
33 |
34 |
35 |
36 |
37 |
38 |
39 | );
40 | }
41 |
42 | const useStyles = makeStyles((theme) => ({
43 | sidebar: {
44 | height: "100%",
45 | background: theme.palette.primary.main,
46 | [theme.breakpoints.up("md")]: {
47 | height: "100vh",
48 | },
49 | },
50 | }));
51 |
--------------------------------------------------------------------------------
/src/components/course/EditButton.js:
--------------------------------------------------------------------------------
1 | import React, { useState } from "react";
2 | import { Button, Dialog, DialogActions, DialogTitle } from "@material-ui/core";
3 | import CreateIcon from "@material-ui/icons/Create";
4 | import { Link, useParams } from "react-router-dom";
5 |
6 | export default function EditButton() {
7 | const [open, setOpen] = useState(false);
8 | const { id } = useParams();
9 | const handleOpen = () => {
10 | setOpen(true);
11 | };
12 |
13 | const handleCancel = () => {
14 | setOpen(false);
15 | };
16 |
17 | return (
18 |
19 |
20 | Edit
21 |
22 |
28 |
29 | {"Are you sure you want to edit this course?"}
30 |
31 |
32 |
33 | Cancel
34 |
35 |
36 |
40 | Edit
41 |
42 |
43 |
44 |
45 |
46 | );
47 | }
48 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "lms-app-amplify",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "@aws-amplify/ui-react": "^1.2.1",
7 | "@devexpress/dx-react-core": "^2.7.5",
8 | "@devexpress/dx-react-scheduler": "^2.7.5",
9 | "@devexpress/dx-react-scheduler-material-ui": "^2.7.5",
10 | "@material-ui/core": "^4.11.4",
11 | "@material-ui/icons": "^4.11.2",
12 | "@material-ui/lab": "*",
13 | "@testing-library/jest-dom": "^5.12.0",
14 | "@testing-library/react": "^11.2.7",
15 | "@testing-library/user-event": "^12.8.3",
16 | "aws-amplify": "^3.4.3",
17 | "boring-avatars": "^1.5.5",
18 | "draft-js": "^0.11.7",
19 | "draftjs-to-html": "^0.9.1",
20 | "immutable": "^3.8.2",
21 | "react": "^16.0.0",
22 | "react-dom": "^16.0.0",
23 | "react-draft-wysiwyg": "^1.14.7",
24 | "react-html-parser": "^2.0.2",
25 | "react-pdf": "^5.3.0",
26 | "react-player": "^2.9.0",
27 | "react-router-dom": "^5.2.0",
28 | "react-scripts": "4.0.3",
29 | "uuid": "^8.3.2",
30 | "web-vitals": "^1.1.2"
31 | },
32 | "scripts": {
33 | "start": "react-scripts start",
34 | "build": "react-scripts build",
35 | "test": "react-scripts test",
36 | "eject": "react-scripts eject"
37 | },
38 | "eslintConfig": {
39 | "extends": [
40 | "react-app",
41 | "react-app/jest"
42 | ]
43 | },
44 | "browserslist": {
45 | "production": [
46 | ">0.2%",
47 | "not dead",
48 | "not op_mini all"
49 | ],
50 | "development": [
51 | "last 1 chrome version",
52 | "last 1 firefox version",
53 | "last 1 safari version"
54 | ]
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/src/pages/discussions/AddComment.js:
--------------------------------------------------------------------------------
1 | import React, { useContext, useState } from "react";
2 | import { IconButton, TextField } from "@material-ui/core";
3 | import Avatar from "boring-avatars";
4 | import { UserContext } from "../../context/UserContext";
5 | import SendIcon from "@material-ui/icons/Send";
6 | import { DataStore } from "@aws-amplify/datastore";
7 | import { CommentModel } from "../../models";
8 |
9 | function CommentForm({ discussionID }) {
10 | const user = useContext(UserContext);
11 | const [comment, setComment] = useState();
12 |
13 | async function handleSubmit(e) {
14 | await DataStore.save(
15 | new CommentModel({
16 | comment: comment,
17 | createdBy: user.username,
18 | createdAt: new Date().toLocaleString(),
19 | postmodelID: discussionID,
20 | User: user,
21 | })
22 | );
23 | setComment("");
24 | }
25 | return (
26 | <>
27 |
28 |
33 |
setComment(e.target.value)}
42 | />
43 |
44 |
45 |
46 |
47 | >
48 | );
49 | }
50 |
51 | export default CommentForm;
52 |
--------------------------------------------------------------------------------
/src/index.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 0;
3 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen",
4 | "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue",
5 | sans-serif;
6 | -webkit-font-smoothing: antialiased;
7 | -moz-osx-font-smoothing: grayscale;
8 | }
9 |
10 | code {
11 | font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New",
12 | monospace;
13 | }
14 |
15 | * {
16 | margin: 0;
17 | padding: 0;
18 | }
19 |
20 | .home-editor {
21 | border: 1px solid #403f4f !important;
22 | padding: 10px !important;
23 | border-radius: 5px !important;
24 | height: 25vh !important;
25 | }
26 | .rdw-editor-main {
27 | height: 100%;
28 | overflow: auto;
29 | box-sizing: border-box;
30 | }
31 | .wrapper-class {
32 | box-sizing: content-box;
33 | }
34 |
35 | body {
36 | margin: 0;
37 | background-color: #525659;
38 | font-family: Segoe UI, Tahoma, sans-serif;
39 | }
40 |
41 | .Example__container {
42 | display: flex;
43 | flex-direction: column;
44 | align-items: center;
45 | margin: 10px 0;
46 | padding: 10px;
47 | z-index: -1;
48 | }
49 | .Example__container__load {
50 | margin-top: 1em;
51 | color: white;
52 | }
53 | .Example__container__document {
54 | margin: 1em 0;
55 | }
56 | .Example__container__document .react-pdf__Document {
57 | display: flex;
58 | flex-direction: column;
59 | align-items: center;
60 | z-index: -1;
61 | }
62 | .Example__container__document .react-pdf__Page {
63 | max-width: calc(100% - 2em);
64 | box-shadow: 0 0 8px rgba(0, 0, 0, 0.5);
65 | margin: 1em;
66 | z-index: -1;
67 | }
68 | .Example__container__document .react-pdf__Page canvas {
69 | max-width: 100%;
70 | height: auto !important;
71 | }
72 |
--------------------------------------------------------------------------------
/amplify/backend/api/lmsappamplify/stacks/CustomResources.json:
--------------------------------------------------------------------------------
1 | {
2 | "AWSTemplateFormatVersion": "2010-09-09",
3 | "Description": "An auto-generated nested stack.",
4 | "Metadata": {},
5 | "Parameters": {
6 | "AppSyncApiId": {
7 | "Type": "String",
8 | "Description": "The id of the AppSync API associated with this project."
9 | },
10 | "AppSyncApiName": {
11 | "Type": "String",
12 | "Description": "The name of the AppSync API",
13 | "Default": "AppSyncSimpleTransform"
14 | },
15 | "env": {
16 | "Type": "String",
17 | "Description": "The environment name. e.g. Dev, Test, or Production",
18 | "Default": "NONE"
19 | },
20 | "S3DeploymentBucket": {
21 | "Type": "String",
22 | "Description": "The S3 bucket containing all deployment assets for the project."
23 | },
24 | "S3DeploymentRootKey": {
25 | "Type": "String",
26 | "Description": "An S3 key relative to the S3DeploymentBucket that points to the root\nof the deployment directory."
27 | }
28 | },
29 | "Resources": {
30 | "EmptyResource": {
31 | "Type": "Custom::EmptyResource",
32 | "Condition": "AlwaysFalse"
33 | }
34 | },
35 | "Conditions": {
36 | "HasEnvironmentParameter": {
37 | "Fn::Not": [
38 | {
39 | "Fn::Equals": [
40 | {
41 | "Ref": "env"
42 | },
43 | "NONE"
44 | ]
45 | }
46 | ]
47 | },
48 | "AlwaysFalse": {
49 | "Fn::Equals": ["true", "false"]
50 | }
51 | },
52 | "Outputs": {
53 | "EmptyOutput": {
54 | "Description": "An empty output. You may delete this if you have at least one resource above.",
55 | "Value": ""
56 | }
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/src/pages/students/StudentDetails.js:
--------------------------------------------------------------------------------
1 | import React, { useEffect, useState } from "react";
2 | import { Grid, Typography } from "@material-ui/core";
3 | import { Container } from "@material-ui/core";
4 | import { DataStore } from "aws-amplify";
5 | import { CourseUser } from "../../models";
6 | import StudentCard from "./StudentCard";
7 | import noUser from "../../assests/no-user.svg";
8 | import { useParams } from "react-router-dom";
9 |
10 | function StudentDetails() {
11 | const { id } = useParams();
12 | const [enrolledStudents, setEnrolledStudents] = useState([]);
13 | useEffect(() => {
14 | async function getStudents() {
15 | const enrolledStudents = (await DataStore.query(CourseUser))
16 | .filter((pe) => pe.course.id === id)
17 | .map((pe) => pe.user);
18 | setEnrolledStudents(enrolledStudents);
19 | }
20 | getStudents();
21 | }, [id]);
22 |
23 | return (
24 |
25 |
26 |
27 | {enrolledStudents.length > 0 ? (
28 | <>
29 | {enrolledStudents.map((user, index) => (
30 |
31 |
32 |
33 | ))}
34 | >
35 | ) : (
36 |
37 |
38 | No Users Enrolled!
39 |
40 | )}
41 |
42 |
43 |
44 | );
45 | }
46 |
47 | export default StudentDetails;
48 |
--------------------------------------------------------------------------------
/src/components/dashboard/StudentDashboard.js:
--------------------------------------------------------------------------------
1 | import { Grid, Typography } from "@material-ui/core";
2 | import { Container } from "@material-ui/core";
3 | import { DataStore } from "aws-amplify";
4 | import React, { useContext, useEffect, useState } from "react";
5 | import { UserContext } from "../../context/UserContext";
6 | import { CourseUser } from "../../models";
7 | import CourseCard from "../course/CourseCard";
8 | import nodata from "../../assests/no-data.svg";
9 |
10 | function StudentDashboard() {
11 | const user = useContext(UserContext);
12 | const [enrolledCourses, setEnrolledCourses] = useState([]);
13 | useEffect(() => {
14 | async function getCourses() {
15 | const enrolledCourses = (await DataStore.query(CourseUser))
16 | .filter((c) => c.user.id === user.id)
17 | .map((c) => c.course);
18 | setEnrolledCourses(enrolledCourses);
19 | }
20 | getCourses();
21 | }, [user.id]);
22 |
23 | return (
24 |
25 |
26 |
27 | {enrolledCourses.length > 0 ? (
28 | <>
29 | {enrolledCourses.map((course, index) => (
30 |
31 |
32 |
33 | ))}
34 | >
35 | ) : (
36 |
37 |
38 | No Course!
39 |
40 | )}
41 |
42 |
43 |
44 | );
45 | }
46 |
47 | export default StudentDashboard;
48 |
--------------------------------------------------------------------------------
/amplify/backend/auth/lmsappamplify77733d32/parameters.json:
--------------------------------------------------------------------------------
1 | {
2 | "identityPoolName": "lmsappamplify77733d32_identitypool_77733d32",
3 | "allowUnauthenticatedIdentities": true,
4 | "resourceNameTruncated": "lmsapp77733d32",
5 | "userPoolName": "lmsappamplify77733d32_userpool_77733d32",
6 | "autoVerifiedAttributes": [
7 | "email"
8 | ],
9 | "mfaConfiguration": "OFF",
10 | "mfaTypes": [
11 | "SMS Text Message"
12 | ],
13 | "smsAuthenticationMessage": "Your authentication code is {####}",
14 | "smsVerificationMessage": "Your verification code is {####}",
15 | "emailVerificationSubject": "Your verification code",
16 | "emailVerificationMessage": "Your verification code is {####}",
17 | "defaultPasswordPolicy": false,
18 | "passwordPolicyMinLength": 8,
19 | "passwordPolicyCharacters": [],
20 | "requiredAttributes": [
21 | "email"
22 | ],
23 | "userpoolClientGenerateSecret": false,
24 | "userpoolClientRefreshTokenValidity": 30,
25 | "userpoolClientWriteAttributes": [
26 | "email"
27 | ],
28 | "userpoolClientReadAttributes": [
29 | "email"
30 | ],
31 | "userpoolClientLambdaRole": "lmsapp77733d32_userpoolclient_lambda_role",
32 | "userpoolClientSetAttributes": false,
33 | "sharedId": "77733d32",
34 | "resourceName": "lmsappamplify77733d32",
35 | "authSelections": "identityPoolAndUserPool",
36 | "authRoleArn": {
37 | "Fn::GetAtt": [
38 | "AuthRole",
39 | "Arn"
40 | ]
41 | },
42 | "unauthRoleArn": {
43 | "Fn::GetAtt": [
44 | "UnauthRole",
45 | "Arn"
46 | ]
47 | },
48 | "useDefault": "default",
49 | "userPoolGroupList": [],
50 | "serviceName": "Cognito",
51 | "usernameCaseSensitive": false,
52 | "dependsOn": []
53 | }
--------------------------------------------------------------------------------
/src/components/landingPage/Features.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { Grid, makeStyles } from "@material-ui/core";
3 | import lms from "../../assests/lms.png";
4 | import lms2 from "../../assests/lms2.png";
5 |
6 | import { Typography } from "@material-ui/core";
7 |
8 | function Features() {
9 | const classes = useStyles();
10 |
11 | return (
12 |
13 |
14 | What you'll get
15 |
16 |
17 |
18 | subtitle2. Lorem ipsum dolor sit amet, consectetur adipisicing elit.
19 | Quos blanditiis tenetur
20 |
21 |
22 |
23 |
30 |
37 |
38 |
39 | );
40 | }
41 |
42 | export default Features;
43 |
44 | const useStyles = makeStyles((theme) => ({
45 | container: {
46 | [theme.breakpoints.up("sm")]: {
47 | height: "100vh",
48 | },
49 | padding: "30px",
50 | backgroundColor: theme.palette.primary.main,
51 | color: "white",
52 | },
53 | img: { marginTop: "auto", marginBottom: "10px" },
54 | behind: {
55 | position: "relative",
56 | right: 50,
57 | top: 30,
58 | zIndex: 2,
59 | borderRadius: "10px",
60 | },
61 | front: {
62 | position: "relative",
63 | left: 50,
64 | zIndex: 1,
65 | borderRadius: "10px",
66 | },
67 | }));
68 |
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
12 |
13 |
17 |
18 |
26 | EduCouch LMS
27 |
28 |
29 | You need to enable JavaScript to run this app.
30 |
31 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/src/components/Error404.js:
--------------------------------------------------------------------------------
1 | import { Button, makeStyles, Typography } from "@material-ui/core";
2 | import React from "react";
3 | import { Link } from "react-router-dom";
4 |
5 | const useStyles = makeStyles((theme) => ({
6 | container: {
7 | display: "flex",
8 | flexDirection: "column",
9 | alignItems: "center",
10 | justifyContent: "center",
11 | height: "100vh",
12 | backgroundColor: "black",
13 | color: "white",
14 | },
15 | heading: {
16 | fontSize: "80px",
17 | background: "linear-gradient(to right, #C33764 0%, #1D2671 100%)",
18 | backgroundClip: "text",
19 | WebkitBackgroundClip: "text",
20 | WebkitTextFillColor: "transparent",
21 | },
22 | link: {
23 | textDecoration: "none",
24 | color: "white",
25 | },
26 | }));
27 | function Error404() {
28 | const classes = useStyles();
29 |
30 | return (
31 |
32 |
33 |
34 | OOPS!
35 |
36 | 404 - Page not found
37 |
38 |
39 | Sorry, the page you're looking for doesn't exist.
40 |
41 | Or maybe you doesn't have access to the page.
42 |
43 |
44 | Back to home
45 |
46 |
47 | - O R -
48 | {
52 | window.location.href = "/sigin";
53 | }}
54 | >
55 | Login
56 |
57 |
58 |
59 | );
60 | }
61 |
62 | export default Error404;
63 |
--------------------------------------------------------------------------------
/src/components/course/PublishButton.js:
--------------------------------------------------------------------------------
1 | import React, { useState } from "react";
2 | import { Button, Dialog, DialogActions, DialogTitle } from "@material-ui/core";
3 | import PublishIcon from "@material-ui/icons/Publish";
4 | import { useHistory, useParams } from "react-router-dom";
5 | import { DataStore } from "@aws-amplify/datastore";
6 | import { Course, CourseStatus } from "../../models";
7 |
8 | export default function PublishButton() {
9 | const [open, setOpen] = useState(false);
10 | const { id } = useParams();
11 | let history = useHistory();
12 | const handleOpen = () => {
13 | setOpen(true);
14 | };
15 |
16 | const handleCancel = () => {
17 | setOpen(false);
18 | };
19 |
20 | async function handlePublish() {
21 | const original = await DataStore.query(Course, id);
22 |
23 | await DataStore.save(
24 | Course.copyOf(original, (updated) => {
25 | updated.status = CourseStatus.PUBLISHED;
26 | })
27 | );
28 | history.push("/courses");
29 | }
30 | return (
31 |
32 |
33 | Publish
34 |
35 |
41 |
42 | {"Are you sure you want to publish this course?"}
43 |
44 |
45 |
46 | Cancel
47 |
48 |
49 |
54 | Publish
55 |
56 |
57 |
58 |
59 | );
60 | }
61 |
--------------------------------------------------------------------------------
/src/components/course/DeleteButton.js:
--------------------------------------------------------------------------------
1 | import React, { useState } from "react";
2 | import {
3 | Button,
4 | Dialog,
5 | DialogActions,
6 | DialogContent,
7 | DialogTitle,
8 | } from "@material-ui/core";
9 | import DeleteIcon from "@material-ui/icons/Delete";
10 | import { DataStore } from "@aws-amplify/datastore";
11 | import { Course } from "../../models";
12 | import { useHistory, useParams } from "react-router";
13 |
14 | export default function DeleteButton() {
15 | const [open, setOpen] = useState(false);
16 | let history = useHistory();
17 | const { id } = useParams();
18 |
19 | const handleOpen = () => {
20 | setOpen(true);
21 | };
22 |
23 | const handleCancel = () => {
24 | setOpen(false);
25 | };
26 |
27 | async function handleDelete() {
28 | const todelete = await DataStore.query(Course, id);
29 | DataStore.delete(todelete);
30 | setOpen(false);
31 | history.goBack();
32 | }
33 |
34 | return (
35 |
36 |
42 | Delete
43 |
44 |
50 |
51 | {"Are you sure you want to delete this course?"}
52 |
53 | You cannot undo this action!
54 |
55 |
56 | Cancel
57 |
58 |
63 | Delete
64 |
65 |
66 |
67 |
68 | );
69 | }
70 |
--------------------------------------------------------------------------------
/src/pages/syllabus/PreviewSyllabus.js:
--------------------------------------------------------------------------------
1 | import React, { useContext, useEffect, useState } from "react";
2 | import { Button } from "@material-ui/core";
3 | import { Storage } from "aws-amplify";
4 | import { Document, Page } from "react-pdf/dist/esm/entry.webpack";
5 | import { Container } from "@material-ui/core";
6 | import { UserContext } from "../../context/UserContext";
7 |
8 | const options = {
9 | cMapUrl: "cmaps/",
10 | cMapPacked: true,
11 | };
12 |
13 | function PreviewSyllabus({ syllabus, handleDelete }) {
14 | const user = useContext(UserContext);
15 | const [url, setUrl] = useState();
16 | const [numPages, setNumPages] = useState(null);
17 | function onDocumentLoadSuccess({ numPages: nextNumPages }) {
18 | setNumPages(nextNumPages);
19 | }
20 | useEffect(() => {
21 | loadFileUrl(syllabus);
22 | }, [syllabus]);
23 |
24 | const loadFileUrl = async (syllabus) => {
25 | await Storage.get(syllabus.S3Key).then((res) => {
26 | setUrl(res);
27 | });
28 | };
29 | return (
30 | <>
31 | {user.username === syllabus.uploadedBy && (
32 | {
34 | handleDelete(syllabus.id);
35 | }}
36 | variant="contained"
37 | color="primary"
38 | style={{ float: "right" }}
39 | >
40 | Delete
41 |
42 | )}
43 |
44 |
45 |
50 | {Array.from(new Array(numPages), (el, index) => (
51 |
52 | {" "}
53 |
54 |
55 | Page {index + 1} of {numPages}
56 |
57 |
58 | ))}
59 |
60 |
61 | >
62 | );
63 | }
64 |
65 | export default PreviewSyllabus;
66 |
--------------------------------------------------------------------------------
/src/pages/discussions/DisplayComments.js:
--------------------------------------------------------------------------------
1 | import React, { useEffect, useState } from "react";
2 | import { DataStore } from "@aws-amplify/datastore";
3 | import { CommentModel } from "../../models";
4 | import Comment from "./Comment";
5 | import SmsIcon from "@material-ui/icons/Sms";
6 | import { Box } from "@material-ui/core";
7 |
8 | function DisplayComments({ discussionID }) {
9 | const [comments, setComments] = useState([]);
10 | const [open, setOpen] = useState(false);
11 | const [text, setText] = useState("View");
12 |
13 | useEffect(() => {
14 | async function getComments() {
15 | const models = (await DataStore.query(CommentModel)).filter(
16 | (c) => c.postmodelID === discussionID
17 | );
18 | setComments(models);
19 | }
20 | getComments();
21 | const subscription = DataStore.observe(CommentModel).subscribe((msg) => {
22 | getComments();
23 | });
24 | return () => subscription.unsubscribe();
25 | }, [discussionID]);
26 |
27 | async function handleDelete(id) {
28 | const todelete = await DataStore.query(CommentModel, id);
29 | DataStore.delete(todelete);
30 | }
31 | return (
32 |
33 | {
35 | setOpen(!open);
36 | if (!open) setText("Hide");
37 | else setText("View");
38 | }}
39 | >
40 | {comments.length > 0 && (
41 |
49 |
50 | {text} {comments.length} comment(s)
51 |
52 | )}
53 |
54 | {open && (
55 |
56 | {comments.map((comment) => (
57 | <>
58 |
59 | >
60 | ))}
61 |
62 | )}
63 |
64 | );
65 | }
66 |
67 | export default DisplayComments;
68 |
--------------------------------------------------------------------------------
/src/pages/discussions/DisplayDiscussions.js:
--------------------------------------------------------------------------------
1 | import React, { useEffect, useState } from "react";
2 | import { DataStore, Predicates, SortDirection } from "@aws-amplify/datastore";
3 | import { PostModel } from "../../models";
4 | import { useParams } from "react-router";
5 | import { Box, Typography } from "@material-ui/core";
6 | import NewDiscussion from "./NewDiscussion";
7 | import Discussion from "./Discussion";
8 | import noDiscussionImg from "../../assests/noDiscussionImg.svg";
9 |
10 | function DisplayDiscussions() {
11 | const { id } = useParams();
12 | const [discussions, setDiscussions] = useState([]);
13 | useEffect(() => {
14 | async function getDiscussions() {
15 | const models = (
16 | await DataStore.query(PostModel, Predicates.ALL, {
17 | sort: (s) => s.createdAt(SortDirection.DESCENDING),
18 | })
19 | ).filter((c) => c.courseID === id);
20 |
21 | setDiscussions(models);
22 | }
23 | getDiscussions();
24 | const subscription = DataStore.observe(PostModel).subscribe((msg) => {
25 | getDiscussions();
26 | });
27 |
28 | return () => subscription.unsubscribe();
29 | }, [id]);
30 |
31 | async function handleDelete(id) {
32 | const todelete = await DataStore.query(PostModel, id);
33 | DataStore.delete(todelete);
34 | }
35 | return (
36 |
37 |
38 | {discussions.length > 0 ? (
39 | <>
40 | {discussions.map((discussion, index) => (
41 |
46 | ))}
47 | >
48 | ) : (
49 |
50 |
56 | No Discussions!
57 |
58 | )}
59 |
60 | );
61 | }
62 |
63 | export default DisplayDiscussions;
64 |
--------------------------------------------------------------------------------
/src/components/landingPage/Benefit.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { Grid, makeStyles } from "@material-ui/core";
3 | import org from "../../assests/org.svg";
4 | import student from "../../assests/student.svg";
5 | import teacher from "../../assests/teacher.svg";
6 | import { Typography } from "@material-ui/core";
7 |
8 | function Benefit() {
9 | const classes = useStyles();
10 |
11 | return (
12 |
13 |
14 | Who will benefit
15 |
16 |
17 |
18 | Student
19 |
20 | subtitle2. Lorem ipsum dolor sit amet, consectetur adipisicing elit.
21 | Quos blanditiis tenetur
22 |
23 |
24 |
25 |
26 | Teacher
27 |
28 | subtitle2. Lorem ipsum dolor sit amet, consectetur adipisicing elit.
29 | Quos blanditiis tenetur
30 |
31 |
32 |
33 |
34 | University/School
35 |
36 | subtitle2. Lorem ipsum dolor sit amet, consectetur adipisicing elit.
37 | Quos blanditiis tenetur
38 |
39 |
40 |
41 | );
42 | }
43 |
44 | export default Benefit;
45 |
46 | const useStyles = makeStyles((theme) => ({
47 | container: {
48 | [theme.breakpoints.up("sm")]: {
49 | height: "100vh",
50 | },
51 | height: "120vh",
52 | background: "white",
53 | padding: "30px",
54 | },
55 | grid: {},
56 | }));
57 |
--------------------------------------------------------------------------------
/src/pages/discussions/Comment.js:
--------------------------------------------------------------------------------
1 | import React, { useContext } from "react";
2 | import { Box, Divider, Paper, Typography } from "@material-ui/core";
3 | import Avatar from "boring-avatars";
4 | import ClearIcon from "@material-ui/icons/Clear";
5 | import { UserContext } from "../../context/UserContext";
6 |
7 | function Comment({ comment, handleDelete }) {
8 | const user = useContext(UserContext);
9 | return (
10 |
11 |
12 |
13 |
19 |
26 |
27 | {comment.User.name}
28 |
29 |
33 | {comment.createdAt}
34 |
35 |
36 |
37 |
38 |
39 | {comment.comment}
40 |
41 | {user.username === comment.User.username && (
42 | handleDelete(comment.id)}
49 | >
50 |
51 |
52 | )}
53 |
54 |
55 |
56 |
57 | );
58 | }
59 |
60 | export default Comment;
61 |
--------------------------------------------------------------------------------
/src/pages/assignments/DisplayAssignments.js:
--------------------------------------------------------------------------------
1 | import React, { useContext, useEffect, useState } from "react";
2 | import { Box, Container } from "@material-ui/core";
3 | import nodata from "../../assests/nodata.svg";
4 | import { DataStore } from "@aws-amplify/datastore";
5 | import { AssignmentModel } from "../../models";
6 | import AddAssignment from "./AddAssignment";
7 | import Assignment from "./Assignment";
8 | import { useParams } from "react-router";
9 | import { UserContext } from "../../context/UserContext";
10 | import useCourses from "../../customHook/useCourses";
11 |
12 | function DisplayAssignments() {
13 | const { id } = useParams();
14 | const user = useContext(UserContext);
15 | const course = useCourses(id);
16 |
17 | const [assignments, setAssignments] = useState([]);
18 |
19 | useEffect(() => {
20 | async function getAssignments() {
21 | const models = (await DataStore.query(AssignmentModel)).filter(
22 | (c) => c.courseID === id
23 | );
24 | setAssignments(models);
25 | }
26 | getAssignments();
27 | const subscription = DataStore.observe(AssignmentModel).subscribe((msg) => {
28 | getAssignments();
29 | });
30 | return () => subscription.unsubscribe();
31 | }, [id]);
32 |
33 | return (
34 |
35 |
36 | {user.username === course.createdBy && user.isEducator && (
37 |
38 |
39 |
40 | )}
41 |
42 | {assignments.length > 0 ? (
43 |
50 | {assignments.map((assignment) => (
51 |
54 | ))}
55 |
56 | ) : (
57 |
58 | )}
59 |
60 |
61 | );
62 | }
63 |
64 | export default DisplayAssignments;
65 |
--------------------------------------------------------------------------------
/src/components/course/EditCourse.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { useTheme } from "@material-ui/core/styles";
3 | import { useStyles } from "../../utils/useStyles";
4 | import { Box, Drawer, Grid, Hidden } from "@material-ui/core";
5 | import MenuIcon from "@material-ui/icons/Menu";
6 | import Navigation from "../../components/Navigation";
7 | import Navlinks from "../../components/course/Navlinks";
8 | import Edit from "./Edit";
9 |
10 | export default function EditCourse() {
11 | const classes = useStyles();
12 | const theme = useTheme();
13 | const [mobileOpen, setMobileOpen] = React.useState(false);
14 |
15 | const handleDrawerToggle = () => {
16 | setMobileOpen(!mobileOpen);
17 | };
18 |
19 | const drawer = (
20 |
21 |
22 |
23 | );
24 | return (
25 |
26 |
27 |
28 |
35 |
36 |
37 |
38 | {" "}
39 |
40 |
41 |
42 |
43 |
44 |
56 | {drawer}
57 |
58 |
59 |
60 | {drawer}
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 | );
71 | }
72 |
--------------------------------------------------------------------------------
/src/components/account/IsEducatorSwitch.js:
--------------------------------------------------------------------------------
1 | import React, { useContext } from "react";
2 | import { Button, Grid, Switch } from "@material-ui/core";
3 | import { UserContext } from "../../context/UserContext";
4 | import { DataStore } from "@aws-amplify/datastore";
5 | import { User } from "../../models";
6 | import Label from "./Label";
7 | import MessageAlert from "./MessageAlert";
8 |
9 | function IsEducatorSwitch() {
10 | const user = useContext(UserContext);
11 | const [open, setOpen] = React.useState(false);
12 |
13 | const handleClose = (event, reason) => {
14 | if (reason === "clickaway") {
15 | return;
16 | }
17 | setOpen(false);
18 | };
19 |
20 | const [state, setState] = React.useState({
21 | checkedA: user.isEducator,
22 | });
23 | const handleChange = (event) => {
24 | setState({ ...state, [event.target.name]: event.target.checked });
25 | };
26 |
27 | async function handleSubmit(e) {
28 | e.preventDefault();
29 | const original = await DataStore.query(User, user.id);
30 | const data = await DataStore.save(
31 | User.copyOf(original, (updated) => {
32 | updated.isEducator = state.checkedA;
33 | })
34 | );
35 | if (data) setOpen(true);
36 | }
37 | return (
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
51 |
52 |
53 | {user.isEducator === state.checkedA ? (
54 |
60 | Save
61 |
62 | ) : (
63 |
64 | Save
65 |
66 | )}
67 |
68 |
69 | );
70 | }
71 |
72 | export default IsEducatorSwitch;
73 |
--------------------------------------------------------------------------------
/src/pages/announcements/EditAnnouncement.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { useTheme } from "@material-ui/core/styles";
3 | import { useStyles } from "../../utils/useStyles";
4 | import { Box, Drawer, Grid, Hidden } from "@material-ui/core";
5 | import MenuIcon from "@material-ui/icons/Menu";
6 | import Navigation from "../../components/Navigation";
7 | import Navlinks from "../../components/course/Navlinks";
8 | import Edit from "./Edit";
9 |
10 | export default function EditAnnouncement() {
11 | const classes = useStyles();
12 | const theme = useTheme();
13 | const [mobileOpen, setMobileOpen] = React.useState(false);
14 |
15 | const handleDrawerToggle = () => {
16 | setMobileOpen(!mobileOpen);
17 | };
18 |
19 | const drawer = (
20 |
21 |
22 |
23 | );
24 | return (
25 |
26 |
27 |
28 |
35 |
36 |
37 |
38 | {" "}
39 |
40 |
41 |
42 |
43 |
44 |
56 | {drawer}
57 |
58 |
59 |
60 | {drawer}
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 | );
71 | }
72 |
--------------------------------------------------------------------------------
/src/pages/syllabus/DisplaySyllabus.js:
--------------------------------------------------------------------------------
1 | import React, { useContext, useEffect, useState } from "react";
2 | import { Container } from "@material-ui/core";
3 | import nodata from "../../assests/nodata.svg";
4 | import { DataStore } from "@aws-amplify/datastore";
5 | import { SyllabusModel } from "../../models";
6 | import { useParams } from "react-router";
7 | import NewSyllabus from "./NewSyllabus";
8 | import PreviewSyllabus from "./PreviewSyllabus";
9 | import { UserContext } from "../../context/UserContext";
10 | import useCourses from "../../customHook/useCourses";
11 |
12 | function DisplaySyllabus() {
13 | const { id } = useParams();
14 | const course = useCourses(id);
15 | const user = useContext(UserContext);
16 |
17 | const [syllabus, setSyllabus] = useState([]);
18 | useEffect(() => {
19 | async function getSyllabus() {
20 | const models = (await DataStore.query(SyllabusModel)).filter(
21 | (c) => c.courseID === id
22 | );
23 | setSyllabus(models);
24 | }
25 |
26 | getSyllabus();
27 | const subscription = DataStore.observe(SyllabusModel).subscribe((msg) => {
28 | getSyllabus();
29 | });
30 | return () => subscription.unsubscribe();
31 | }, [id]);
32 |
33 | async function handleDelete(id) {
34 | const todelete = await DataStore.query(SyllabusModel, id);
35 | DataStore.delete(todelete);
36 | }
37 | return (
38 |
39 |
40 | {syllabus.length > 0 ? (
41 |
48 | {syllabus.map((item, index) => (
49 |
52 | ))}
53 |
54 | ) : (
55 |
56 | {user.username === course.createdBy && user.isEducator && (
57 |
58 | )}
59 |
60 |
61 | )}
62 |
63 |
64 | );
65 | }
66 |
67 | export default DisplaySyllabus;
68 |
--------------------------------------------------------------------------------
/src/components/course/ShareLinkModal.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { makeStyles } from "@material-ui/core/styles";
3 | import Modal from "@material-ui/core/Modal";
4 | import { Button, TextField } from "@material-ui/core";
5 |
6 | const useStyles = makeStyles((theme) => ({
7 | paper: {
8 | position: "absolute",
9 | width: 350,
10 | backgroundColor: theme.palette.background.paper,
11 | boxShadow: theme.shadows[5],
12 | padding: theme.spacing(2, 4, 3),
13 | borderRadius: "10px",
14 | },
15 | }));
16 |
17 | export default function SimpleModal({ id }) {
18 | const classes = useStyles();
19 | const [open, setOpen] = React.useState(false);
20 | const [buttonText, setButtonText] = React.useState("Copy Link");
21 | const url = window.location.protocol + window.location.host + "/course/" + id;
22 | const handleOpen = () => {
23 | setOpen(true);
24 | };
25 |
26 | const handleClose = () => {
27 | setButtonText("Copy Link");
28 | setOpen(false);
29 | };
30 |
31 | const body = (
32 |
40 |
Share link with your friends!
41 |
49 |
50 | {
55 | navigator.clipboard.writeText(url);
56 | setButtonText("Copied!");
57 | setTimeout(function () {
58 | handleClose();
59 | }, 1000);
60 | }}
61 | >
62 | {buttonText}
63 |
64 |
65 | );
66 |
67 | return (
68 |
69 |
70 | Share
71 |
72 |
73 |
79 | {body}
80 |
81 |
82 | );
83 | }
84 |
--------------------------------------------------------------------------------
/src/components/course/CourseCard.js:
--------------------------------------------------------------------------------
1 | import React, { useContext } from "react";
2 | import {
3 | Button,
4 | Card,
5 | CardActions,
6 | CardContent,
7 | CardHeader,
8 | Typography,
9 | } from "@material-ui/core";
10 | import FavoriteBorderRoundedIcon from "@material-ui/icons/FavoriteBorderRounded";
11 | import ShareLinkModal from "./ShareLinkModal";
12 | import ReactHtmlParser from "react-html-parser";
13 | import Avatar from "boring-avatars";
14 | import { UserContext } from "../../context/UserContext";
15 | import ContinueButton from "./ContinueButton";
16 | import EnrollButton from "./EnrollButton";
17 |
18 | function CourseCard({ course, handleUpdate }) {
19 | const user = useContext(UserContext);
20 |
21 | return (
22 |
23 |
24 |
32 | }
33 | title={course.title}
34 | fontWeight="900"
35 | subheader={"By " + course.createdBy}
36 | />
37 |
38 |
39 | Created on : {course.createdAt.split(",")[0]}
40 | {ReactHtmlParser(course.desc.length) > 80 ? (
41 | {ReactHtmlParser(course.desc.substring(0, 80))}..
42 | ) : (
43 | {ReactHtmlParser(course.desc)}
44 | )}
45 |
46 |
47 |
54 |
55 |
56 |
57 |
58 |
59 | {course.enrolledStudents.includes(user.id) ? (
60 |
61 | ) : (
62 | <>
63 | {!user.isEducator && (
64 |
65 | )}
66 | >
67 | )}
68 |
69 |
70 |
71 | );
72 | }
73 |
74 | export default CourseCard;
75 |
--------------------------------------------------------------------------------
/src/pages/lessons/Lessons.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { useTheme } from "@material-ui/core/styles";
3 | import { useStyles } from "../../utils/useStyles";
4 | import { Box, Drawer, Grid, Hidden } from "@material-ui/core";
5 | import MenuIcon from "@material-ui/icons/Menu";
6 | import Navigation from "../../components/Navigation";
7 | import Navlinks from "../../components/course/Navlinks";
8 | import LessonsList from "./LessonsList";
9 | import Breadcrumb from "../../components/Breadcrumb";
10 |
11 | export default function Lessons() {
12 | const classes = useStyles();
13 | const theme = useTheme();
14 | const [mobileOpen, setMobileOpen] = React.useState(false);
15 |
16 | const handleDrawerToggle = () => {
17 | setMobileOpen(!mobileOpen);
18 | };
19 |
20 | const drawer = (
21 |
22 |
23 |
24 | );
25 | return (
26 |
27 |
28 |
29 |
36 |
37 |
38 |
39 | {" "}
40 |
41 |
42 |
43 |
44 |
45 |
57 | {drawer}
58 |
59 |
60 |
61 | {drawer}
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 | );
75 | }
76 |
--------------------------------------------------------------------------------
/src/components/landingPage/Hero.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { Button, makeStyles, Typography } from "@material-ui/core";
3 | import { Container, Grid } from "@material-ui/core";
4 | import { Link } from "react-router-dom";
5 | import hero from "../../assests/hero.svg";
6 | import ArrowForwardIosIcon from "@material-ui/icons/ArrowForwardIos";
7 |
8 | function Hero() {
9 | const classes = useStyles();
10 | return (
11 |
12 |
17 |
18 |
19 |
20 | Everything you need to manage your educational institution!
21 |
22 |
27 | A cloud-based learning management system is a platform intended
28 | for educational institution for managing their educational
29 | content.
30 |
31 |
32 |
37 |
47 |
48 | Get Started
49 |
{" "}
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 | );
61 | }
62 |
63 | export default Hero;
64 |
65 | const useStyles = makeStyles((theme) => ({
66 | button: {
67 | margin: "50px 10px 0px 10px",
68 | padding: "13px 18px",
69 | },
70 | subtitle: {
71 | [theme.breakpoints.up("md")]: {
72 | padding: "0 120px",
73 | },
74 | },
75 | }));
76 |
--------------------------------------------------------------------------------
/src/pages/students/Students.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { useTheme } from "@material-ui/core/styles";
3 | import { useStyles } from "../../utils/useStyles";
4 | import { Box, Drawer, Grid, Hidden } from "@material-ui/core";
5 | import MenuIcon from "@material-ui/icons/Menu";
6 | import Navigation from "../../components/Navigation";
7 | import Navlinks from "../../components/course/Navlinks";
8 | import Breadcrumb from "../../components/Breadcrumb";
9 | import StudentDetails from "./StudentDetails";
10 |
11 | export default function Discussions() {
12 | const classes = useStyles();
13 | const theme = useTheme();
14 | const [mobileOpen, setMobileOpen] = React.useState(false);
15 |
16 | const handleDrawerToggle = () => {
17 | setMobileOpen(!mobileOpen);
18 | };
19 |
20 | const drawer = (
21 |
22 |
23 |
24 | );
25 | return (
26 |
27 |
28 |
29 |
36 |
37 |
38 |
39 | {" "}
40 |
41 |
42 |
43 |
44 |
45 |
57 | {drawer}
58 |
59 |
60 |
61 | {drawer}
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 | );
75 | }
76 |
--------------------------------------------------------------------------------
/src/pages/syllabus/Syllabus.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { useTheme } from "@material-ui/core/styles";
3 | import { useStyles } from "../../utils/useStyles";
4 | import { Box, Drawer, Grid, Hidden } from "@material-ui/core";
5 | import MenuIcon from "@material-ui/icons/Menu";
6 | import Navigation from "../../components/Navigation";
7 | import Navlinks from "../../components/course/Navlinks";
8 | import Breadcrumb from "../../components/Breadcrumb";
9 | import DisplaySyllabus from "./DisplaySyllabus";
10 |
11 | export default function Syllabus() {
12 | const classes = useStyles();
13 | const theme = useTheme();
14 | const [mobileOpen, setMobileOpen] = React.useState(false);
15 |
16 | const handleDrawerToggle = () => {
17 | setMobileOpen(!mobileOpen);
18 | };
19 |
20 | const drawer = (
21 |
22 |
23 |
24 | );
25 | return (
26 |
27 |
28 |
29 |
36 |
37 |
38 |
39 | {" "}
40 |
41 |
42 |
43 |
44 |
45 |
57 | {drawer}
58 |
59 |
60 |
61 | {drawer}
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 | );
75 | }
76 |
--------------------------------------------------------------------------------
/src/pages/discussions/Discussions.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { useTheme } from "@material-ui/core/styles";
3 | import { useStyles } from "../../utils/useStyles";
4 | import { Box, Drawer, Grid, Hidden } from "@material-ui/core";
5 | import MenuIcon from "@material-ui/icons/Menu";
6 | import Navigation from "../../components/Navigation";
7 | import Navlinks from "../../components/course/Navlinks";
8 | import Breadcrumb from "../../components/Breadcrumb";
9 | import DisplayDiscussions from "./DisplayDiscussions";
10 |
11 | export default function Discussions() {
12 | const classes = useStyles();
13 | const theme = useTheme();
14 | const [mobileOpen, setMobileOpen] = React.useState(false);
15 |
16 | const handleDrawerToggle = () => {
17 | setMobileOpen(!mobileOpen);
18 | };
19 |
20 | const drawer = (
21 |
22 |
23 |
24 | );
25 | return (
26 |
27 |
28 |
29 |
36 |
37 |
38 |
39 | {" "}
40 |
41 |
42 |
43 |
44 |
45 |
57 | {drawer}
58 |
59 |
60 |
61 | {drawer}
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 | );
75 | }
76 |
--------------------------------------------------------------------------------
/src/pages/assignments/Assignments.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { useTheme } from "@material-ui/core/styles";
3 | import { useStyles } from "../../utils/useStyles";
4 | import { Box, Drawer, Grid, Hidden } from "@material-ui/core";
5 | import MenuIcon from "@material-ui/icons/Menu";
6 | import Navigation from "../../components/Navigation";
7 | import Navlinks from "../../components/course/Navlinks";
8 | import Breadcrumb from "../../components/Breadcrumb";
9 | import DisplayAssignments from "./DisplayAssignments";
10 |
11 | export default function Assignments() {
12 | const classes = useStyles();
13 | const theme = useTheme();
14 | const [mobileOpen, setMobileOpen] = React.useState(false);
15 |
16 | const handleDrawerToggle = () => {
17 | setMobileOpen(!mobileOpen);
18 | };
19 |
20 | const drawer = (
21 |
22 |
23 |
24 | );
25 |
26 | return (
27 |
28 |
29 |
30 |
37 |
38 |
39 |
40 | {" "}
41 |
42 |
43 |
44 |
45 |
46 |
58 | {drawer}
59 |
60 |
61 |
62 | {drawer}
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 | );
76 | }
77 |
--------------------------------------------------------------------------------
/src/pages/announcements/Announcements.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { useTheme } from "@material-ui/core/styles";
3 | import { useStyles } from "../../utils/useStyles";
4 | import { Box, Drawer, Grid, Hidden } from "@material-ui/core";
5 | import MenuIcon from "@material-ui/icons/Menu";
6 | import Navigation from "../../components/Navigation";
7 | import Navlinks from "../../components/course/Navlinks";
8 | import Breadcrumb from "../../components/Breadcrumb";
9 | import DisplayAnnouncements from "./DisplayAnnouncements";
10 |
11 | export default function Announcements() {
12 | const classes = useStyles();
13 | const theme = useTheme();
14 | const [mobileOpen, setMobileOpen] = React.useState(false);
15 |
16 | const handleDrawerToggle = () => {
17 | setMobileOpen(!mobileOpen);
18 | };
19 |
20 | const drawer = (
21 |
22 |
23 |
24 | );
25 | return (
26 |
27 |
28 |
29 |
36 |
37 |
38 |
39 | {" "}
40 |
41 |
42 |
43 |
44 |
45 |
57 | {drawer}
58 |
59 |
60 |
61 | {drawer}
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 | );
75 | }
76 |
--------------------------------------------------------------------------------
/src/pages/announcements/DisplayAnnouncements.js:
--------------------------------------------------------------------------------
1 | import React, { useContext, useEffect, useState } from "react";
2 | import Announcement from "./Announcement";
3 | import NewAnnouncement from "./NewAnnouncement";
4 | import { DataStore, Predicates, SortDirection } from "@aws-amplify/datastore";
5 | import { AnnouncementsModel } from "../../models";
6 | import { useParams } from "react-router";
7 | import { UserContext } from "../../context/UserContext";
8 | import { Box, Typography } from "@material-ui/core";
9 | import announcementImg from "../../assests/announcementImg.svg";
10 | import useCourses from "../../customHook/useCourses";
11 |
12 | function DisplayAnnouncement() {
13 | const { id } = useParams();
14 | const user = useContext(UserContext);
15 | const course = useCourses(id);
16 | const [announcements, setAnnouncements] = useState([]);
17 |
18 | useEffect(() => {
19 | async function getAnnouncements() {
20 | const models = (
21 | await DataStore.query(AnnouncementsModel, Predicates.ALL, {
22 | sort: (s) => s.createdAt(SortDirection.DESCENDING),
23 | })
24 | ).filter((c) => c.courseID === id);
25 |
26 | setAnnouncements(models);
27 | }
28 | getAnnouncements();
29 | const subscription = DataStore.observe(AnnouncementsModel).subscribe(
30 | (msg) => {
31 | getAnnouncements();
32 | }
33 | );
34 |
35 | return () => subscription.unsubscribe();
36 | }, [id]);
37 |
38 | async function handleDelete(id) {
39 | const todelete = await DataStore.query(AnnouncementsModel, id);
40 | DataStore.delete(todelete);
41 | }
42 | return (
43 |
44 | {course.createdBy === user.username && user.isEducator && (
45 |
46 | )}
47 | {announcements.length > 0 ? (
48 | <>
49 | {announcements.map((announcement, index) => (
50 |
55 | ))}
56 | >
57 | ) : (
58 |
59 |
65 | No Announcement posted!
66 |
67 | )}
68 |
69 | );
70 | }
71 |
72 | export default DisplayAnnouncement;
73 |
--------------------------------------------------------------------------------
/src/components/course/CourseList.js:
--------------------------------------------------------------------------------
1 | import React, { useContext, useEffect, useState } from "react";
2 | import { DataStore, SortDirection } from "@aws-amplify/datastore";
3 | import { Course, CourseStatus, CourseUser } from "../../models";
4 | import { API } from "aws-amplify";
5 | import * as mutations from "../../graphql/mutations";
6 | import { Container, Grid, Typography } from "@material-ui/core";
7 | import { UserContext } from "../../context/UserContext";
8 | import CourseCard from "./CourseCard";
9 | import nodata from "../../assests/no-data.svg";
10 |
11 | function CourseList() {
12 | const [courses, setCourses] = useState([]);
13 | const user = useContext(UserContext);
14 |
15 | useEffect(() => {
16 | getCourses();
17 | const subscription = DataStore.observe(Course).subscribe((msg) => {
18 | getCourses();
19 | });
20 | return () => subscription.unsubscribe();
21 | }, []);
22 |
23 | async function getCourses() {
24 | const models = await DataStore.query(
25 | Course,
26 | (c) => c.status("eq", CourseStatus.PUBLISHED),
27 | {
28 | sort: (s) => s.createdAt(SortDirection.DESCENDING),
29 | }
30 | );
31 | setCourses(models);
32 | }
33 | async function handleUpdate(courseID) {
34 | const models = await DataStore.query(Course, courseID);
35 |
36 | await DataStore.save(
37 | new CourseUser({
38 | course: models,
39 | user: user,
40 | })
41 | );
42 | const updateCourseDetails = {
43 | id: courseID,
44 | enrolledStudents: user.id,
45 | };
46 | await API.graphql({
47 | query: mutations.updateCourse,
48 | variables: { input: updateCourseDetails },
49 | });
50 | }
51 | return (
52 |
53 |
54 |
55 | {courses.length > 0 ? (
56 | <>
57 | {courses.map((course, index) => (
58 |
59 |
64 |
65 | ))}
66 | >
67 | ) : (
68 |
69 |
70 | No Course!
71 |
72 | )}
73 |
74 |
75 |
76 | );
77 | }
78 |
79 | export default CourseList;
80 |
--------------------------------------------------------------------------------
/src/pages/lessons/LessonsList.js:
--------------------------------------------------------------------------------
1 | import React, { useContext, useEffect, useState } from "react";
2 | import ReactPlayer from "react-player/youtube";
3 | import NewLesson from "./NewLesson";
4 | import { DataStore } from "@aws-amplify/datastore";
5 | import { Lesson } from "../../models";
6 | import { useParams } from "react-router";
7 | import { Avatar, Card, CardHeader, Grid, IconButton } from "@material-ui/core";
8 | import { Delete, Edit } from "@material-ui/icons";
9 | import { UserContext } from "../../context/UserContext";
10 | import useCourses from "../../customHook/useCourses";
11 |
12 | function Lessons() {
13 | let { id } = useParams();
14 | const user = useContext(UserContext);
15 | const [lessons, setLessons] = useState([]);
16 | const course = useCourses(id);
17 |
18 | useEffect(() => {
19 | async function getLessons() {
20 | const models = (await DataStore.query(Lesson)).filter(
21 | (c) => c.courseID === id
22 | );
23 | setLessons(models);
24 | }
25 |
26 | getLessons();
27 | const subscription = DataStore.observe(Lesson).subscribe((msg) => {
28 | getLessons();
29 | });
30 | return () => subscription.unsubscribe();
31 | }, [id]);
32 |
33 | async function handleDelete(id) {
34 | const modelToDelete = await DataStore.query(Lesson, id);
35 | DataStore.delete(modelToDelete);
36 | }
37 | return (
38 |
39 | {course.createdBy === user.username && user.isEducator && }
40 |
41 | {lessons.map((lesson, index) => (
42 |
48 |
49 |
55 |
56 |
57 |
58 | {
60 | handleDelete(lesson.id);
61 | }}
62 | >
63 |
64 |
65 |
66 | )
67 | }
68 | avatar={{index + 1} }
69 | />
70 |
75 |
76 |
77 | ))}
78 |
79 |
80 | );
81 | }
82 |
83 | export default Lessons;
84 |
--------------------------------------------------------------------------------
/CodeSnippets.md:
--------------------------------------------------------------------------------
1 | # Code Snippets for Data Store
2 |
3 | ## Table of Contents
4 | - [Store Data ](#storing-data)
5 | - [Query Data](#query-data)
6 | - [Fetch single data](#case-1--)
7 | - [Fetch all the data](#case-2--)
8 | - [Fetch filtered data](#case-3--)
9 | - [Fetch data in ascending/descending order](#case-4--)
10 | - [Real Time Data](#real-time-data)
11 | - [Delete Data](#delete-data)
12 |
13 | ## Storing data
14 | > To save data in the data model
15 |
16 | ```javascript
17 | async function handleSubmit(e){
18 | e.preventDefault();
19 | await DataStore.save(
20 | new Comment({
21 | "postID": postID, // postID received as props
22 | "createdBy": createdBy, //
23 | "content": comment,
24 | "createdAt":new Date().toLocaleString()
25 | })
26 | );
27 | console.log(comment, postID, createdBy)
28 | }
29 | ```
30 |
31 | ## Query Data
32 |
33 | ### Case 1 -
34 | > To fetch all the data from data model
35 |
36 | ```javascript
37 | async function fetchComments(){
38 | const comment = await DataStore.query(Comment);
39 | }
40 | ```
41 |
42 | ### Case 2 -
43 | > To fetch single data from data model
44 |
45 | ```javascript
46 | async function fetchComments(id){
47 | const comment = await DataStore.query(Comment, id);
48 | console.log(comment);
49 | }
50 | ```
51 |
52 | ### Case 3 -
53 | > To fetch filtered data from data model
54 |
55 | ```javascript
56 | async function fetchComments() {
57 | const comment = (await DataStore.query(Comment)).filter(
58 | (c) => c.postID === postID //postID received as props
59 | );
60 | setDisplayComments(comment);
61 | }
62 | ```
63 |
64 | ### Case 4 -
65 | > To fetch and display data in particular sorted order
66 |
67 | ```javascript
68 | async function fetchComments() {
69 | const comments = await DataStore.query(Comment, Predicates.ALL, {
70 | sort: (s) => s.createdAt(SortDirection.DESCENDING)
71 | });
72 | /* const posts = await DataStore.query(Comment, Predicates.ALL, {
73 | sort: (s) => s.createdAt(SortDirection.ASCENDING)
74 | }); */
75 | console.log(comments);
76 | setDisplayComments(comments);
77 | }
78 | ```
79 |
80 | ## Real Time data
81 | > To display real-time data
82 |
83 | ```javascript
84 | useEffect(() => {
85 | const subscription = DataStore.observe(Comment).subscribe((msg) => {
86 | fetchComments();
87 | });
88 |
89 | return () => subscription.unsubscribe();
90 | }, []);
91 | ```
92 |
93 | ## Delete Data
94 | > To delete the particular data item from Data model
95 |
96 | ```javascript
97 | async function handleDelete(id) {
98 |
99 | const todelete = await DataStore.query(Comment, id);
100 | DataStore.delete(todelete);
101 | }
102 | ```
103 |
104 | ## Reference
105 | https://docs.amplify.aws/lib/datastore/getting-started/q/platform/js
106 |
--------------------------------------------------------------------------------
/src/pages/assignments/AddAssignment.js:
--------------------------------------------------------------------------------
1 | import React, { useContext, useState } from "react";
2 | import { Box, Button, Typography } from "@material-ui/core";
3 | import CheckCircleIcon from "@material-ui/icons/CheckCircle";
4 | import { useParams } from "react-router";
5 | import { DataStore } from "@aws-amplify/datastore";
6 | import { AssignmentModel } from "../../models";
7 | import { UserContext } from "../../context/UserContext";
8 | import Amplify, { Storage } from "aws-amplify";
9 | import awsconfig from "../../aws-exports";
10 |
11 | Amplify.configure(awsconfig);
12 |
13 | function AddAssignment() {
14 | const { id } = useParams();
15 | const user = useContext(UserContext);
16 | const [file, setFile] = useState();
17 | const hiddenFileInput = React.useRef(null);
18 |
19 | async function uploadAssignment() {
20 | const filename = file.name.split(".");
21 | const updatedFilename =
22 | filename[0] + Math.round(new Date().getTime() / 1000) + "." + filename[1];
23 | const KEY = `${id}/${updatedFilename}`;
24 |
25 | await Storage.put(KEY, file, {
26 | progressCallback(progress) {
27 | if (progress.loaded === progress.total) {
28 | }
29 | },
30 | });
31 | await DataStore.save(
32 | new AssignmentModel({
33 | title: "title",
34 | content: "content",
35 | S3Key: KEY,
36 | courseID: id,
37 | uploadedBy: user.username,
38 | uploadedAt: new Date().toLocaleString(),
39 | })
40 | );
41 | setFile("");
42 | }
43 |
44 | function handleChange(event) {
45 | setFile(event.target.files[0]);
46 | }
47 | const handleClick = (event) => {
48 | hiddenFileInput.current.click();
49 | };
50 | return (
51 |
52 |
59 |
60 |
61 | Add Assignment
62 |
63 |
70 |
71 |
72 |
73 | {file && (
74 |
75 |
76 | {file.name}
77 |
83 | Save
84 |
85 |
86 | )}
87 |
88 |
89 |
90 | );
91 | }
92 |
93 | export default AddAssignment;
94 |
--------------------------------------------------------------------------------
/src/components/course/EnrollButton.js:
--------------------------------------------------------------------------------
1 | import React, { useState } from "react";
2 | import { Box, Button, Modal, TextField } from "@material-ui/core";
3 | import { makeStyles } from "@material-ui/core/styles";
4 | import useCourses from "../../customHook/useCourses";
5 |
6 | function EnrollButton({ course, handleUpdate }) {
7 | const classes = useStyles();
8 | const [open, setOpen] = React.useState(false);
9 | const [coursePin, setCoursePin] = useState();
10 | const courses = useCourses(course.id);
11 |
12 | const handleOpen = () => {
13 | setOpen(true);
14 | };
15 |
16 | const handleClose = () => {
17 | setOpen(false);
18 | };
19 | const body = (
20 |
28 | Enter the course pin
29 | setCoursePin(event.target.value)}
38 | />
39 |
40 | setOpen(false)}
45 | >
46 | Cancel
47 |
48 | {
52 | if (coursePin === courses.coursePin) {
53 | handleUpdate(course.id);
54 | } else {
55 | alert("Wrong Course Pin!");
56 | setCoursePin();
57 | }
58 | setOpen(false);
59 | }}
60 | >
61 | Enroll
62 |
63 |
64 |
65 | );
66 | return (
67 |
68 | {
72 | handleOpen();
73 | }}
74 | style={{ marginLeft: "auto", borderRadius: "20px" }}
75 | >
76 | Enroll
77 |
78 |
84 | {body}
85 |
86 |
87 | );
88 | }
89 |
90 | export default EnrollButton;
91 |
92 | const useStyles = makeStyles((theme) => ({
93 | paper: {
94 | position: "absolute",
95 | width: 350,
96 | backgroundColor: theme.palette.background.paper,
97 | boxShadow: theme.shadows[5],
98 | padding: theme.spacing(2, 4, 3),
99 | borderRadius: "10px",
100 | },
101 | }));
102 |
--------------------------------------------------------------------------------
/amplify/backend/api/lmsappamplify/schema.graphql:
--------------------------------------------------------------------------------
1 | type AssignmentModel @model @auth(rules: [{allow: public}]) @key(name: "byCourse", fields: ["courseID"]) {
2 | id: ID!
3 | title: String
4 | content: String
5 | S3Key: String
6 | courseID: ID
7 | uploadedBy: String
8 | uploadedAt: String
9 | }
10 |
11 | type SyllabusModel @model @auth(rules: [{allow: public}]) @key(name: "byCourse", fields: ["courseID"]) {
12 | id: ID!
13 | title: String
14 | S3Key: String
15 | courseID: ID
16 | uploadedBy: String
17 | uploadedAt: String
18 | }
19 |
20 | enum CourseStatus {
21 | PUBLISHED
22 | DRAFT
23 | }
24 |
25 | type CommentModel @model @auth(rules: [{allow: public}]) @key(name: "byPostModel", fields: ["postmodelID"]) {
26 | id: ID!
27 | comment: String
28 | createdBy: String
29 | createdAt: String
30 | postmodelID: ID
31 | User: User @connection
32 | }
33 |
34 | type PostModel @model @auth(rules: [{allow: public}]) @key(name: "byCourse", fields: ["courseID"]) {
35 | id: ID!
36 | content: String
37 | createdBy: String
38 | createdAt: String
39 | CommentModels: [CommentModel] @connection(keyName: "byPostModel", fields: ["id"])
40 | courseID: ID
41 | User: User @connection
42 | }
43 |
44 | type AnnouncementsModel @model @auth(rules: [{allow: public}]) @key(name: "byCourse", fields: ["courseID"]) {
45 | id: ID!
46 | title: String
47 | content: String
48 | createdAt: String
49 | courseID: ID
50 | User: User @connection
51 | }
52 |
53 | type Lesson @model @auth(rules: [{allow: public}]) @key(name: "byCourse", fields: ["courseID"]) {
54 | id: ID!
55 | title: String
56 | summary: String
57 | videoURL: String
58 | courseID: ID
59 | createdBy: String
60 | }
61 |
62 | type Course @model @auth(rules: [{allow: public}]) {
63 | id: ID!
64 | title: String
65 | desc: String
66 | introduction: String
67 | createdBy: String
68 | Lessons: [Lesson] @connection(keyName: "byCourse", fields: ["id"])
69 | AnnouncementsModels: [AnnouncementsModel] @connection(keyName: "byCourse", fields: ["id"])
70 | createdAt: String
71 | User: User @connection
72 | status: CourseStatus
73 | PostModels: [PostModel] @connection(keyName: "byCourse", fields: ["id"])
74 | SyllabusModels: [SyllabusModel] @connection(keyName: "byCourse", fields: ["id"])
75 | AssignmentModels: [AssignmentModel] @connection(keyName: "byCourse", fields: ["id"])
76 | enrolledStudents: [String]
77 | CourseUsers: [CourseUser] @connection(keyName: "byCourse", fields: ["id"])
78 | coursePin: String
79 | }
80 |
81 | type User @model @auth(rules: [{allow: public}]) {
82 | id: ID!
83 | name: String
84 | email: String
85 | username: String
86 | isEducator: Boolean
87 | courses: [CourseUser] @connection(keyName: "byUser", fields: ["id"])
88 | }
89 |
90 | type CourseUser @model(queries: null) @key(name: "byCourse", fields: ["courseID", "userID"]) @key(name: "byUser", fields: ["userID", "courseID"]) @auth(rules: [{allow: public}, {allow: public}]) {
91 | id: ID!
92 | courseID: ID!
93 | userID: ID!
94 | course: Course! @connection(fields: ["courseID"])
95 | user: User! @connection(fields: ["userID"])
96 | }
97 |
--------------------------------------------------------------------------------
/src/components/course/Navlinks.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { CssBaseline, List, ListItem, makeStyles } from "@material-ui/core";
3 | import { useParams } from "react-router";
4 | import { NavLink } from "react-router-dom";
5 | import { UserContext } from "../../context/UserContext";
6 |
7 | function Navlinks() {
8 | let classes = useStyles();
9 | let { id } = useParams();
10 | const user = React.useContext(UserContext);
11 | return (
12 |
13 |
14 |
15 |
16 |
17 | Home
18 |
19 |
20 |
21 |
26 | Announcements
27 |
28 |
29 |
30 |
35 | Assignments
36 |
37 |
38 |
39 |
44 | Lessons
45 |
46 |
47 |
48 |
53 | Discussion
54 |
55 |
56 |
57 |
62 | Syllabus
63 |
64 |
65 | {user.isEducator && (
66 |
67 |
72 | Students
73 |
74 |
75 | )}
76 |
77 |
78 | );
79 | }
80 |
81 | export default Navlinks;
82 |
83 | const useStyles = makeStyles((theme) => ({
84 | root: {
85 | [theme.breakpoints.up("md")]: {
86 | position: "fixed",
87 | left: 100,
88 | padding: "20px",
89 | },
90 | },
91 | links: {
92 | padding: "5px",
93 | "&:hover": {
94 | fontWeight: "bold",
95 | },
96 | },
97 | activeLinks: {
98 | fontWeight: "bold",
99 | color: "black",
100 | borderLeft: "3px solid black",
101 | padding: "5px",
102 | },
103 | }));
104 |
--------------------------------------------------------------------------------
/src/pages/syllabus/NewSyllabus.js:
--------------------------------------------------------------------------------
1 | import React, { useContext, useState } from "react";
2 | import { Box, Button, Typography } from "@material-ui/core";
3 | import CheckCircleIcon from "@material-ui/icons/CheckCircle";
4 | import { useParams } from "react-router";
5 | import { DataStore } from "@aws-amplify/datastore";
6 | import { SyllabusModel } from "../../models";
7 | import { UserContext } from "../../context/UserContext";
8 | import Amplify, { Storage } from "aws-amplify";
9 | import awsconfig from "../../aws-exports";
10 |
11 | Amplify.configure(awsconfig);
12 |
13 | function NewSyllabus() {
14 | const { id } = useParams();
15 | const user = useContext(UserContext);
16 | const [file, setFile] = useState();
17 | const hiddenFileInput = React.useRef(null);
18 |
19 | async function uploadSyllabus() {
20 | const filename = file.name.split(".");
21 | const updatedFilename =
22 | filename[0] + Math.round(new Date().getTime() / 1000) + "." + filename[1];
23 | const KEY = `${id}/syllabus/${updatedFilename}`;
24 |
25 | await Storage.put(KEY, file, {
26 | progressCallback(progress) {
27 | if (progress.loaded === progress.total) {
28 | }
29 | },
30 | }).then(
31 | await DataStore.save(
32 | new SyllabusModel({
33 | title: "title",
34 | S3Key: KEY,
35 | courseID: id,
36 | uploadedBy: user.username,
37 | uploadedAt: new Date().toLocaleString(),
38 | })
39 | )
40 | );
41 | setFile("");
42 | }
43 |
44 | function handleChange(event) {
45 | setFile(event.target.files[0]);
46 | }
47 | const handleClick = (event) => {
48 | hiddenFileInput.current.click();
49 | };
50 | return (
51 |
52 |
59 |
60 |
61 | Add Syllabus
62 |
63 |
70 |
71 |
72 | {file && (
73 |
80 |
81 | {file.name}
82 |
88 | Save
89 |
90 |
91 | )}
92 |
93 |
94 |
95 | );
96 | }
97 |
98 | export default NewSyllabus;
99 |
--------------------------------------------------------------------------------
/src/components/landingPage/Footer.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { Grid, Link, Typography } from "@material-ui/core";
3 | import FavoriteIcon from "@material-ui/icons/Favorite";
4 |
5 | function Footer() {
6 | return (
7 |
89 | );
90 | }
91 |
92 | export default Footer;
93 |
--------------------------------------------------------------------------------
/src/components/Navigation.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { makeStyles, Grid, Hidden, Typography } from "@material-ui/core";
3 | import { NavLink } from "react-router-dom";
4 | import AccountCircleOutlinedIcon from "@material-ui/icons/AccountCircleOutlined";
5 | import DashboardRoundedIcon from "@material-ui/icons/DashboardRounded";
6 | import LibraryBooksRoundedIcon from "@material-ui/icons/LibraryBooksRounded";
7 | import DateRangeRoundedIcon from "@material-ui/icons/DateRangeRounded";
8 | import LinkRoundedIcon from "@material-ui/icons/LinkRounded";
9 | import SignoutButton from "./SignoutButton";
10 | function Nav() {
11 | const classes = useStyles();
12 |
13 | return (
14 |
15 |
20 |
21 |
22 |
23 |
24 | Account
25 |
26 |
27 |
28 |
33 |
34 |
35 |
36 |
37 | Dashboard
38 |
39 |
40 |
41 |
46 |
47 |
48 |
49 |
50 | Courses
51 |
52 |
53 |
54 |
59 |
60 |
61 |
62 |
63 | Calendar
64 |
65 |
66 |
67 |
72 |
73 |
74 |
75 |
76 | Connect
77 |
78 |
79 |
80 |
81 | );
82 | }
83 |
84 | export default Nav;
85 |
86 | const useStyles = makeStyles((theme) => ({
87 | nav: {
88 | display: "flex",
89 | flexDirection: "column",
90 | alignItems: "center",
91 | color: "white",
92 | },
93 | navItem: {
94 | display: "flex",
95 | flexDirection: "column",
96 | alignItems: "center",
97 | padding: "4px",
98 | [theme.breakpoints.up("md")]: {
99 | padding: "15px",
100 | },
101 | },
102 | navlink: {
103 | textDecoration: "none",
104 | color: "white",
105 |
106 | "&:hover": {
107 | fontWeight: "bold",
108 | color: theme.palette.secondary.main,
109 | },
110 | },
111 | navActive: {
112 | fontWeight: "bold",
113 | color: theme.palette.secondary.main,
114 | },
115 | }));
116 |
--------------------------------------------------------------------------------
/src/components/dashboard/EducatorDashboard.js:
--------------------------------------------------------------------------------
1 | import React, { useContext, useEffect, useState } from "react";
2 | import PropTypes from "prop-types";
3 | import { makeStyles } from "@material-ui/core/styles";
4 | import AppBar from "@material-ui/core/AppBar";
5 | import Tabs from "@material-ui/core/Tabs";
6 | import Tab from "@material-ui/core/Tab";
7 | import Typography from "@material-ui/core/Typography";
8 | import Box from "@material-ui/core/Box";
9 | import { Container } from "@material-ui/core";
10 | import Published from "./Published";
11 | import AllCourses from "./AllCourses";
12 | import Drafts from "./Drafts";
13 | import { CourseContext } from "../../context/CourseContext";
14 | import { DataStore } from "@aws-amplify/datastore";
15 | import { Course } from "../../models";
16 | import { UserContext } from "../../context/UserContext";
17 |
18 | function EducatorDashboard() {
19 | const user = useContext(UserContext);
20 | const classes = useStyles();
21 | const [value, setValue] = React.useState(0);
22 | const [courses, setCourses] = useState([]);
23 |
24 | useEffect(() => {
25 | async function getCourses() {
26 | const models = (await DataStore.query(Course)).filter(
27 | (c) => c.createdBy === user.username
28 | );
29 | setCourses(models);
30 | }
31 | getCourses();
32 | const subscription = DataStore.observe(Course).subscribe((msg) => {
33 | getCourses();
34 | });
35 | return () => subscription.unsubscribe();
36 | }, [user.username]);
37 |
38 | const handleChange = (event, newValue) => {
39 | setValue(newValue);
40 | };
41 |
42 | return (
43 |
44 |
45 |
46 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 | );
72 | }
73 | export default EducatorDashboard;
74 |
75 | const useStyles = makeStyles((theme) => ({
76 | root: {
77 | flexGrow: 1,
78 | backgroundColor: theme.palette.background.paper,
79 | marginTop: "40px",
80 | },
81 | }));
82 |
83 | function TabPanel(props) {
84 | const { children, value, index, ...other } = props;
85 |
86 | return (
87 |
94 | {value === index && (
95 |
96 | {children}
97 |
98 | )}
99 |
100 | );
101 | }
102 |
103 | TabPanel.propTypes = {
104 | children: PropTypes.node,
105 | index: PropTypes.any.isRequired,
106 | value: PropTypes.any.isRequired,
107 | };
108 |
109 | function a11yProps(index) {
110 | return {
111 | id: `simple-tab-${index}`,
112 | "aria-controls": `simple-tabpanel-${index}`,
113 | };
114 | }
115 |
--------------------------------------------------------------------------------
/src/pages/lessons/NewLesson.js:
--------------------------------------------------------------------------------
1 | import React, { useContext, useState } from "react";
2 | import {
3 | Button,
4 | TextField,
5 | Dialog,
6 | DialogActions,
7 | DialogContent,
8 | DialogTitle,
9 | } from "@material-ui/core";
10 | import AddIcon from "@material-ui/icons/Add";
11 | import { makeStyles } from "@material-ui/core/styles";
12 | import { useParams } from "react-router";
13 | import { DataStore } from "@aws-amplify/datastore";
14 | import { Lesson } from "../../models";
15 | import { UserContext } from "../../context/UserContext";
16 | const useStyles = makeStyles((theme) => ({
17 | form: {
18 | [theme.breakpoints.up("md")]: {
19 | minWidth: 500,
20 | },
21 | },
22 | }));
23 |
24 | export default function NewLesson(props) {
25 | const user = useContext(UserContext);
26 | const classes = useStyles();
27 | let { id } = useParams();
28 | const [open, setOpen] = useState(false);
29 | const [title, setTitle] = useState();
30 | const [summary, setSummary] = useState();
31 | const [url, setURL] = useState();
32 |
33 | const handleOpen = () => {
34 | setOpen(true);
35 | };
36 |
37 | const handleClose = () => {
38 | setTitle("");
39 | setSummary("");
40 | setURL("");
41 | setOpen(false);
42 | };
43 |
44 | async function handleSubmit(event) {
45 | event.preventDefault();
46 | await DataStore.save(
47 | new Lesson({
48 | title: title,
49 | summary: summary,
50 | videoURL: url,
51 | courseID: id,
52 | createdBy: user.username,
53 | })
54 | );
55 | setTitle("");
56 | setSummary("");
57 | setURL("");
58 | setOpen(false);
59 | }
60 | return (
61 |
62 |
69 | New Lesson
70 |
71 |
76 |
77 | Add New Lesson
78 |
79 | setTitle(event.target.value)}
86 | />
87 |
88 | setSummary(event.target.value)}
97 | />
98 |
99 | setURL(event.target.value)}
106 | />
107 |
108 |
109 |
110 |
111 |
112 | Add
113 |
114 |
115 | Cancel
116 |
117 |
118 |
119 |
120 |
121 | );
122 | }
123 |
--------------------------------------------------------------------------------
/src/routes/Routes.js:
--------------------------------------------------------------------------------
1 | import React, { useEffect, useState } from "react";
2 | import { Switch, Route, Redirect } from "react-router-dom";
3 | import { UserContext } from "../context/UserContext";
4 | import { DataStore } from "@aws-amplify/datastore";
5 | import { User } from "../models";
6 | import Auth from "@aws-amplify/auth";
7 | import PrivateRoute from "./PrivateRoute";
8 | import ProtectedRoute from "./ProtectedRoute";
9 |
10 | import SignIn from "../auth/SignIn";
11 | import SignUp from "../auth/SignUp";
12 | import Account from "../components/account/Account";
13 | import Calendar from "../components/calendar/Calendar";
14 | import Connect from "../components/Connect";
15 | import Dashboard from "../components/dashboard/Dashboard";
16 | import Error404 from "../components/Error404";
17 | import Home from "../components/Home";
18 | import Courses from "../components/course/Courses";
19 | import CourseDetails from "../components/course/CourseDetails";
20 | import EditCourse from "../components/course/EditCourse";
21 | import Assignments from "../pages/assignments/Assignments";
22 | import Announcements from "../pages/announcements/Announcements";
23 | import Lessons from "../pages/lessons/Lessons";
24 | import Discussions from "../pages/discussions/Discussions";
25 | import Syllabus from "../pages/syllabus/Syllabus";
26 | import EditAnnouncement from "../pages/announcements/EditAnnouncement";
27 | import Students from "../pages/students/Students";
28 |
29 | export default function Routes() {
30 | const [currentUser, setCurrentUser] = useState([]);
31 | useEffect(() => {
32 | getUser();
33 | const subscription = DataStore.observe(User).subscribe((msg) => {
34 | getUser();
35 | });
36 | return () => subscription.unsubscribe();
37 | }, []);
38 | async function getUser() {
39 | const user = await Auth.currentAuthenticatedUser();
40 | const currentUser = (await DataStore.query(User)).filter(
41 | (c) => c.username === user.username
42 | );
43 | setCurrentUser(currentUser);
44 | console.log("user");
45 | }
46 | return (
47 |
48 |
49 |
50 |
51 |
52 | {currentUser.map((user, index) => (
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
68 |
72 |
76 |
80 |
81 | {user.isEducator && (
82 |
83 | )}
84 |
85 | ))}
86 |
87 |
88 | );
89 | }
90 |
--------------------------------------------------------------------------------
/src/components/dashboard/Drafts.js:
--------------------------------------------------------------------------------
1 | import React, { useContext } from "react";
2 | import { withStyles, makeStyles } from "@material-ui/core/styles";
3 | import Table from "@material-ui/core/Table";
4 | import TableBody from "@material-ui/core/TableBody";
5 | import TableCell from "@material-ui/core/TableCell";
6 | import TableContainer from "@material-ui/core/TableContainer";
7 | import TableHead from "@material-ui/core/TableHead";
8 | import TableRow from "@material-ui/core/TableRow";
9 | import Paper from "@material-ui/core/Paper";
10 | import { CourseContext } from "../../context/CourseContext";
11 | import { Chip } from "@material-ui/core";
12 | import LabelIcon from "@material-ui/icons/Label";
13 | import { Link } from "react-router-dom";
14 |
15 | const StyledTableCell = withStyles((theme) => ({
16 | head: {
17 | backgroundColor: theme.palette.common.black,
18 | color: theme.palette.common.white,
19 | },
20 | body: {
21 | fontSize: 14,
22 | },
23 | }))(TableCell);
24 |
25 | const StyledTableRow = withStyles((theme) => ({
26 | root: {
27 | "&:nth-of-type(odd)": {
28 | backgroundColor: theme.palette.action.hover,
29 | },
30 | },
31 | }))(TableRow);
32 |
33 | const useStyles = makeStyles({
34 | table: {
35 | minWidth: 700,
36 | },
37 | });
38 |
39 | export default function CustomizedTables() {
40 | const classes = useStyles();
41 | const courses = useContext(CourseContext);
42 |
43 | return (
44 |
45 |
46 |
47 |
48 | Course Title
49 | Course Description
50 | Status
51 | Created on
52 | Course Pin
53 |
54 |
55 |
56 | {courses.map((course, index) => (
57 | <>
58 | {course.status === "DRAFT" && (
59 |
60 |
61 |
66 | {course.title}
67 |
68 |
69 |
70 | {course.desc}
71 |
72 |
73 | }
75 | label={course.status}
76 | variant="outlined"
77 | style={{
78 | border: "1px solid rgb(0, 123, 255)",
79 | background: "rgb(0, 123, 255, 0.2)",
80 | }}
81 | />
82 |
83 |
84 | {course.createdAt}
85 |
86 |
87 | {
88 |
93 | }
94 |
95 |
96 | )}
97 | >
98 | ))}
99 |
100 |
101 |
102 | );
103 | }
104 |
--------------------------------------------------------------------------------
/src/auth/SignIn.js:
--------------------------------------------------------------------------------
1 | import React, { useState } from "react";
2 | import Amplify, { Auth } from "aws-amplify";
3 | import awsconfig from "../aws-exports";
4 | import { Link, useHistory } from "react-router-dom";
5 | import {
6 | makeStyles,
7 | Grid,
8 | Typography,
9 | Container,
10 | TextField,
11 | Button,
12 | Box,
13 | Hidden,
14 | } from "@material-ui/core";
15 | import login from "../assests/login.svg";
16 |
17 | Auth.configure(awsconfig); //For NoUserPool Found error
18 | Amplify.configure(awsconfig);
19 | const useStyles = makeStyles((theme) => ({
20 | right: {
21 | backgroundColor: theme.palette.primary.main,
22 | height: "100vh",
23 | display: "flex",
24 | flexDirection: "column",
25 | justifyContent: "center",
26 | alignItems: "center",
27 | },
28 | container: {
29 | height: "100vh",
30 | display: "flex",
31 | flexDirection: "column",
32 | justifyContent: "center",
33 | alignItems: "flex-start",
34 | },
35 | textField: {
36 | width: "70%",
37 | marginBottom: "20px",
38 | },
39 | }));
40 | const initialFormState = {
41 | username: "",
42 | password: "",
43 | formType: "signIn",
44 | };
45 |
46 | function SignIn() {
47 | const classes = useStyles();
48 | let history = useHistory();
49 | const [formState, updateFormState] = useState(initialFormState);
50 |
51 | const onChange = (e) => {
52 | e.persist();
53 | updateFormState(() => ({ ...formState, [e.target.name]: e.target.value }));
54 | };
55 | const { formType } = formState;
56 |
57 | async function signIn() {
58 | const { username, password } = formState;
59 | try {
60 | await Auth.signIn(username, password);
61 | updateFormState(() => ({ ...formState }));
62 | localStorage.setItem("auth", username);
63 | history.push("/");
64 | window.location.reload();
65 | } catch (err) {
66 | alert(err.message);
67 | console.log("error signing in user...", err.message);
68 | }
69 | }
70 |
71 | return (
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 | {formType === "signIn" && (
82 |
83 |
84 | Sign In Now!
85 |
86 |
87 |
88 | Username
89 |
90 |
98 |
99 | Password
100 |
101 |
110 |
111 | Sign In
112 |
113 |
114 | Not a member? Sign up now
115 |
116 |
117 | Demo credentials:
118 |
username: test-user
119 |
password: Test@123#
120 |
121 |
122 | )}
123 |
124 |
125 |
126 | );
127 | }
128 | export default SignIn;
129 |
--------------------------------------------------------------------------------
/src/pages/discussions/NewDiscussion.js:
--------------------------------------------------------------------------------
1 | import React, { useContext, useState } from "react";
2 | import {
3 | Button,
4 | Dialog,
5 | DialogActions,
6 | DialogContent,
7 | DialogTitle,
8 | } from "@material-ui/core";
9 | import AddIcon from "@material-ui/icons/Add";
10 | import { makeStyles } from "@material-ui/core/styles";
11 | import { useParams } from "react-router";
12 | import { DataStore } from "@aws-amplify/datastore";
13 | import { PostModel } from "../../models";
14 | import { UserContext } from "../../context/UserContext";
15 | import { Editor } from "react-draft-wysiwyg";
16 | import { EditorState, convertToRaw } from "draft-js";
17 | import "react-draft-wysiwyg/dist/react-draft-wysiwyg.css";
18 | import draftToHtml from "draftjs-to-html";
19 |
20 | const useStyles = makeStyles((theme) => ({
21 | form: {
22 | [theme.breakpoints.up("md")]: {
23 | minWidth: 500,
24 | },
25 | },
26 | }));
27 |
28 | export default function NewDiscussion() {
29 | const user = useContext(UserContext);
30 | const classes = useStyles();
31 | let { id } = useParams();
32 | const [open, setOpen] = useState(false);
33 | const [content, setContent] = useState();
34 | const [editorState, setEditorState] = useState(EditorState.createEmpty());
35 |
36 | const handleOpen = () => {
37 | setOpen(true);
38 | };
39 |
40 | const handleClose = () => {
41 | setContent("");
42 | setOpen(false);
43 | };
44 | function onEditorStateChange(editorState) {
45 | setContent(draftToHtml(convertToRaw(editorState.getCurrentContent())));
46 | setEditorState(editorState);
47 | }
48 | async function handleSubmit(event) {
49 | event.preventDefault();
50 | await DataStore.save(
51 | new PostModel({
52 | content: content,
53 | createdAt: new Date().toLocaleString(),
54 | createdBy: user.username,
55 | courseID: id,
56 | User: user,
57 | })
58 | );
59 | setContent("");
60 | setEditorState("");
61 | setOpen(false);
62 | }
63 | return (
64 |
65 |
72 | Discussion
73 |
74 |
79 |
80 |
81 | Create New Discussion
82 |
83 |
84 |
107 |
108 |
109 |
110 |
111 | Add
112 |
113 |
114 | Cancel
115 |
116 |
117 |
118 |
119 |
120 | );
121 | }
122 |
--------------------------------------------------------------------------------
/src/components/dashboard/Published.js:
--------------------------------------------------------------------------------
1 | import React, { useContext } from "react";
2 | import { withStyles, makeStyles } from "@material-ui/core/styles";
3 | import Table from "@material-ui/core/Table";
4 | import TableBody from "@material-ui/core/TableBody";
5 | import TableCell from "@material-ui/core/TableCell";
6 | import TableContainer from "@material-ui/core/TableContainer";
7 | import TableHead from "@material-ui/core/TableHead";
8 | import TableRow from "@material-ui/core/TableRow";
9 | import Paper from "@material-ui/core/Paper";
10 | import { CourseContext } from "../../context/CourseContext";
11 | import { Chip } from "@material-ui/core";
12 | import CheckCircleIcon from "@material-ui/icons/CheckCircle";
13 | import { Link } from "react-router-dom";
14 |
15 | const StyledTableCell = withStyles((theme) => ({
16 | head: {
17 | backgroundColor: theme.palette.common.black,
18 | color: theme.palette.common.white,
19 | },
20 | body: {
21 | fontSize: 14,
22 | },
23 | }))(TableCell);
24 |
25 | const StyledTableRow = withStyles((theme) => ({
26 | root: {
27 | "&:nth-of-type(odd)": {
28 | backgroundColor: theme.palette.action.hover,
29 | },
30 | },
31 | }))(TableRow);
32 |
33 | const useStyles = makeStyles({
34 | table: {
35 | minWidth: 700,
36 | },
37 | });
38 |
39 | export default function CustomizedTables() {
40 | const classes = useStyles();
41 | const courses = useContext(CourseContext);
42 |
43 | return (
44 |
45 |
46 |
47 |
48 | Course Title
49 | Course Description
50 | Status
51 | Created on
52 | Course Pin
53 |
54 |
55 |
56 | {courses.map((course, index) => (
57 | <>
58 | {course.status === "PUBLISHED" && (
59 |
60 |
61 |
66 | {course.title}
67 |
68 |
69 |
70 | {course.desc}
71 |
72 |
73 |
78 | }
79 | label={course.status}
80 | variant="outlined"
81 | style={{
82 | border: "1px solid rgba(34, 208, 36)",
83 | background: "rgba(34, 208, 36, 0.2)",
84 | }}
85 | />
86 |
87 |
88 | {course.createdAt}
89 |
90 |
91 | {
92 |
97 | }
98 |
99 |
100 | )}
101 | >
102 | ))}
103 |
104 |
105 |
106 | );
107 | }
108 |
--------------------------------------------------------------------------------
/public/hero.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/components/course/NewCourse.js:
--------------------------------------------------------------------------------
1 | import React, { useContext, useState } from "react";
2 | import {
3 | Button,
4 | TextField,
5 | Dialog,
6 | DialogActions,
7 | DialogContent,
8 | DialogTitle,
9 | } from "@material-ui/core";
10 | import AddIcon from "@material-ui/icons/Add";
11 | import { makeStyles } from "@material-ui/core/styles";
12 | import { DataStore } from "@aws-amplify/datastore";
13 | import { Course, CourseStatus, CourseUser } from "../../models";
14 | import { UserContext } from "../../context/UserContext";
15 |
16 | const useStyles = makeStyles((theme) => ({
17 | form: {
18 | [theme.breakpoints.up("md")]: {
19 | minWidth: 500,
20 | },
21 | },
22 | }));
23 |
24 | export default function NewCourse() {
25 | const user = useContext(UserContext);
26 | const classes = useStyles();
27 | const [open, setOpen] = useState(false);
28 | const [title, setTitle] = useState();
29 | const [desc, setDesc] = useState();
30 | const [coursePin, setCoursePin] = useState();
31 |
32 | const handleOpen = () => {
33 | setOpen(true);
34 | };
35 |
36 | const handleClose = () => {
37 | setTitle("");
38 | setDesc("");
39 | setOpen(false);
40 | };
41 |
42 | async function handleSubmit(event) {
43 | event.preventDefault();
44 | await DataStore.save(
45 | new Course({
46 | title: title,
47 | desc: desc,
48 | introduction: "",
49 | createdBy: user.username,
50 | createdAt: new Date().toLocaleString(),
51 | User: user,
52 | status: CourseStatus.DRAFT,
53 | enrolledStudents: [user.id],
54 | coursePin: coursePin,
55 | })
56 | ).then((res) => async () => {
57 | await DataStore.save(
58 | new CourseUser({
59 | course: res,
60 | user: user,
61 | })
62 | );
63 | });
64 |
65 | setTitle("");
66 | setDesc("");
67 | setOpen(false);
68 | }
69 | return (
70 |
71 |
78 | Add Course
79 |
80 |
85 |
86 | Create New Course
87 |
88 | setTitle(event.target.value)}
96 | />
97 |
98 | setDesc(event.target.value)}
108 | />
109 |
110 | setCoursePin(event.target.value)}
118 | />
119 |
120 |
121 |
122 |
123 |
124 | Add
125 |
126 |
132 | Cancel
133 |
134 |
135 |
136 |
137 |
138 | );
139 | }
140 |
--------------------------------------------------------------------------------
/src/assests/hero.svg:
--------------------------------------------------------------------------------
1 | abstract
--------------------------------------------------------------------------------
/src/pages/announcements/Edit.js:
--------------------------------------------------------------------------------
1 | import React, { useEffect, useState } from "react";
2 | import { useHistory, useParams } from "react-router";
3 | import { DataStore } from "@aws-amplify/datastore";
4 | import { AnnouncementsModel } from "../../models";
5 | import {
6 | Box,
7 | Button,
8 | Container,
9 | TextField,
10 | Typography,
11 | } from "@material-ui/core";
12 | import { Editor } from "react-draft-wysiwyg";
13 | import {
14 | EditorState,
15 | convertToRaw,
16 | ContentState,
17 | convertFromHTML,
18 | } from "draft-js";
19 | import "react-draft-wysiwyg/dist/react-draft-wysiwyg.css";
20 | import draftToHtml from "draftjs-to-html";
21 |
22 | function Edit() {
23 | const [title, setTitle] = useState();
24 | const [content, setContent] = useState();
25 | const [editorState, setEditorState] = useState();
26 | const { aID } = useParams();
27 | let history = useHistory();
28 | useEffect(() => {
29 | async function fetchAnnouncementModels() {
30 | const models = await DataStore.query(AnnouncementsModel, aID);
31 | setTitle(models.title);
32 | setContent(models.content);
33 | setEditorState(
34 | EditorState.createWithContent(
35 | ContentState.createFromBlockArray(convertFromHTML(models.content))
36 | )
37 | );
38 | }
39 |
40 | fetchAnnouncementModels();
41 | }, [aID]);
42 |
43 | function onEditorStateChange(editorState) {
44 | setContent(draftToHtml(convertToRaw(editorState.getCurrentContent())));
45 | setEditorState(editorState);
46 | }
47 |
48 | async function handleSubmit(event) {
49 | event.preventDefault();
50 | if (title === "" || content === "") {
51 | alert("Cannot be blank!");
52 | } else {
53 | const original = await DataStore.query(AnnouncementsModel, aID);
54 |
55 | await DataStore.save(
56 | AnnouncementsModel.copyOf(original, (updated) => {
57 | updated.title = title;
58 | updated.content = content;
59 | })
60 | );
61 | history.goBack();
62 | }
63 | }
64 | function handleCancel() {
65 | history.goBack();
66 | }
67 | return (
68 |
75 |
76 | Edit Announcement
77 |
78 | setTitle(event.target.value)}
87 | />
88 |
89 |
90 |
114 |
115 |
116 |
117 |
123 | Cancel
124 |
125 |
126 | Save
127 |
128 |
129 |
130 | );
131 | }
132 |
133 | export default Edit;
134 |
--------------------------------------------------------------------------------
/src/components/course/CourseDetails.js:
--------------------------------------------------------------------------------
1 | import React, { useContext, useEffect, useState } from "react";
2 | import { useTheme } from "@material-ui/core/styles";
3 | import { useStyles } from "../../utils/useStyles";
4 | import {
5 | AppBar,
6 | Box,
7 | Chip,
8 | Drawer,
9 | Grid,
10 | Hidden,
11 | Toolbar,
12 | } from "@material-ui/core";
13 | import MenuIcon from "@material-ui/icons/Menu";
14 | import Navigation from "../Navigation";
15 | import Navlinks from "./Navlinks";
16 | import Breadcrumb from "../Breadcrumb";
17 | import { UserContext } from "../../context/UserContext";
18 | import { DataStore } from "@aws-amplify/datastore";
19 | import { Course } from "../../models";
20 | import { useParams } from "react-router";
21 | import DeleteButton from "./DeleteButton";
22 | import EditButton from "./EditButton";
23 | import CourseIntro from "./CourseIntro";
24 | import PublishButton from "./PublishButton";
25 | import CourseStatus from "./CourseStatus";
26 |
27 | export default function CourseDetails() {
28 | const user = useContext(UserContext);
29 | const classes = useStyles();
30 | const theme = useTheme();
31 | const { id } = useParams();
32 | const [course, setCourse] = useState([]);
33 | const [mobileOpen, setMobileOpen] = React.useState(false);
34 | useEffect(() => {
35 | async function getCourses() {
36 | const models = await DataStore.query(Course, id);
37 | setCourse(models);
38 | }
39 | getCourses();
40 | }, [id]);
41 |
42 | const handleDrawerToggle = () => {
43 | setMobileOpen(!mobileOpen);
44 | };
45 |
46 | const drawer = (
47 |
48 |
49 |
50 | );
51 | return (
52 |
53 |
54 |
55 |
56 |
57 |
58 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 | {" "}
73 |
74 |
75 |
76 |
77 |
78 |
90 | {drawer}
91 |
92 |
93 |
94 | {drawer}
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 | {course.createdBy === user.username && user.isEducator && (
103 |
113 |
114 | {course.status === "DRAFT" &&
}
115 |
116 |
117 |
122 |
123 | )}
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 | );
132 | }
133 |
--------------------------------------------------------------------------------
/src/components/dashboard/AllCourses.js:
--------------------------------------------------------------------------------
1 | import React, { useContext } from "react";
2 | import { withStyles, makeStyles } from "@material-ui/core/styles";
3 | import Table from "@material-ui/core/Table";
4 | import TableBody from "@material-ui/core/TableBody";
5 | import TableCell from "@material-ui/core/TableCell";
6 | import TableContainer from "@material-ui/core/TableContainer";
7 | import TableHead from "@material-ui/core/TableHead";
8 | import TableRow from "@material-ui/core/TableRow";
9 | import Paper from "@material-ui/core/Paper";
10 | import { CourseContext } from "../../context/CourseContext";
11 | import { Chip } from "@material-ui/core";
12 | import LabelIcon from "@material-ui/icons/Label";
13 | import CheckCircleIcon from "@material-ui/icons/CheckCircle";
14 | import { Link } from "react-router-dom";
15 |
16 | const StyledTableCell = withStyles((theme) => ({
17 | head: {
18 | backgroundColor: theme.palette.common.black,
19 | color: theme.palette.common.white,
20 | },
21 | body: {
22 | fontSize: 14,
23 | },
24 | }))(TableCell);
25 |
26 | const StyledTableRow = withStyles((theme) => ({
27 | root: {
28 | "&:nth-of-type(odd)": {
29 | backgroundColor: theme.palette.action.hover,
30 | },
31 | },
32 | }))(TableRow);
33 |
34 | const useStyles = makeStyles({
35 | table: {
36 | minWidth: 700,
37 | },
38 | });
39 |
40 | export default function CustomizedTables() {
41 | const classes = useStyles();
42 | const courses = useContext(CourseContext);
43 |
44 | return (
45 |
46 |
47 |
48 |
49 | S. No.
50 | Course Title
51 | Course Description
52 | Status
53 | Created On
54 | Course Pin
55 |
56 |
57 |
58 | {courses.map((course, index) => (
59 |
60 | {index + 1}
61 |
62 |
67 | {course.title}
68 |
69 |
70 | {course.desc}
71 |
72 | {course.status === "PUBLISHED" ? (
73 |
76 | }
77 | label={course.status}
78 | variant="outlined"
79 | style={{
80 | border: "1px solid rgba(34, 208, 36)",
81 | background: "rgba(34, 208, 36, 0.2)",
82 | }}
83 | />
84 | ) : (
85 | }
87 | label={course.status}
88 | variant="outlined"
89 | style={{
90 | border: "1px solid rgb(0, 123, 255)",
91 | background: "rgb(0, 123, 255, 0.2)",
92 | }}
93 | />
94 | )}
95 |
96 |
97 | {course.createdAt}
98 |
99 |
100 | {
101 |
106 | }
107 |
108 |
109 | ))}
110 |
111 |
112 |
113 | );
114 | }
115 |
--------------------------------------------------------------------------------
/src/pages/announcements/NewAnnouncement.js:
--------------------------------------------------------------------------------
1 | import React, { useContext, useState } from "react";
2 | import {
3 | Button,
4 | TextField,
5 | Dialog,
6 | DialogActions,
7 | DialogContent,
8 | DialogTitle,
9 | } from "@material-ui/core";
10 | import AddIcon from "@material-ui/icons/Add";
11 | import { makeStyles } from "@material-ui/core/styles";
12 | import { useParams } from "react-router";
13 | import { DataStore } from "@aws-amplify/datastore";
14 | import { AnnouncementsModel } from "../../models";
15 | import { UserContext } from "../../context/UserContext";
16 | import { Editor } from "react-draft-wysiwyg";
17 | import { EditorState, convertToRaw } from "draft-js";
18 | import "react-draft-wysiwyg/dist/react-draft-wysiwyg.css";
19 | import draftToHtml from "draftjs-to-html";
20 |
21 | const useStyles = makeStyles((theme) => ({
22 | form: {
23 | [theme.breakpoints.up("md")]: {
24 | minWidth: 500,
25 | },
26 | },
27 | }));
28 |
29 | export default function NewAnnouncement() {
30 | const user = useContext(UserContext);
31 | const classes = useStyles();
32 | let { id } = useParams();
33 | const [open, setOpen] = useState(false);
34 | const [title, setTitle] = useState();
35 | const [content, setContent] = useState();
36 | const [editorState, setEditorState] = useState(EditorState.createEmpty());
37 |
38 | const handleOpen = () => {
39 | setOpen(true);
40 | };
41 |
42 | const handleClose = () => {
43 | setTitle("");
44 | setContent("");
45 | setOpen(false);
46 | };
47 | function onEditorStateChange(editorState) {
48 | setContent(draftToHtml(convertToRaw(editorState.getCurrentContent())));
49 | setEditorState(editorState);
50 | }
51 | async function handleSubmit(event) {
52 | event.preventDefault();
53 | await DataStore.save(
54 | new AnnouncementsModel({
55 | title: title,
56 | content: content,
57 | createdAt: new Date().toLocaleString(),
58 | courseID: id,
59 | User: user,
60 | })
61 | );
62 | setTitle("");
63 | setContent("");
64 | setEditorState("");
65 | setOpen(false);
66 | }
67 | return (
68 |
69 |
76 | New Announcement
77 |
78 |
83 |
84 |
85 | Create New Announcement
86 |
87 |
88 | setTitle(event.target.value)}
95 | />
96 |
97 |
121 |
122 |
123 |
124 |
125 | Add
126 |
127 |
128 | Cancel
129 |
130 |
131 |
132 |
133 |
134 | );
135 | }
136 |
--------------------------------------------------------------------------------
/src/models/index.d.ts:
--------------------------------------------------------------------------------
1 | import { ModelInit, MutableModel, PersistentModelConstructor } from "@aws-amplify/datastore";
2 |
3 | export enum CourseStatus {
4 | PUBLISHED = "PUBLISHED",
5 | DRAFT = "DRAFT"
6 | }
7 |
8 |
9 |
10 | export declare class AssignmentModel {
11 | readonly id: string;
12 | readonly title?: string;
13 | readonly content?: string;
14 | readonly S3Key?: string;
15 | readonly courseID?: string;
16 | readonly uploadedBy?: string;
17 | readonly uploadedAt?: string;
18 | constructor(init: ModelInit);
19 | static copyOf(source: AssignmentModel, mutator: (draft: MutableModel) => MutableModel | void): AssignmentModel;
20 | }
21 |
22 | export declare class SyllabusModel {
23 | readonly id: string;
24 | readonly title?: string;
25 | readonly S3Key?: string;
26 | readonly courseID?: string;
27 | readonly uploadedBy?: string;
28 | readonly uploadedAt?: string;
29 | constructor(init: ModelInit);
30 | static copyOf(source: SyllabusModel, mutator: (draft: MutableModel) => MutableModel | void): SyllabusModel;
31 | }
32 |
33 | export declare class CommentModel {
34 | readonly id: string;
35 | readonly comment?: string;
36 | readonly createdBy?: string;
37 | readonly createdAt?: string;
38 | readonly postmodelID?: string;
39 | readonly User?: User;
40 | constructor(init: ModelInit);
41 | static copyOf(source: CommentModel, mutator: (draft: MutableModel) => MutableModel | void): CommentModel;
42 | }
43 |
44 | export declare class User {
45 | readonly id: string;
46 | readonly name?: string;
47 | readonly email?: string;
48 | readonly username?: string;
49 | readonly isEducator?: boolean;
50 | readonly courses?: (CourseUser | null)[];
51 | constructor(init: ModelInit);
52 | static copyOf(source: User, mutator: (draft: MutableModel) => MutableModel | void): User;
53 | }
54 |
55 | export declare class CourseUser {
56 | readonly id: string;
57 | readonly course: Course;
58 | readonly user: User;
59 | constructor(init: ModelInit);
60 | static copyOf(source: CourseUser, mutator: (draft: MutableModel) => MutableModel | void): CourseUser;
61 | }
62 |
63 | export declare class Course {
64 | readonly id: string;
65 | readonly title?: string;
66 | readonly desc?: string;
67 | readonly introduction?: string;
68 | readonly createdBy?: string;
69 | readonly Lessons?: (Lesson | null)[];
70 | readonly AnnouncementsModels?: (AnnouncementsModel | null)[];
71 | readonly createdAt?: string;
72 | readonly User?: User;
73 | readonly status?: CourseStatus | keyof typeof CourseStatus;
74 | readonly PostModels?: (PostModel | null)[];
75 | readonly SyllabusModels?: (SyllabusModel | null)[];
76 | readonly AssignmentModels?: (AssignmentModel | null)[];
77 | readonly enrolledStudents?: (string | null)[];
78 | readonly CourseUsers?: (CourseUser | null)[];
79 | readonly coursePin?: string;
80 | constructor(init: ModelInit);
81 | static copyOf(source: Course, mutator: (draft: MutableModel) => MutableModel | void): Course;
82 | }
83 |
84 | export declare class Lesson {
85 | readonly id: string;
86 | readonly title?: string;
87 | readonly summary?: string;
88 | readonly videoURL?: string;
89 | readonly courseID?: string;
90 | readonly createdBy?: string;
91 | constructor(init: ModelInit);
92 | static copyOf(source: Lesson, mutator: (draft: MutableModel) => MutableModel | void): Lesson;
93 | }
94 |
95 | export declare class AnnouncementsModel {
96 | readonly id: string;
97 | readonly title?: string;
98 | readonly content?: string;
99 | readonly createdAt?: string;
100 | readonly courseID?: string;
101 | readonly User?: User;
102 | constructor(init: ModelInit);
103 | static copyOf(source: AnnouncementsModel, mutator: (draft: MutableModel) => MutableModel | void): AnnouncementsModel;
104 | }
105 |
106 | export declare class PostModel {
107 | readonly id: string;
108 | readonly content?: string;
109 | readonly createdBy?: string;
110 | readonly createdAt?: string;
111 | readonly CommentModels?: (CommentModel | null)[];
112 | readonly courseID?: string;
113 | readonly User?: User;
114 | constructor(init: ModelInit);
115 | static copyOf(source: PostModel, mutator: (draft: MutableModel) => MutableModel | void): PostModel;
116 | }
--------------------------------------------------------------------------------
/src/components/course/CourseIntro.js:
--------------------------------------------------------------------------------
1 | import React, { useContext, useState } from "react";
2 | import { useParams } from "react-router";
3 | import { DataStore } from "@aws-amplify/datastore";
4 | import { Course } from "../../models";
5 | import { Box, Button, Container, Paper, Typography } from "@material-ui/core";
6 | import { Editor } from "react-draft-wysiwyg";
7 | import { convertToRaw } from "draft-js";
8 | import "react-draft-wysiwyg/dist/react-draft-wysiwyg.css";
9 | import draftToHtml from "draftjs-to-html";
10 | import ReactHtmlParser from "react-html-parser";
11 | import { UserContext } from "../../context/UserContext";
12 | import Avatar from "boring-avatars";
13 | import useCourses from "../../customHook/useCourses";
14 |
15 | function CourseIntro() {
16 | const user = useContext(UserContext);
17 | const [introduction, setIntroduction] = useState();
18 | const [editorState, setEditorState] = useState();
19 | const { id } = useParams();
20 | const course = useCourses(id);
21 |
22 | function onEditorStateChange(editorState) {
23 | setIntroduction(draftToHtml(convertToRaw(editorState.getCurrentContent())));
24 | setEditorState(editorState);
25 | }
26 |
27 | async function handleSubmit(event) {
28 | event.preventDefault();
29 | const original = await DataStore.query(Course, id);
30 | await DataStore.save(
31 | Course.copyOf(original, (updated) => {
32 | updated.introduction = introduction;
33 | })
34 | );
35 | }
36 |
37 | return (
38 |
39 |
48 | {course.title}
49 | {course.desc}
50 |
59 | Meet your instructor
60 |
66 | @{course.createdBy}
67 |
68 |
69 |
70 | {course.introduction === "" ? (
71 |
72 | {course.createdBy === user.username && user.isEducator && (
73 |
74 |
78 | Add Introduction to the course
79 |
80 |
81 |
106 |
107 |
113 | Save
114 | {" "}
115 |
116 | )}
117 |
118 | ) : (
119 |
120 | {ReactHtmlParser(course.introduction)}
121 |
122 | )}
123 |
124 | );
125 | }
126 |
127 | export default CourseIntro;
128 |
--------------------------------------------------------------------------------
/src/pages/discussions/Discussion.js:
--------------------------------------------------------------------------------
1 | import React, { useContext } from "react";
2 | import {
3 | Box,
4 | Button,
5 | Dialog,
6 | DialogActions,
7 | DialogContent,
8 | DialogTitle,
9 | Grid,
10 | makeStyles,
11 | Paper,
12 | Typography,
13 | } from "@material-ui/core";
14 | import DeleteIcon from "@material-ui/icons/Delete";
15 | import { UserContext } from "../../context/UserContext";
16 | import ReactHtmlParser from "react-html-parser";
17 | import Avatar from "boring-avatars";
18 | import AddComment from "./AddComment";
19 | import DisplayComments from "./DisplayComments";
20 |
21 | function Discussion({ discussion, handleDelete }) {
22 | const classes = useStyles();
23 | const user = useContext(UserContext);
24 | const [open, setOpen] = React.useState(false);
25 |
26 | const handleClickOpen = () => {
27 | setOpen(true);
28 | };
29 |
30 | const handleClose = () => {
31 | handleDelete(discussion.id);
32 | setOpen(false);
33 | };
34 | const handleCancel = () => {
35 | setOpen(false);
36 | };
37 |
38 | return (
39 |
40 |
41 |
42 |
43 |
44 |
45 |
58 |
59 | {discussion.User.name}
60 |
61 | {discussion.createdAt}
62 |
63 |
64 |
65 | {user.username === discussion.User.username && (
66 |
67 |
68 |
72 |
78 |
79 | {"Are you sure you want to delete this post?"}
80 |
81 |
82 |
83 |
88 | Cancel
89 |
90 |
95 | Delete
96 |
97 |
98 |
99 |
100 |
101 | )}
102 |
103 |
104 |
105 | {discussion.title}
106 |
107 |
108 | {ReactHtmlParser(discussion.content)}
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 | );
122 | }
123 |
124 | export default Discussion;
125 |
126 | const useStyles = makeStyles((theme) => ({
127 | paper: {
128 | margin: theme.spacing(2),
129 | padding: theme.spacing(2),
130 | },
131 | header: {
132 | display: "flex",
133 | flexDirection: "row",
134 | padding: "10px 0px",
135 | },
136 | root: {
137 | display: "flex",
138 | flexDirection: "row",
139 | justifyContent: "space-between",
140 | },
141 | icons: {
142 | padding: theme.spacing(2),
143 | },
144 | delete: {
145 | cursor: "pointer",
146 | "&:hover": { color: "red" },
147 | },
148 | }));
149 |
--------------------------------------------------------------------------------
/src/components/Details.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { Box, Container, Divider, Link, Typography } from "@material-ui/core";
3 |
4 | function Details() {
5 | return (
6 |
7 |
8 |
9 | This project is built using
10 |
11 |
12 | Frontend
13 |
14 |
15 |
16 |
21 |
22 |
26 |
31 |
32 |
33 |
38 |
39 |
40 | Backend
41 |
42 |
43 |
44 |
49 |
50 |
51 |
56 |
57 |
58 |
59 |
64 |
65 |
66 | Other Tools
67 |
68 |
69 |
70 |
75 |
76 |
81 |
86 |
87 |
88 |
89 | {" "}
90 | Connect or see my other work{" "}
91 |
92 |
93 |
94 |
99 |
100 |
101 |
106 |
107 |
111 |
116 |
117 |
118 |
123 |
124 |
125 |
126 | );
127 | }
128 |
129 | export default Details;
130 |
--------------------------------------------------------------------------------