feat: phase1 platform upgrade with moderation, dev payments, admin panel and landing updates

This commit is contained in:
root
2026-02-06 17:26:53 +00:00
parent 4ca66ea896
commit 979adb9d3d
54 changed files with 2687 additions and 318 deletions

View File

@ -1,14 +1,33 @@
import { ApiProperty } from '@nestjs/swagger';
import { IsString, MaxLength, MinLength } from 'class-validator';
import { HomeworkType } from '@coursecraft/database';
import { IsEnum, IsOptional, IsString, MaxLength, MinLength } from 'class-validator';
export class SubmitHomeworkDto {
@ApiProperty({
description: 'Written homework answer',
minLength: 50,
minLength: 1,
maxLength: 20000,
})
@IsOptional()
@IsString()
@MinLength(50)
@MinLength(1)
@MaxLength(20000)
content: string;
content?: string;
@ApiProperty({ enum: HomeworkType, default: HomeworkType.TEXT })
@IsOptional()
@IsEnum(HomeworkType)
type?: HomeworkType;
@ApiProperty({ required: false, description: 'File URL for FILE/PROJECT submission' })
@IsOptional()
@IsString()
@MaxLength(1024)
attachmentUrl?: string;
@ApiProperty({ required: false, description: 'GitHub repository URL' })
@IsOptional()
@IsString()
@MaxLength(1024)
githubUrl?: string;
}

View File

@ -83,7 +83,7 @@ export class EnrollmentController {
@Body() dto: SubmitHomeworkDto,
@CurrentUser() user: User,
): Promise<any> {
return this.enrollmentService.submitHomework(user.id, courseId, lessonId, dto.content);
return this.enrollmentService.submitHomework(user.id, courseId, lessonId, dto);
}
@Post(':courseId/review')

View File

@ -6,7 +6,7 @@ import {
NotFoundException,
} from '@nestjs/common';
import { PrismaService } from '../common/prisma/prisma.service';
import { HomeworkReviewStatus } from '@coursecraft/database';
import { HomeworkReviewStatus, HomeworkType } from '@coursecraft/database';
const QUIZ_PASS_THRESHOLD = 70;
@ -172,7 +172,12 @@ export class EnrollmentService {
return { homework, submission };
}
async submitHomework(userId: string, courseId: string, lessonId: string, content: string): Promise<any> {
async submitHomework(
userId: string,
courseId: string,
lessonId: string,
dto: { content?: string; type?: HomeworkType; attachmentUrl?: string; githubUrl?: string }
): Promise<any> {
const enrollment = await this.requireEnrollment(userId, courseId);
await this.assertLessonUnlocked(userId, courseId, lessonId);
@ -184,20 +189,36 @@ export class EnrollmentService {
}
const { homework } = await this.getHomework(userId, courseId, lessonId);
const aiResult = this.gradeHomeworkWithAI(content);
const submissionType = dto.type || homework.type || HomeworkType.TEXT;
const normalizedContent = (dto.content || '').trim();
if (!normalizedContent && !dto.attachmentUrl && !dto.githubUrl) {
throw new BadRequestException('Provide content, attachment URL, or GitHub URL');
}
const fallbackContent =
normalizedContent ||
dto.githubUrl ||
dto.attachmentUrl ||
`Submission type: ${submissionType}`;
const aiResult = this.gradeHomeworkWithAI(fallbackContent);
const submission = await this.prisma.homeworkSubmission.upsert({
where: { homeworkId_userId: { homeworkId: homework.id, userId } },
create: {
homeworkId: homework.id,
userId,
content,
content: fallbackContent,
answerType: submissionType,
attachmentUrl: dto.attachmentUrl || null,
githubUrl: dto.githubUrl || null,
aiScore: aiResult.score,
aiFeedback: aiResult.feedback,
reviewStatus: HomeworkReviewStatus.AI_REVIEWED,
},
update: {
content,
content: fallbackContent,
answerType: submissionType,
attachmentUrl: dto.attachmentUrl || null,
githubUrl: dto.githubUrl || null,
aiScore: aiResult.score,
aiFeedback: aiResult.feedback,
reviewStatus: HomeworkReviewStatus.AI_REVIEWED,