Below is an implementation for a Learning Management System (LMS) focusing on online course management, assignment submission, and virtual classroom features using Next.js, NestJS, and GraphQL.
Backend (NestJS)
1. Entities
Course Entity:
// course.entity.ts
import { Entity, Column, PrimaryGeneratedColumn, OneToMany } from 'typeorm';
import { Assignment } from './assignment.entity';
@Entity()
export class Course {
@PrimaryGeneratedColumn()
id: number;
@Column()
name: string;
@Column()
description: string;
@OneToMany(() => Assignment, (assignment) => assignment.course)
assignments: Assignment[];
}
Assignment Entity:
// assignment.entity.ts
import { Entity, Column, PrimaryGeneratedColumn, ManyToOne } from 'typeorm';
import { Course } from './course.entity';
@Entity()
export class Assignment {
@PrimaryGeneratedColumn()
id: number;
@Column()
title: string;
@Column()
description: string;
@Column()
dueDate: Date;
@ManyToOne(() => Course, (course) => course.assignments)
course: Course;
}
Submission Entity:
// submission.entity.ts
import { Entity, Column, PrimaryGeneratedColumn, ManyToOne } from 'typeorm';
import { Assignment } from './assignment.entity';
import { User } from './user.entity';
@Entity()
export class Submission {
@PrimaryGeneratedColumn()
id: number;
@ManyToOne(() => Assignment, (assignment) => assignment.submissions)
assignment: Assignment;
@ManyToOne(() => User, (user) => user.submissions)
student: User;
@Column()
content: string;
@Column()
submittedAt: Date;
}
2. Services
Course Service:
// course.service.ts
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { Course } from './course.entity';
@Injectable()
export class CourseService {
constructor(
@InjectRepository(Course)
private courseRepository: Repository<Course>,
) {}
findAll(): Promise<Course[]> {
return this.courseRepository.find({ relations: ['assignments'] });
}
findOne(id: number): Promise<Course> {
return this.courseRepository.findOne(id, { relations: ['assignments'] });
}
create(name: string, description: string): Promise<Course> {
const newCourse = this.courseRepository.create({ name, description });
return this.courseRepository.save(newCourse);
}
}
Assignment Service:
// assignment.service.ts
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { Assignment } from './assignment.entity';
@Injectable()
export class AssignmentService {
constructor(
@InjectRepository(Assignment)
private assignmentRepository: Repository<Assignment>,
) {}
findAll(): Promise<Assignment[]> {
return this.assignmentRepository.find({ relations: ['course'] });
}
findOne(id: number): Promise<Assignment> {
return this.assignmentRepository.findOne(id, { relations: ['course'] });
}
create(title: string, description: string, dueDate: Date, courseId: number): Promise<Assignment> {
const newAssignment = this.assignmentRepository.create({ title, description, dueDate, course: { id: courseId } });
return this.assignmentRepository.save(newAssignment);
}
}
Submission Service:
// submission.service.ts
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { Submission } from './submission.entity';
@Injectable()
export class SubmissionService {
constructor(
@InjectRepository(Submission)
private submissionRepository: Repository<Submission>,
) {}
findAll(): Promise<Submission[]> {
return this.submissionRepository.find({ relations: ['assignment', 'student'] });
}
findOne(id: number): Promise<Submission> {
return this.submissionRepository.findOne(id, { relations: ['assignment', 'student'] });
}
create(content: string, assignmentId: number, studentId: number): Promise<Submission> {
const newSubmission = this.submissionRepository.create({
content,
submittedAt: new Date(),
assignment: { id: assignmentId },
student: { id: studentId },
});
return this.submissionRepository.save(newSubmission);
}
}
3. Resolvers
Course Resolver:
// course.resolver.ts
import { Resolver, Query, Mutation, Args } from '@nestjs/graphql';
import { CourseService } from './course.service';
import { Course } from './course.entity';
@Resolver(() => Course)
export class CourseResolver {
constructor(private courseService: CourseService) {}
@Query(() => [Course])
async courses() {
return this.courseService.findAll();
}
@Mutation(() => Course)
async createCourse(
@Args('name') name: string,
@Args('description') description: string,
) {
return this.courseService.create(name, description);
}
}
Assignment Resolver:
// assignment.resolver.ts
import { Resolver, Query, Mutation, Args } from '@nestjs/graphql';
import { AssignmentService } from './assignment.service';
import { Assignment } from './assignment.entity';
@Resolver(() => Assignment)
export class AssignmentResolver {
constructor(private assignmentService: AssignmentService) {}
@Query(() => [Assignment])
async assignments() {
return this.assignmentService.findAll();
}
@Mutation(() => Assignment)
async createAssignment(
@Args('title') title: string,
@Args('description') description: string,
@Args('dueDate') dueDate: string,
@Args('courseId') courseId: number,
) {
return this.assignmentService.create(title, description, new Date(dueDate), courseId);
}
}
Submission Resolver:
// submission.resolver.ts
import { Resolver, Query, Mutation, Args } from '@nestjs/graphql';
import { SubmissionService } from './submission.service';
import { Submission } from './submission.entity';
@Resolver(() => Submission)
export class SubmissionResolver {
constructor(private submissionService: SubmissionService) {}
@Query(() => [Submission])
async submissions() {
return this.submissionService.findAll();
}
@Mutation(() => Submission)
async createSubmission(
@Args('content') content: string,
@Args('assignmentId') assignmentId: number,
@Args('studentId') studentId: number,
) {
return this.submissionService.create(content, assignmentId, studentId);
}
}
Frontend (Next.js)
1. Apollo Client Setup
// apollo-client.js
import { ApolloClient, InMemoryCache } from '@apollo/client';
const client = new ApolloClient({
uri: 'http://localhost:3000/graphql',
cache: new InMemoryCache(),
});
export default client;
2. Course Management Page
// pages/courses.js
import { useState } from 'react';
import { useQuery, useMutation, gql } from '@apollo/client';
const GET_COURSES = gql`
query GetCourses {
courses {
id
name
description
assignments {
id
title
dueDate
}
}
}
`;
const CREATE_COURSE = gql`
mutation CreateCourse($name: String!, $description: String!) {
createCourse(name: $name, description: $description) {
id
name
description
}
}
`;
export default function Courses() {
const { loading, error, data } = useQuery(GET_COURSES);
const [createCourse] = useMutation(CREATE_COURSE);
const [name, setName] = useState('');
const [description, setDescription] = useState('');
const handleSubmit = async (e) => {
e.preventDefault();
await createCourse({ variables: { name, description } });
setName('');
setDescription('');
};
if (loading) return <p>Loading...</p>;
if (error) return <p>Error: {error.message}</p>;
return (
<div>
<h1>Courses</h1>
<form onSubmit={handleSubmit}>
<input
type="text"
placeholder="Course Name"
value={name}
onChange={(e) => setName(e.target.value)}
/>
<textarea
placeholder="Course Description"
value={description}
onChange={(e) => setDescription(e.target.value)}
></textarea>
<button type="submit">Create Course</button>
</form>
<ul>
{data.courses.map((course) => (
<li key={course.id}>
<h2>{course.name}</h2>
<p>{course.description}</p>
<h3>Assignments</h3>
<ul>
{course.assignments.map((assignment) => (
<li key={assignment.id}>
{assignment.title} - Due: {assignment.dueDate}
</li>
))}
</ul>
</li>
))}
</ul>
</div>
);
}
3. Assignment Management
Page
// pages/assignments.js
import { useState } from 'react';
import { useQuery, useMutation, gql } from '@apollo/client';
const GET_ASSIGNMENTS = gql`
query GetAssignments {
assignments {
id
title
description
dueDate
course {
name
}
}
}
`;
const CREATE_ASSIGNMENT = gql`
mutation CreateAssignment($title: String!, $description: String!, $dueDate: String!, $courseId: Int!) {
createAssignment(title: $title, description: $description, dueDate: $dueDate, courseId: $courseId) {
id
title
description
dueDate
}
}
`;
export default function Assignments() {
const { loading, error, data } = useQuery(GET_ASSIGNMENTS);
const [createAssignment] = useMutation(CREATE_ASSIGNMENT);
const [title, setTitle] = useState('');
const [description, setDescription] = useState('');
const [dueDate, setDueDate] = useState('');
const [courseId, setCourseId] = useState('');
const handleSubmit = async (e) => {
e.preventDefault();
await createAssignment({ variables: { title, description, dueDate, courseId: parseInt(courseId) } });
setTitle('');
setDescription('');
setDueDate('');
setCourseId('');
};
if (loading) return <p>Loading...</p>;
if (error) return <p>Error: {error.message}</p>;
return (
<div>
<h1>Assignments</h1>
<form onSubmit={handleSubmit}>
<input
type="text"
placeholder="Title"
value={title}
onChange={(e) => setTitle(e.target.value)}
/>
<textarea
placeholder="Description"
value={description}
onChange={(e) => setDescription(e.target.value)}
></textarea>
<input
type="date"
placeholder="Due Date"
value={dueDate}
onChange={(e) => setDueDate(e.target.value)}
/>
<input
type="number"
placeholder="Course ID"
value={courseId}
onChange={(e) => setCourseId(e.target.value)}
/>
<button type="submit">Create Assignment</button>
</form>
<ul>
{data.assignments.map((assignment) => (
<li key={assignment.id}>
<h2>{assignment.title}</h2>
<p>{assignment.description}</p>
<p>Due Date: {assignment.dueDate}</p>
<p>Course: {assignment.course.name}</p>
</li>
))}
</ul>
</div>
);
}
4. Submission Management Page
// pages/submissions.js
import { useState } from 'react';
import { useQuery, useMutation, gql } from '@apollo/client';
const GET_SUBMISSIONS = gql`
query GetSubmissions {
submissions {
id
content
submittedAt
assignment {
title
}
student {
username
}
}
}
`;
const CREATE_SUBMISSION = gql`
mutation CreateSubmission($content: String!, $assignmentId: Int!, $studentId: Int!) {
createSubmission(content: $content, assignmentId: $assignmentId, studentId: $studentId) {
id
content
submittedAt
}
}
`;
export default function Submissions() {
const { loading, error, data } = useQuery(GET_SUBMISSIONS);
const [createSubmission] = useMutation(CREATE_SUBMISSION);
const [content, setContent] = useState('');
const [assignmentId, setAssignmentId] = useState('');
const [studentId, setStudentId] = useState('');
const handleSubmit = async (e) => {
e.preventDefault();
await createSubmission({ variables: { content, assignmentId: parseInt(assignmentId), studentId: parseInt(studentId) } });
setContent('');
setAssignmentId('');
setStudentId('');
};
if (loading) return <p>Loading...</p>;
if (error) return <p>Error: {error.message}</p>;
return (
<div>
<h1>Submissions</h1>
<form onSubmit={handleSubmit}>
<textarea
placeholder="Content"
value={content}
onChange={(e) => setContent(e.target.value)}
></textarea>
<input
type="number"
placeholder="Assignment ID"
value={assignmentId}
onChange={(e) => setAssignmentId(e.target.value)}
/>
<input
type="number"
placeholder="Student ID"
value={studentId}
onChange={(e) => setStudentId(e.target.value)}
/>
<button type="submit">Submit Assignment</button>
</form>
<ul>
{data.submissions.map((submission) => (
<li key={submission.id}>
<h2>{submission.assignment.title}</h2>
<p>Submitted by: {submission.student.username}</p>
<p>Submitted at: {submission.submittedAt}</p>
<p>Content: {submission.content}</p>
</li>
))}
</ul>
</div>
);
}
Virtual Classroom (Integration)
For virtual classroom functionality, you can integrate a service like Zoom, Google Meet, or any other preferred video conferencing tool. You will need to manage scheduling and links to these virtual classes.
Virtual Class Entity:
// virtual-class.entity.ts
import { Entity, Column, PrimaryGeneratedColumn, ManyToOne } from 'typeorm';
import { Course } from './course.entity';
@Entity()
export class VirtualClass {
@PrimaryGeneratedColumn()
id: number;
@Column()
meetingLink: string;
@Column()
schedule: Date;
@ManyToOne(() => Course, (course) => course.virtualClasses)
course: Course;
}
Virtual Class Service:
// virtual-class.service.ts
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { VirtualClass } from './virtual-class.entity';
@Injectable()
export class VirtualClassService {
constructor(
@InjectRepository(VirtualClass)
private virtualClassRepository: Repository<VirtualClass>,
) {}
findAll(): Promise<VirtualClass[]> {
return this.virtualClassRepository.find({ relations: ['course'] });
}
create(meetingLink: string, schedule: Date, courseId: number): Promise<VirtualClass> {
const newVirtualClass = this.virtualClassRepository.create({ meetingLink, schedule, course: { id: courseId } });
return this.virtualClassRepository.save(newVirtualClass);
}
}
Virtual Class Resolver:
// virtual-class.resolver.ts
import { Resolver, Query, Mutation, Args } from '@nestjs/graphql';
import { VirtualClassService } from './virtual-class.service';
import { VirtualClass } from './virtual-class.entity';
@Resolver(() => VirtualClass)
export class VirtualClassResolver {
constructor(private virtualClassService: VirtualClassService) {}
@Query(() => [VirtualClass])
async virtualClasses() {
return this.virtualClassService.findAll();
}
@Mutation(() => VirtualClass)
async createVirtualClass(
@Args('meetingLink') meetingLink: string,
@Args('schedule') schedule: string,
@Args('courseId') courseId: number,
) {
return this.virtualClassService.create(meetingLink, new Date(schedule), courseId);
}
}
Virtual Classroom Management Page:
// pages/virtual-classes.js
import { useState } from 'react';
import { useQuery, useMutation, gql } from '@apollo/client';
const GET_VIRTUAL_CLASSES = gql`
query GetVirtualClasses {
virtualClasses {
id
meetingLink
schedule
course {
name
}
}
}
`;
const CREATE_VIRTUAL_CLASS = gql`
mutation CreateVirtualClass($meetingLink: String!, $schedule: String!, $courseId: Int!) {
createVirtualClass(meetingLink: $meetingLink, schedule: $schedule, courseId: $courseId) {
id
meetingLink
schedule
}
}
`;
export default function VirtualClasses() {
const { loading, error, data } = useQuery(GET_VIRTUAL_CLASSES);
const [createVirtualClass] = useMutation(CREATE_VIRTUAL_CLASS);
const [meetingLink, setMeetingLink] = useState('');
const [schedule, setSchedule] = useState('');
const [courseId, setCourseId] = useState('');
const handleSubmit = async (e) => {
e.preventDefault();
await createVirtualClass({ variables: { meetingLink, schedule, courseId: parseInt(courseId) } });
setMeetingLink('');
setSchedule('');
setCourseId('');
};
if (loading) return <p>Loading...</p>;
if (error) return <p>Error: {error.message}</p>;
return (
<div>
<h1>Virtual Classes</h1>
<form onSubmit={handleSubmit}>
<input
type="text"
placeholder="Meeting Link"
value={meetingLink}
onChange={(e
) => setMeetingLink(e.target.value)}
/>
<input
type="datetime-local"
placeholder="Schedule"
value={schedule}
onChange={(e) => setSchedule(e.target.value)}
/>
<input
type="number"
placeholder="Course ID"
value={courseId}
onChange={(e) => setCourseId(e.target.value)}
/>
<button type="submit">Create Virtual Class</button>
</form>
<ul>
{data.virtualClasses.map((virtualClass) => (
<li key={virtualClass.id}>
<p>Meeting Link: <a href={virtualClass.meetingLink} target="_blank" rel="noopener noreferrer">Join</a></p>
<p>Schedule: {virtualClass.schedule}</p>
<p>Course: {virtualClass.course.name}</p>
</li>
))}
</ul>
</div>
);
}
GraphQL Schema
Define your GraphQL schema to match the resolver functions:
type User {
id: ID!
username: String!
}
type Course {
id: ID!
name: String!
description: String!
assignments: [Assignment!]!
}
type Assignment {
id: ID!
title: String!
description: String!
dueDate: String!
course: Course!
}
type Submission {
id: ID!
content: String!
submittedAt: String!
assignment: Assignment!
student: User!
}
type VirtualClass {
id: ID!
meetingLink: String!
schedule: String!
course: Course!
}
type Query {
courses: [Course!]!
assignments: [Assignment!]!
submissions: [Submission!]!
virtualClasses: [VirtualClass!]!
}
type Mutation {
createCourse(name: String!, description: String!): Course!
createAssignment(title: String!, description: String!, dueDate: String!, courseId: Int!): Assignment!
createSubmission(content: String!, assignmentId: Int!, studentId: Int!): Submission!
createVirtualClass(meetingLink: String!, schedule: String!, courseId: Int!): VirtualClass!
}
This setup covers the backend and frontend code for developing online course management, assignment submission, and virtual classroom features. You can expand on this by adding more features such as grading, feedback, and advanced scheduling for virtual classes.
Adding Grading, Feedback, and Advanced Scheduling
To add grading, feedback, and advanced scheduling for virtual classes, we'll update the existing system to include these features.
Backend (NestJS)
1. Update Entities
Assignment Entity:
// assignment.entity.ts
import { Entity, Column, PrimaryGeneratedColumn, ManyToOne, OneToMany } from 'typeorm';
import { Course } from './course.entity';
import { Submission } from './submission.entity';
@Entity()
export class Assignment {
@PrimaryGeneratedColumn()
id: number;
@Column()
title: string;
@Column()
description: string;
@Column()
dueDate: Date;
@ManyToOne(() => Course, (course) => course.assignments)
course: Course;
@OneToMany(() => Submission, (submission) => submission.assignment)
submissions: Submission[];
}
Submission Entity:
// submission.entity.ts
import { Entity, Column, PrimaryGeneratedColumn, ManyToOne } from 'typeorm';
import { Assignment } from './assignment.entity';
import { User } from './user.entity';
@Entity()
export class Submission {
@PrimaryGeneratedColumn()
id: number;
@ManyToOne(() => Assignment, (assignment) => assignment.submissions)
assignment: Assignment;
@ManyToOne(() => User, (user) => user.submissions)
student: User;
@Column()
content: string;
@Column({ nullable: true })
grade: number;
@Column({ nullable: true })
feedback: string;
@Column()
submittedAt: Date;
}
Virtual Class Entity:
// virtual-class.entity.ts
import { Entity, Column, PrimaryGeneratedColumn, ManyToOne } from 'typeorm';
import { Course } from './course.entity';
@Entity()
export class VirtualClass {
@PrimaryGeneratedColumn()
id: number;
@Column()
meetingLink: string;
@Column()
schedule: Date;
@ManyToOne(() => Course, (course) => course.virtualClasses)
course: Course;
}
2. Update Services
Assignment Service:
// assignment.service.ts
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { Assignment } from './assignment.entity';
@Injectable()
export class AssignmentService {
constructor(
@InjectRepository(Assignment)
private assignmentRepository: Repository<Assignment>,
) {}
findAll(): Promise<Assignment[]> {
return this.assignmentRepository.find({ relations: ['course', 'submissions'] });
}
findOne(id: number): Promise<Assignment> {
return this.assignmentRepository.findOne(id, { relations: ['course', 'submissions'] });
}
create(title: string, description: string, dueDate: Date, courseId: number): Promise<Assignment> {
const newAssignment = this.assignmentRepository.create({ title, description, dueDate, course: { id: courseId } });
return this.assignmentRepository.save(newAssignment);
}
gradeSubmission(submissionId: number, grade: number, feedback: string): Promise<Assignment> {
return this.assignmentRepository.update({ id: submissionId }, { grade, feedback });
}
}
Virtual Class Service:
// virtual-class.service.ts
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { VirtualClass } from './virtual-class.entity';
@Injectable()
export class VirtualClassService {
constructor(
@InjectRepository(VirtualClass)
private virtualClassRepository: Repository<VirtualClass>,
) {}
findAll(): Promise<VirtualClass[]> {
return this.virtualClassRepository.find({ relations: ['course'] });
}
create(meetingLink: string, schedule: Date, courseId: number): Promise<VirtualClass> {
const newVirtualClass = this.virtualClassRepository.create({ meetingLink, schedule, course: { id: courseId } });
return this.virtualClassRepository.save(newVirtualClass);
}
updateSchedule(id: number, schedule: Date): Promise<VirtualClass> {
return this.virtualClassRepository.update(id, { schedule });
}
}
3. Update Resolvers
Assignment Resolver:
// assignment.resolver.ts
import { Resolver, Query, Mutation, Args } from '@nestjs/graphql';
import { AssignmentService } from './assignment.service';
import { Assignment } from './assignment.entity';
@Resolver(() => Assignment)
export class AssignmentResolver {
constructor(private assignmentService: AssignmentService) {}
@Query(() => [Assignment])
async assignments() {
return this.assignmentService.findAll();
}
@Mutation(() => Assignment)
async createAssignment(
@Args('title') title: string,
@Args('description') description: string,
@Args('dueDate') dueDate: string,
@Args('courseId') courseId: number,
) {
return this.assignmentService.create(title, description, new Date(dueDate), courseId);
}
@Mutation(() => Assignment)
async gradeSubmission(
@Args('submissionId') submissionId: number,
@Args('grade') grade: number,
@Args('feedback') feedback: string,
) {
return this.assignmentService.gradeSubmission(submissionId, grade, feedback);
}
}
Virtual Class Resolver:
// virtual-class.resolver.ts
import { Resolver, Query, Mutation, Args } from '@nestjs/graphql';
import { VirtualClassService } from './virtual-class.service';
import { VirtualClass } from './virtual-class.entity';
@Resolver(() => VirtualClass)
export class VirtualClassResolver {
constructor(private virtualClassService: VirtualClassService) {}
@Query(() => [VirtualClass])
async virtualClasses() {
return this.virtualClassService.findAll();
}
@Mutation(() => VirtualClass)
async createVirtualClass(
@Args('meetingLink') meetingLink: string,
@Args('schedule') schedule: string,
@Args('courseId') courseId: number,
) {
return this.virtualClassService.create(meetingLink, new Date(schedule), courseId);
}
@Mutation(() => VirtualClass)
async updateSchedule(
@Args('id') id: number,
@Args('schedule') schedule: string,
) {
return this.virtualClassService.updateSchedule(id, new Date(schedule));
}
}
Frontend (Next.js)
1. Apollo Client Setup
// apollo-client.js
import { ApolloClient, InMemoryCache } from '@apollo/client';
const client = new ApolloClient({
uri: 'http://localhost:3000/graphql',
cache: new InMemoryCache(),
});
export default client;
2. Update Assignment Management Page
// pages/assignments.js
import { useState } from 'react';
import { useQuery, useMutation, gql } from '@apollo/client';
const GET_ASSIGNMENTS = gql`
query GetAssignments {
assignments {
id
title
description
dueDate
course {
name
}
submissions {
id
content
submittedAt
grade
feedback
student {
username
}
}
}
}
`;
const CREATE_ASSIGNMENT = gql`
mutation CreateAssignment($title: String!, $description: String!, $dueDate: String!, $courseId: Int!) {
createAssignment(title: $title, description: $description, dueDate: $dueDate, courseId: $courseId) {
id
title
description
dueDate
}
}
`;
const GRADE_SUBMISSION = gql`
mutation GradeSubmission($submissionId: Int!, $grade: Int!, $feedback: String!) {
gradeSubmission(submissionId: $submissionId, grade: $grade, feedback: $feedback) {
id
grade
feedback
}
}
`;
export default function Assignments() {
const { loading, error, data } = useQuery(GET_ASSIGNMENTS);
const [createAssignment] = useMutation(CREATE_ASSIGNMENT);
const [gradeSubmission] = useMutation(GRADE_SUBMISSION);
const [title, setTitle] = useState('');
const [description, setDescription] = useState('');
const [dueDate, setDueDate] = useState('');
const [courseId, setCourseId] = useState('');
const [submissionId, setSubmissionId] = useState('');
const [grade, setGrade] = useState('');
const [feedback, setFeedback] = useState('');
const handleCreateAssignment = async (e) => {
e.preventDefault();
await createAssignment({ variables: { title, description, dueDate, courseId: parseInt(courseId) } });
setTitle('');
setDescription('');
setDueDate('');
setCourseId('');
};
const handleGradeSubmission = async (e) => {
e.preventDefault();
await gradeSubmission({ variables: { submissionId: parseInt(submissionId), grade: parseInt(grade), feedback } });
setSubmissionId('');
setGrade('');
setFeedback('');
};
if (loading) return <p>Loading...</p>;
if (error) return <p>Error: {error.message}</p>;
return (
<div>
<h1>Assignments</h1>
<form onSubmit={handleCreateAssignment}>
<input
type="text"
placeholder="Title"
value={title}
onChange={(e) => setTitle(e.target.value)}
/>
<textarea
placeholder="Description"
value={description}
onChange={(e) => setDescription(e.target.value)}
></textarea>
<input
type="date"
placeholder="Due Date"
value={dueDate}
onChange={(e) => setDueDate(e.target.value)}
/>
<input
type="number"
placeholder="Course ID"
value={courseId}
onChange={(e) => setCourseId(e.target.value)}
/>
<button type="submit">Create Assignment</button>
</form>
<form onSubmit={handleGradeSubmission}>
<input
type="number"
placeholder="Submission ID"
value={submissionId}
onChange={(e) => setSubmissionId(e.target.value)}
/>
<input
type="number"
placeholder="Grade"
value={grade}
onChange={(e) => setGrade(e.target.value)}
/>
<textarea
placeholder="Feedback"
value={feedback}
onChange={(e) => setFeedback(e.target.value)}
></textarea>
<button type="submit">Grade Submission</button>
</form>
<ul>
{data.assignments.map((assignment) => (
<li key={assignment.id}>
<h2>{assignment.title}</h2>
<p>{assignment.description}</p>
<p>Due Date: {assignment.dueDate}</p>
<p>Course: {assignment.course.name}</p>
<h3>Submissions</h3>
<ul>
{assignment.submissions.map((submission) => (
<li key={submission.id}>
<p>Submitted by: {submission.student.username}</p>
<p>Submitted at: {submission.submittedAt}</p>
<p>Content: {submission.content}</p>
<p>Grade: {submission.grade}</p>
<p>Feedback: {submission.feedback}</p>
</li>
))}
</ul>
</li>
))}
</ul>
</div>
);
}
3. Update Virtual Classroom Management Page
// pages/virtual-classes.js
import { useState } from 'react';
import { useQuery, useMutation, gql } from '@apollo/client';
const GET_VIRTUAL_CLASSES = gql`
query GetVirtualClasses {
virtualClasses {
id
meetingLink
schedule
course {
name
}
}
}
`;
const CREATE_VIRTUAL_CLASS = gql`
mutation CreateVirtualClass($meetingLink: String!, $schedule: String!, $courseId: Int!) {
createVirtualClass(meetingLink: $meetingLink, schedule: $schedule, courseId: $courseId) {
id
meetingLink
schedule
}
}
`;
const UPDATE_SCHEDULE = gql`
mutation UpdateSchedule($id: Int!, $schedule: String!) {
updateSchedule(id: $id, schedule: $schedule) {
id
schedule
}
}
`;
export default function VirtualClasses() {
const { loading, error, data } = useQuery(GET_VIRTUAL_CLASSES);
const [createVirtualClass] = useMutation(CREATE_VIRTUAL_CLASS);
const [updateSchedule] = useMutation(UPDATE_SCHEDULE);
const [meetingLink, setMeetingLink] = useState('');
const [schedule, setSchedule] = useState('');
const [courseId, setCourseId] = useState('');
const [classId, setClassId] = useState('');
const handleCreateVirtualClass = async (e) => {
e.preventDefault();
await createVirtualClass({ variables: { meetingLink, schedule, courseId: parseInt(courseId) } });
setMeetingLink('');
setSchedule('');
setCourseId('');
};
const handleUpdateSchedule = async (e) => {
e.preventDefault();
await updateSchedule({ variables: { id: parseInt(classId), schedule } });
setClassId('');
setSchedule('');
};
if (loading) return <p>Loading...</p>;
if (error) return <p>Error: {error.message}</p>;
return (
<div>
<h1>Virtual Classes</h1>
<form onSubmit={handleCreateVirtualClass}>
<input
type="text"
placeholder="Meeting Link"
value={meetingLink}
onChange={(e) => setMeetingLink(e.target.value)}
/>
<input
type="datetime-local"
placeholder="Schedule"
value={schedule}
onChange={(e) => setSchedule(e.target.value)}
/>
<input
type="number"
placeholder="Course ID"
value={courseId}
onChange={(e) => setCourseId(e.target.value)}
/>
<button type="submit">Create Virtual Class</button>
</form>
<form onSubmit={handleUpdateSchedule}>
<input
type="number"
placeholder="Class ID"
value={classId}
onChange={(e) => setClassId(e.target.value)}
/>
<input
type="datetime-local"
placeholder="New Schedule"
value={schedule}
onChange={(e) => setSchedule(e.target.value)}
/>
<button type="submit">Update Schedule</button>
</form>
<ul>
{data.virtualClasses.map((virtualClass) => (
<li key={virtualClass.id}>
<p>Meeting Link: <a href={virtualClass.meetingLink} target="_blank" rel="noopener noreferrer">Join</a></p>
<p>Schedule: {virtualClass.schedule}</p>
<p>Course: {virtualClass.course.name}</p>
</li>
))}
</ul>
</div>
);
}
GraphQL Schema
Update your GraphQL schema to include the new fields and mutations:
type User {
id: ID!
username: String!
}
type Course {
id: ID!
name: String!
description: String!
assignments: [Assignment!]!
}
type Assignment {
id: ID!
title: String!
description: String!
dueDate: String!
course: Course!
submissions: [Submission!]!
}
type Submission {
id: ID!
content: String!
submittedAt: String!
grade: Int
feedback: String
assignment: Assignment!
student: User!
}
type VirtualClass {
id: ID!
meetingLink: String!
schedule: String!
course: Course!
}
type Query {
courses: [Course!]!
assignments: [Assignment!]!
submissions: [Submission!]!
virtualClasses: [VirtualClass!]!
}
type Mutation {
createCourse(name: String!, description: String!): Course!
createAssignment(title: String!, description: String!, dueDate: String!, courseId: Int!): Assignment!
createSubmission(content: String!, assignmentId: Int!, studentId: Int!): Submission!
gradeSubmission(submissionId: Int!, grade: Int!, feedback: String!): Submission!
createVirtualClass(meetingLink: String!, schedule: String!, courseId: Int!): VirtualClass!
updateSchedule(id: Int!, schedule: String!): VirtualClass!
}
This expanded setup includes grading, feedback, and advanced scheduling for virtual classes, covering the backend and frontend code needed to implement these features. You can further enhance these features by adding notifications, more detailed reports, and improved user interfaces.
If you enjoy my content and would like to support my work, you can buy me a coffee. Your support is greatly appreciated!
Disclaimer: This content in generated by AI.
Top comments (0)