Below is an implementation for generating various reports for academic performance, attendance, and finances using Next.js, NestJS, and GraphQL.
Backend (NestJS)
1. Entities
We will use the entities defined previously for courses, assignments, submissions, fees, payments, and attendance.
2. Services
Report Service:
// report.service.ts
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { Course } from './course.entity';
import { Assignment } from './assignment.entity';
import { Submission } from './submission.entity';
import { Fee } from './fee.entity';
import { Payment } from './payment.entity';
import { Attendance } from './attendance.entity';
@Injectable()
export class ReportService {
constructor(
@InjectRepository(Course)
private courseRepository: Repository<Course>,
@InjectRepository(Assignment)
private assignmentRepository: Repository<Assignment>,
@InjectRepository(Submission)
private submissionRepository: Repository<Submission>,
@InjectRepository(Fee)
private feeRepository: Repository<Fee>,
@InjectRepository(Payment)
private paymentRepository: Repository<Payment>,
@InjectRepository(Attendance)
private attendanceRepository: Repository<Attendance>,
) {}
async getAcademicPerformanceReport(courseId: number) {
const course = await this.courseRepository.findOne(courseId, { relations: ['assignments', 'assignments.submissions'] });
const report = course.assignments.map(assignment => ({
assignmentTitle: assignment.title,
submissions: assignment.submissions.length,
averageScore: assignment.submissions.reduce((acc, sub) => acc + sub.score, 0) / assignment.submissions.length,
}));
return report;
}
async getAttendanceReport() {
const attendances = await this.attendanceRepository.find({ relations: ['student', 'class'] });
const report = attendances.map(attendance => ({
student: attendance.student.username,
class: attendance.class.name,
date: attendance.date,
status: attendance.status,
}));
return report;
}
async getFinancialReport() {
const fees = await this.feeRepository.find({ relations: ['user'] });
const payments = await this.paymentRepository.find({ relations: ['fee', 'fee.user'] });
const totalFees = fees.reduce((acc, fee) => acc + fee.amount, 0);
const totalPayments = payments.reduce((acc, payment) => acc + payment.amount, 0);
return {
totalFees,
totalPayments,
outstandingAmount: totalFees - totalPayments,
};
}
}
3. Resolvers
Report Resolver:
// report.resolver.ts
import { Resolver, Query, Args } from '@nestjs/graphql';
import { ReportService } from './report.service';
@Resolver()
export class ReportResolver {
constructor(private reportService: ReportService) {}
@Query(() => [Object])
async academicPerformanceReport(@Args('courseId') courseId: number) {
return this.reportService.getAcademicPerformanceReport(courseId);
}
@Query(() => [Object])
async attendanceReport() {
return this.reportService.getAttendanceReport();
}
@Query(() => Object)
async financialReport() {
return this.reportService.getFinancialReport();
}
}
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. Academic Performance Report Page
// pages/reports/academic-performance.js
import { useQuery, gql } from '@apollo/client';
import { useState } from 'react';
const GET_ACADEMIC_PERFORMANCE_REPORT = gql`
query GetAcademicPerformanceReport($courseId: Int!) {
academicPerformanceReport(courseId: $courseId) {
assignmentTitle
submissions
averageScore
}
}
`;
export default function AcademicPerformanceReport() {
const [courseId, setCourseId] = useState('');
const { loading, error, data, refetch } = useQuery(GET_ACADEMIC_PERFORMANCE_REPORT, {
variables: { courseId: parseInt(courseId) },
skip: !courseId,
});
const handleSubmit = (e) => {
e.preventDefault();
refetch();
};
return (
<div>
<h1>Academic Performance Report</h1>
<form onSubmit={handleSubmit}>
<input
type="number"
placeholder="Course ID"
value={courseId}
onChange={(e) => setCourseId(e.target.value)}
/>
<button type="submit">Generate Report</button>
</form>
{loading && <p>Loading...</p>}
{error && <p>Error: {error.message}</p>}
{data && (
<ul>
{data.academicPerformanceReport.map((report, index) => (
<li key={index}>
<h2>{report.assignmentTitle}</h2>
<p>Submissions: {report.submissions}</p>
<p>Average Score: {report.averageScore}</p>
</li>
))}
</ul>
)}
</div>
);
}
3. Attendance Report Page
// pages/reports/attendance.js
import { useQuery, gql } from '@apollo/client';
const GET_ATTENDANCE_REPORT = gql`
query GetAttendanceReport {
attendanceReport {
student
class
date
status
}
}
`;
export default function AttendanceReport() {
const { loading, error, data } = useQuery(GET_ATTENDANCE_REPORT);
if (loading) return <p>Loading...</p>;
if (error) return <p>Error: {error.message}</p>;
return (
<div>
<h1>Attendance Report</h1>
<ul>
{data.attendanceReport.map((report, index) => (
<li key={index}>
<p>Student: {report.student}</p>
<p>Class: {report.class}</p>
<p>Date: {report.date}</p>
<p>Status: {report.status}</p>
</li>
))}
</ul>
</div>
);
}
4. Financial Report Page
// pages/reports/financial.js
import { useQuery, gql } from '@apollo/client';
const GET_FINANCIAL_REPORT = gql`
query GetFinancialReport {
financialReport {
totalFees
totalPayments
outstandingAmount
}
}
`;
export default function FinancialReport() {
const { loading, error, data } = useQuery(GET_FINANCIAL_REPORT);
if (loading) return <p>Loading...</p>;
if (error) return <p>Error: {error.message}</p>;
return (
<div>
<h1>Financial Report</h1>
<p>Total Fees: {data.financialReport.totalFees}</p>
<p>Total Payments: {data.financialReport.totalPayments}</p>
<p>Outstanding Amount: {data.financialReport.outstandingAmount}</p>
</div>
);
}
GraphQL Schema
Define your GraphQL schema to match the resolver functions:
type AcademicPerformanceReport {
assignmentTitle: String!
submissions: Int!
averageScore: Float!
}
type AttendanceReport {
student: String!
class: String!
date: String!
status: String!
}
type FinancialReport {
totalFees: Float!
totalPayments: Float!
outstandingAmount: Float!
}
type Query {
academicPerformanceReport(courseId: Int!): [AcademicPerformanceReport!]!
attendanceReport: [AttendanceReport!]!
financialReport: FinancialReport!
}
This setup covers the backend and frontend code for generating various reports for academic performance, attendance, and finances. You can expand on this by adding more details, such as charts and graphs for better visualization, and additional report types as needed.
To add graphs for better visualization and additional report types, we'll use a charting library such as chart.js
on the frontend. Below, I'll show you how to integrate chart.js
to display the data in a more visual and interactive way.
Backend (NestJS)
The backend setup remains the same as previously defined for generating various reports.
Frontend (Next.js)
1. Install Chart.js and react-chartjs-2
First, install chart.js
and react-chartjs-2
:
npm install chart.js react-chartjs-2
2. Update Academic Performance Report Page
pages/reports/academic-performance.js
import { useQuery, gql } from '@apollo/client';
import { useState } from 'react';
import { Bar } from 'react-chartjs-2';
import 'chart.js/auto';
const GET_ACADEMIC_PERFORMANCE_REPORT = gql`
query GetAcademicPerformanceReport($courseId: Int!) {
academicPerformanceReport(courseId: $courseId) {
assignmentTitle
submissions
averageScore
}
}
`;
export default function AcademicPerformanceReport() {
const [courseId, setCourseId] = useState('');
const { loading, error, data, refetch } = useQuery(GET_ACADEMIC_PERFORMANCE_REPORT, {
variables: { courseId: parseInt(courseId) },
skip: !courseId,
});
const handleSubmit = (e) => {
e.preventDefault();
refetch();
};
const chartData = {
labels: data?.academicPerformanceReport.map((report) => report.assignmentTitle) || [],
datasets: [
{
label: 'Average Score',
data: data?.academicPerformanceReport.map((report) => report.averageScore) || [],
backgroundColor: 'rgba(75, 192, 192, 0.6)',
},
],
};
return (
<div>
<h1>Academic Performance Report</h1>
<form onSubmit={handleSubmit}>
<input
type="number"
placeholder="Course ID"
value={courseId}
onChange={(e) => setCourseId(e.target.value)}
/>
<button type="submit">Generate Report</button>
</form>
{loading && <p>Loading...</p>}
{error && <p>Error: {error.message}</p>}
{data && (
<div>
<Bar data={chartData} />
<ul>
{data.academicPerformanceReport.map((report, index) => (
<li key={index}>
<h2>{report.assignmentTitle}</h2>
<p>Submissions: {report.submissions}</p>
<p>Average Score: {report.averageScore}</p>
</li>
))}
</ul>
</div>
)}
</div>
);
}
3. Update Attendance Report Page
pages/reports/attendance.js
import { useQuery, gql } from '@apollo/client';
import { Bar } from 'react-chartjs-2';
import 'chart.js/auto';
const GET_ATTENDANCE_REPORT = gql`
query GetAttendanceReport {
attendanceReport {
student
class
date
status
}
}
`;
export default function AttendanceReport() {
const { loading, error, data } = useQuery(GET_ATTENDANCE_REPORT);
if (loading) return <p>Loading...</p>;
if (error) return <p>Error: {error.message}</p>;
const presentData = data.attendanceReport.filter((report) => report.status === 'Present');
const absentData = data.attendanceReport.filter((report) => report.status === 'Absent');
const chartData = {
labels: [...new Set(data.attendanceReport.map((report) => report.date))],
datasets: [
{
label: 'Present',
data: presentData.map((report) => report.status === 'Present' ? 1 : 0),
backgroundColor: 'rgba(75, 192, 192, 0.6)',
},
{
label: 'Absent',
data: absentData.map((report) => report.status === 'Absent' ? 1 : 0),
backgroundColor: 'rgba(255, 99, 132, 0.6)',
},
],
};
return (
<div>
<h1>Attendance Report</h1>
<Bar data={chartData} />
<ul>
{data.attendanceReport.map((report, index) => (
<li key={index}>
<p>Student: {report.student}</p>
<p>Class: {report.class}</p>
<p>Date: {report.date}</p>
<p>Status: {report.status}</p>
</li>
))}
</ul>
</div>
);
}
4. Update Financial Report Page
pages/reports/financial.js
import { useQuery, gql } from '@apollo/client';
import { Pie } from 'react-chartjs-2';
import 'chart.js/auto';
const GET_FINANCIAL_REPORT = gql`
query GetFinancialReport {
financialReport {
totalFees
totalPayments
outstandingAmount
}
}
`;
export default function FinancialReport() {
const { loading, error, data } = useQuery(GET_FINANCIAL_REPORT);
if (loading) return <p>Loading...</p>;
if (error) return <p>Error: {error.message}</p>;
const chartData = {
labels: ['Total Fees', 'Total Payments', 'Outstanding Amount'],
datasets: [
{
label: 'Financial Data',
data: [
data.financialReport.totalFees,
data.financialReport.totalPayments,
data.financialReport.outstandingAmount,
],
backgroundColor: ['rgba(75, 192, 192, 0.6)', 'rgba(54, 162, 235, 0.6)', 'rgba(255, 206, 86, 0.6)'],
},
],
};
return (
<div>
<h1>Financial Report</h1>
<Pie data={chartData} />
<p>Total Fees: {data.financialReport.totalFees}</p>
<p>Total Payments: {data.financialReport.totalPayments}</p>
<p>Outstanding Amount: {data.financialReport.outstandingAmount}</p>
</div>
);
}
Additional Report Types
You can add more types of reports, such as:
- Student Performance Report: Aggregates a student's grades across different courses.
- Monthly Attendance Summary: Summarizes attendance for each student across a month.
- Payment History Report: Lists all payments made by students over a period.
Below is an example of how you can add a Student Performance Report:
Backend (NestJS)
1. Service
Report Service:
// report.service.ts
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { Submission } from './submission.entity';
import { User } from './user.entity';
@Injectable()
export class ReportService {
constructor(
@InjectRepository(Submission)
private submissionRepository: Repository<Submission>,
@InjectRepository(User)
private userRepository: Repository<User>,
) {}
async getStudentPerformanceReport(studentId: number) {
const student = await this.userRepository.findOne(studentId, { relations: ['submissions', 'submissions.assignment'] });
const report = student.submissions.map(submission => ({
assignmentTitle: submission.assignment.title,
grade: submission.grade,
}));
return report;
}
}
2. Resolver
Report Resolver:
// report.resolver.ts
import { Resolver, Query, Args } from '@nestjs/graphql';
import { ReportService } from './report.service';
@Resolver()
export class ReportResolver {
constructor(private reportService: ReportService) {}
@Query(() => [Object])
async studentPerformanceReport(@Args('studentId') studentId: number) {
return this.reportService.getStudentPerformanceReport(studentId);
}
}
Frontend (Next.js)
1. Student Performance Report Page
pages/reports/student-performance.js
import { useQuery, gql } from '@apollo/client';
import { useState } from 'react';
import { Line } from 'react-chartjs-2';
import 'chart.js/auto';
const GET_STUDENT_PERFORMANCE_REPORT = gql`
query GetStudentPerformanceReport($studentId: Int!) {
studentPerformanceReport(studentId: $studentId) {
assignmentTitle
grade
}
}
`;
export default function StudentPerformanceReport() {
const [studentId, setStudentId] = useState('');
const { loading, error, data, refetch } = useQuery(GET_STUDENT_PERFORMANCE_REPORT, {
variables: { studentId: parseInt(studentId) },
skip: !studentId,
});
const handleSubmit = (e) => {
e.preventDefault();
refetch();
};
const chartData = {
labels: data?.studentPerformanceReport.map((report) => report.assignmentTitle) || [],
datasets: [
{
label: 'Grades',
data: data?.studentPerformanceReport.map((report) => report.grade) || [],
background
Color: 'rgba(75, 192, 192, 0.6)',
borderColor: 'rgba(75, 192, 192, 1)',
fill: false,
},
],
};
return (
<div>
<h1>Student Performance Report</h1>
<form onSubmit={handleSubmit}>
<input
type="number"
placeholder="Student ID"
value={studentId}
onChange={(e) => setStudentId(e.target.value)}
/>
<button type="submit">Generate Report</button>
</form>
{loading && <p>Loading...</p>}
{error && <p>Error: {error.message}</p>}
{data && (
<div>
<Line data={chartData} />
<ul>
{data.studentPerformanceReport.map((report, index) => (
<li key={index}>
<h2>{report.assignmentTitle}</h2>
<p>Grade: {report.grade}</p>
</li>
))}
</ul>
</div>
)}
</div>
);
}
GraphQL Schema
Update your GraphQL schema to include the new StudentPerformanceReport
type and query:
type AcademicPerformanceReport {
assignmentTitle: String!
submissions: Int!
averageScore: Float!
}
type AttendanceReport {
student: String!
class: String!
date: String!
status: String!
}
type FinancialReport {
totalFees: Float!
totalPayments: Float!
outstandingAmount: Float!
}
type StudentPerformanceReport {
assignmentTitle: String!
grade: Float!
}
type Query {
academicPerformanceReport(courseId: Int!): [AcademicPerformanceReport!]!
attendanceReport: [AttendanceReport!]!
financialReport: FinancialReport!
studentPerformanceReport(studentId: Int!): [StudentPerformanceReport!]!
}
This setup adds graphs for better visualization of reports and includes an additional report type for student performance. You can further expand this by adding more report types 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)