Below is an implementation for Academic Management, including features for class and subject management, timetable creation and management, and attendance tracking and reporting using Next.js, NestJS, and GraphQL.
Backend (NestJS)
1. Entities
Class Entity:
// class.entity.ts
import { Entity, Column, PrimaryGeneratedColumn, ManyToOne } from 'typeorm';
import { Teacher } from './teacher.entity';
@Entity()
export class Class {
@PrimaryGeneratedColumn()
id: number;
@Column()
name: string;
@ManyToOne(() => Teacher, (teacher) => teacher.classes)
teacher: Teacher;
}
Subject Entity:
// subject.entity.ts
import { Entity, Column, PrimaryGeneratedColumn, ManyToOne } from 'typeorm';
import { Class } from './class.entity';
@Entity()
export class Subject {
@PrimaryGeneratedColumn()
id: number;
@Column()
name: string;
@ManyToOne(() => Class, (cls) => cls.subjects)
class: Class;
}
Timetable Entity:
// timetable.entity.ts
import { Entity, Column, PrimaryGeneratedColumn, ManyToOne } from 'typeorm';
import { Class } from './class.entity';
import { Subject } from './subject.entity';
@Entity()
export class Timetable {
@PrimaryGeneratedColumn()
id: number;
@ManyToOne(() => Class, (cls) => cls.timetables)
class: Class;
@ManyToOne(() => Subject, (subject) => subject.timetables)
subject: Subject;
@Column()
day: string;
@Column()
startTime: string;
@Column()
endTime: string;
}
Attendance Entity:
// attendance.entity.ts
import { Entity, Column, PrimaryGeneratedColumn, ManyToOne } from 'typeorm';
import { Class } from './class.entity';
import { Student } from './student.entity';
@Entity()
export class Attendance {
@PrimaryGeneratedColumn()
id: number;
@ManyToOne(() => Class, (cls) => cls.attendances)
class: Class;
@ManyToOne(() => Student, (student) => student.attendances)
student: Student;
@Column()
date: string;
@Column()
status: string; // Present or Absent
}
2. Services
Class Service:
// class.service.ts
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { Class } from './class.entity';
@Injectable()
export class ClassService {
constructor(
@InjectRepository(Class)
private classRepository: Repository<Class>,
) {}
findAll(): Promise<Class[]> {
return this.classRepository.find();
}
findOne(id: number): Promise<Class> {
return this.classRepository.findOne(id);
}
create(name: string, teacherId: number): Promise<Class> {
const newClass = this.classRepository.create({ name, teacher: { id: teacherId } });
return this.classRepository.save(newClass);
}
}
Subject Service:
// subject.service.ts
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { Subject } from './subject.entity';
@Injectable()
export class SubjectService {
constructor(
@InjectRepository(Subject)
private subjectRepository: Repository<Subject>,
) {}
findAll(): Promise<Subject[]> {
return this.subjectRepository.find();
}
findOne(id: number): Promise<Subject> {
return this.subjectRepository.findOne(id);
}
create(name: string, classId: number): Promise<Subject> {
const newSubject = this.subjectRepository.create({ name, class: { id: classId } });
return this.subjectRepository.save(newSubject);
}
}
Timetable Service:
// timetable.service.ts
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { Timetable } from './timetable.entity';
@Injectable()
export class TimetableService {
constructor(
@InjectRepository(Timetable)
private timetableRepository: Repository<Timetable>,
) {}
findAll(): Promise<Timetable[]> {
return this.timetableRepository.find();
}
create(classId: number, subjectId: number, day: string, startTime: string, endTime: string): Promise<Timetable> {
const newTimetable = this.timetableRepository.create({ class: { id: classId }, subject: { id: subjectId }, day, startTime, endTime });
return this.timetableRepository.save(newTimetable);
}
}
Attendance Service:
// attendance.service.ts
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { Attendance } from './attendance.entity';
@Injectable()
export class AttendanceService {
constructor(
@InjectRepository(Attendance)
private attendanceRepository: Repository<Attendance>,
) {}
findAll(): Promise<Attendance[]> {
return this.attendanceRepository.find();
}
create(classId: number, studentId: number, date: string, status: string): Promise<Attendance> {
const newAttendance = this.attendanceRepository.create({ class: { id: classId }, student: { id: studentId }, date, status });
return this.attendanceRepository.save(newAttendance);
}
}
3. Resolvers
Class Resolver:
// class.resolver.ts
import { Resolver, Query, Mutation, Args } from '@nestjs/graphql';
import { ClassService } from './class.service';
import { Class } from './class.entity';
@Resolver(() => Class)
export class ClassResolver {
constructor(private classService: ClassService) {}
@Query(() => [Class])
async classes() {
return this.classService.findAll();
}
@Mutation(() => Class)
async createClass(@Args('name') name: string, @Args('teacherId') teacherId: number) {
return this.classService.create(name, teacherId);
}
}
Subject Resolver:
// subject.resolver.ts
import { Resolver, Query, Mutation, Args } from '@nestjs/graphql';
import { SubjectService } from './subject.service';
import { Subject } from './subject.entity';
@Resolver(() => Subject)
export class SubjectResolver {
constructor(private subjectService: SubjectService) {}
@Query(() => [Subject])
async subjects() {
return this.subjectService.findAll();
}
@Mutation(() => Subject)
async createSubject(@Args('name') name: string, @Args('classId') classId: number) {
return this.subjectService.create(name, classId);
}
}
Timetable Resolver:
// timetable.resolver.ts
import { Resolver, Query, Mutation, Args } from '@nestjs/graphql';
import { TimetableService } from './timetable.service';
import { Timetable } from './timetable.entity';
@Resolver(() => Timetable)
export class TimetableResolver {
constructor(private timetableService: TimetableService) {}
@Query(() => [Timetable])
async timetables() {
return this.timetableService.findAll();
}
@Mutation(() => Timetable)
async createTimetable(
@Args('classId') classId: number,
@Args('subjectId') subjectId: number,
@Args('day') day: string,
@Args('startTime') startTime: string,
@Args('endTime') endTime: string,
) {
return this.timetableService.create(classId, subjectId, day, startTime, endTime);
}
}
Attendance Resolver:
// attendance.resolver.ts
import { Resolver, Query, Mutation, Args } from '@nestjs/graphql';
import { AttendanceService } from './attendance.service';
import { Attendance } from './attendance.entity';
@Resolver(() => Attendance)
export class AttendanceResolver {
constructor(private attendanceService: AttendanceService) {}
@Query(() => [Attendance])
async attendances() {
return this.attendanceService.findAll();
}
@Mutation(() => Attendance)
async markAttendance(
@Args('classId') classId: number,
@Args('studentId') studentId: number,
@Args('date') date: string,
@Args('status') status: string,
) {
return this.attendanceService.create(classId, studentId, date, status);
}
}
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. Class Management Page
// pages/classes.js
import { useState } from 'react';
import { useQuery, useMutation, gql } from '@apollo/client';
const GET_CLASSES = gql`
query GetClasses {
classes {
id
name
teacher {
name
}
}
}
`;
const CREATE_CLASS = gql`
mutation CreateClass($name: String!, $teacherId: Int
!) {
createClass(name: $name, teacherId: $teacherId) {
id
name
}
}
`;
export default function Classes() {
const { loading, error, data } = useQuery(GET_CLASSES);
const [createClass] = useMutation(CREATE_CLASS);
const [name, setName] = useState('');
const [teacherId, setTeacherId] = useState('');
const handleSubmit = async (e) => {
e.preventDefault();
await createClass({ variables: { name, teacherId: parseInt(teacherId) } });
setName('');
setTeacherId('');
};
if (loading) return <p>Loading...</p>;
if (error) return <p>Error: {error.message}</p>;
return (
<div>
<h1>Classes</h1>
<form onSubmit={handleSubmit}>
<input
type="text"
placeholder="Class Name"
value={name}
onChange={(e) => setName(e.target.value)}
/>
<input
type="number"
placeholder="Teacher ID"
value={teacherId}
onChange={(e) => setTeacherId(e.target.value)}
/>
<button type="submit">Create Class</button>
</form>
<ul>
{data.classes.map((cls) => (
<li key={cls.id}>
{cls.name} - {cls.teacher.name}
</li>
))}
</ul>
</div>
);
}
3. Subject Management Page
// pages/subjects.js
import { useState } from 'react';
import { useQuery, useMutation, gql } from '@apollo/client';
const GET_SUBJECTS = gql`
query GetSubjects {
subjects {
id
name
class {
name
}
}
}
`;
const CREATE_SUBJECT = gql`
mutation CreateSubject($name: String!, $classId: Int!) {
createSubject(name: $name, classId: $classId) {
id
name
}
}
`;
export default function Subjects() {
const { loading, error, data } = useQuery(GET_SUBJECTS);
const [createSubject] = useMutation(CREATE_SUBJECT);
const [name, setName] = useState('');
const [classId, setClassId] = useState('');
const handleSubmit = async (e) => {
e.preventDefault();
await createSubject({ variables: { name, classId: parseInt(classId) } });
setName('');
setClassId('');
};
if (loading) return <p>Loading...</p>;
if (error) return <p>Error: {error.message}</p>;
return (
<div>
<h1>Subjects</h1>
<form onSubmit={handleSubmit}>
<input
type="text"
placeholder="Subject Name"
value={name}
onChange={(e) => setName(e.target.value)}
/>
<input
type="number"
placeholder="Class ID"
value={classId}
onChange={(e) => setClassId(e.target.value)}
/>
<button type="submit">Create Subject</button>
</form>
<ul>
{data.subjects.map((subject) => (
<li key={subject.id}>
{subject.name} - {subject.class.name}
</li>
))}
</ul>
</div>
);
}
4. Timetable Management Page
// pages/timetable.js
import { useState } from 'react';
import { useQuery, useMutation, gql } from '@apollo/client';
const GET_TIMETABLES = gql`
query GetTimetables {
timetables {
id
class {
name
}
subject {
name
}
day
startTime
endTime
}
}
`;
const CREATE_TIMETABLE = gql`
mutation CreateTimetable($classId: Int!, $subjectId: Int!, $day: String!, $startTime: String!, $endTime: String!) {
createTimetable(classId: $classId, subjectId: $subjectId, day: $day, startTime: $startTime, endTime: $endTime) {
id
day
startTime
endTime
}
}
`;
export default function Timetable() {
const { loading, error, data } = useQuery(GET_TIMETABLES);
const [createTimetable] = useMutation(CREATE_TIMETABLE);
const [classId, setClassId] = useState('');
const [subjectId, setSubjectId] = useState('');
const [day, setDay] = useState('');
const [startTime, setStartTime] = useState('');
const [endTime, setEndTime] = useState('');
const handleSubmit = async (e) => {
e.preventDefault();
await createTimetable({ variables: { classId: parseInt(classId), subjectId: parseInt(subjectId), day, startTime, endTime } });
setClassId('');
setSubjectId('');
setDay('');
setStartTime('');
setEndTime('');
};
if (loading) return <p>Loading...</p>;
if (error) return <p>Error: {error.message}</p>;
return (
<div>
<h1>Timetable</h1>
<form onSubmit={handleSubmit}>
<input
type="number"
placeholder="Class ID"
value={classId}
onChange={(e) => setClassId(e.target.value)}
/>
<input
type="number"
placeholder="Subject ID"
value={subjectId}
onChange={(e) => setSubjectId(e.target.value)}
/>
<input
type="text"
placeholder="Day"
value={day}
onChange={(e) => setDay(e.target.value)}
/>
<input
type="text"
placeholder="Start Time"
value={startTime}
onChange={(e) => setStartTime(e.target.value)}
/>
<input
type="text"
placeholder="End Time"
value={endTime}
onChange={(e) => setEndTime(e.target.value)}
/>
<button type="submit">Create Timetable</button>
</form>
<ul>
{data.timetables.map((tt) => (
<li key={tt.id}>
{tt.class.name} - {tt.subject.name} - {tt.day} - {tt.startTime} - {tt.endTime}
</li>
))}
</ul>
</div>
);
}
5. Attendance Tracking Page
// pages/attendance.js
import { useState } from 'react';
import { useQuery, useMutation, gql } from '@apollo/client';
const GET_ATTENDANCES = gql`
query GetAttendances {
attendances {
id
class {
name
}
student {
name
}
date
status
}
}
`;
const MARK_ATTENDANCE = gql`
mutation MarkAttendance($classId: Int!, $studentId: Int!, $date: String!, $status: String!) {
markAttendance(classId: $classId, studentId: $studentId, date: $date, status: $status) {
id
date
status
}
}
`;
export default function Attendance() {
const { loading, error, data } = useQuery(GET_ATTENDANCES);
const [markAttendance] = useMutation(MARK_ATTENDANCE);
const [classId, setClassId] = useState('');
const [studentId, setStudentId] = useState('');
const [date, setDate] = useState('');
const [status, setStatus] = useState('');
const handleSubmit = async (e) => {
e.preventDefault();
await markAttendance({ variables: { classId: parseInt(classId), studentId: parseInt(studentId), date, status } });
setClassId('');
setStudentId('');
setDate('');
setStatus('');
};
if (loading) return <p>Loading...</p>;
if (error) return <p>Error: {error.message}</p>;
return (
<div>
<h1>Attendance</h1>
<form onSubmit={handleSubmit}>
<input
type="number"
placeholder="Class ID"
value={classId}
onChange={(e) => setClassId(e.target.value)}
/>
<input
type="number"
placeholder="Student ID"
value={studentId}
onChange={(e) => setStudentId(e.target.value)}
/>
<input
type="date"
placeholder="Date"
value={date}
onChange={(e) => setDate(e.target.value)}
/>
<select value={status} onChange={(e) => setStatus(e.target.value)}>
<option value="Present">Present</option>
<option value="Absent">Absent</option>
</select>
<button type="submit">Mark Attendance</button>
</form>
<ul>
{data.attendances.map((att) => (
<li key={att.id}>
{att.class.name
} - {att.student.name} - {att.date} - {att.status}
</li>
))}
</ul>
</div>
);
}
GraphQL Schema
Define your GraphQL schema to match the resolver functions:
type Class {
id: ID!
name: String!
teacher: Teacher!
}
type Subject {
id: ID!
name: String!
class: Class!
}
type Timetable {
id: ID!
class: Class!
subject: Subject!
day: String!
startTime: String!
endTime: String!
}
type Attendance {
id: ID!
class: Class!
student: Student!
date: String!
status: String!
}
type Query {
classes: [Class!]!
subjects: [Subject!]!
timetables: [Timetable!]!
attendances: [Attendance!]!
}
type Mutation {
createClass(name: String!, teacherId: Int!): Class!
createSubject(name: String!, classId: Int!): Subject!
createTimetable(classId: Int!, subjectId: Int!, day: String!, startTime: String!, endTime: String!): Timetable!
markAttendance(classId: Int!, studentId: Int!, date: String!, status: String!): Attendance!
}
This setup covers the backend and frontend code for academic management in a School Management System. You can expand on this by adding more details, such as validations, error handling, and additional features as needed.
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)