Detail implementation of Academic Management
Published: 2024-06-13 15:08:17

Certainly! 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:**
```typescript
// 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:**
```typescript
// 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:**
```typescript
// 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:**
```typescript
// 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:**
```typescript
// 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:**
```typescript
// 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:**
```typescript
// 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:**
```typescript
// 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:**
```typescript
// 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:**
```typescript
// 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:**
```typescript
// 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:**
```typescript
// 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
```javascript
// 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
```javascript
// 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
```javascript
// 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
```javascript
// 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
```javascript
// 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:

```graphql
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.

Disclaimer: This content in generated by AI.