feat: 과목 제한 해제 — 수학 외 자유 과목 추가 가능
- backend: ALLOWED_SUBJECT_NAMES, @IsIn() 삭제, 모든 서비스에서 name:'수학' 필터 제거 - frontend: purgeNonMath 제거 (대시보드 안정화), 과목 생성 가드 삭제 - defaultSubjectColor에 영어/국어/과학/사회/한국사 색상 추가 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -28,7 +28,6 @@ export class DashboardService {
|
||||
scheduledAt: { lte: now },
|
||||
studyLog: {
|
||||
psProblemId: null,
|
||||
subject: { name: '수학' },
|
||||
},
|
||||
},
|
||||
}),
|
||||
@@ -39,7 +38,6 @@ export class DashboardService {
|
||||
scheduledAt: { gt: now, lte: todayEnd },
|
||||
studyLog: {
|
||||
psProblemId: null,
|
||||
subject: { name: '수학' },
|
||||
},
|
||||
},
|
||||
}),
|
||||
@@ -47,7 +45,6 @@ export class DashboardService {
|
||||
where: {
|
||||
userId,
|
||||
psProblemId: null,
|
||||
subject: { name: '수학' },
|
||||
},
|
||||
orderBy: { studiedAt: 'desc' },
|
||||
take: 5,
|
||||
@@ -61,7 +58,6 @@ export class DashboardService {
|
||||
where: {
|
||||
userId,
|
||||
psProblemId: null,
|
||||
subject: { name: '수학' },
|
||||
studiedAt: { gte: weekStart },
|
||||
},
|
||||
_count: { result: true },
|
||||
@@ -70,7 +66,6 @@ export class DashboardService {
|
||||
where: {
|
||||
userId,
|
||||
tagId: { not: null },
|
||||
tag: { subject: { name: '수학' } },
|
||||
},
|
||||
include: {
|
||||
tag: {
|
||||
|
||||
@@ -16,7 +16,6 @@ import {
|
||||
IsArray,
|
||||
IsBoolean,
|
||||
IsEnum,
|
||||
IsIn,
|
||||
IsInt,
|
||||
IsOptional,
|
||||
IsString,
|
||||
@@ -34,7 +33,6 @@ import { AuthUser } from '../auth/jwt.strategy';
|
||||
import { MeService } from './me.service';
|
||||
|
||||
const ALLOWED_AVATAR_MIME_TYPES = ['image/jpeg', 'image/png', 'image/webp'] as const;
|
||||
const ALLOWED_FOCUS_SUBJECTS = ['수학'] as const;
|
||||
const AVATAR_UPLOAD_DIR = join(__dirname, '..', '..', 'uploads', 'avatars');
|
||||
|
||||
function ensureAvatarUploadDir() {
|
||||
@@ -69,8 +67,8 @@ class OnboardingDto {
|
||||
|
||||
@IsOptional()
|
||||
@IsArray()
|
||||
@ArrayMaxSize(1)
|
||||
@IsIn(ALLOWED_FOCUS_SUBJECTS, { each: true })
|
||||
@ArrayMaxSize(5)
|
||||
@IsString({ each: true })
|
||||
focusSubjects?: string[];
|
||||
|
||||
@IsEnum(ReviewIntensity)
|
||||
@@ -121,8 +119,8 @@ class ProfilePatchDto {
|
||||
@IsOptional()
|
||||
@IsArray()
|
||||
@ArrayMinSize(1)
|
||||
@ArrayMaxSize(1)
|
||||
@IsIn(ALLOWED_FOCUS_SUBJECTS, { each: true })
|
||||
@ArrayMaxSize(5)
|
||||
@IsString({ each: true })
|
||||
focusSubjects?: string[];
|
||||
|
||||
@IsOptional()
|
||||
|
||||
@@ -37,7 +37,6 @@ export class ReviewsService {
|
||||
scheduledAt: { lte: soon },
|
||||
studyLog: {
|
||||
psProblemId: null,
|
||||
subject: { name: '수학' },
|
||||
},
|
||||
},
|
||||
include: {
|
||||
@@ -81,7 +80,6 @@ export class ReviewsService {
|
||||
scheduledAt: { lt: utcTodayStartKST },
|
||||
studyLog: {
|
||||
psProblemId: null,
|
||||
subject: { name: '수학' },
|
||||
},
|
||||
},
|
||||
select: { id: true },
|
||||
@@ -302,7 +300,6 @@ export class ReviewsService {
|
||||
status: { in: [ReviewStatus.done, ReviewStatus.skipped] },
|
||||
studyLog: {
|
||||
psProblemId: null,
|
||||
subject: { name: '수학' },
|
||||
},
|
||||
},
|
||||
include: {
|
||||
@@ -332,7 +329,6 @@ export class ReviewsService {
|
||||
scheduledAt: { gte: start, lt: end },
|
||||
studyLog: {
|
||||
psProblemId: null,
|
||||
subject: { name: '수학' },
|
||||
},
|
||||
},
|
||||
select: {
|
||||
@@ -379,7 +375,6 @@ export class ReviewsService {
|
||||
scheduledAt: { gte: start, lt: end },
|
||||
studyLog: {
|
||||
psProblemId: null,
|
||||
subject: { name: '수학' },
|
||||
},
|
||||
},
|
||||
include: {
|
||||
|
||||
@@ -193,9 +193,8 @@ export class StatsService {
|
||||
}
|
||||
|
||||
async bySubject(userId: number) {
|
||||
// 프로토타입: 수학 과목만 노출 (soft filter — DB 데이터는 유지)
|
||||
const subjects = await this.prisma.subject.findMany({
|
||||
where: { userId, name: '수학' },
|
||||
where: { userId },
|
||||
include: {
|
||||
tags: {
|
||||
include: {
|
||||
|
||||
@@ -508,7 +508,6 @@ export class StudyLogsService {
|
||||
where: {
|
||||
userId,
|
||||
psProblemId: null,
|
||||
subject: { name: '수학' },
|
||||
...(opts.subjectId && { subjectId: opts.subjectId }),
|
||||
...(opts.tagId && { tagId: opts.tagId }),
|
||||
},
|
||||
@@ -803,6 +802,11 @@ function clamp01(n: number): number {
|
||||
function defaultSubjectColor(subjectName: string): string {
|
||||
const colorMap: Record<string, string> = {
|
||||
수학: "#3b82f6",
|
||||
영어: "#10b981",
|
||||
국어: "#f59e0b",
|
||||
과학: "#8b5cf6",
|
||||
사회: "#ec4899",
|
||||
한국사: "#ef4444",
|
||||
};
|
||||
|
||||
return colorMap[subjectName] ?? "#6366f1";
|
||||
|
||||
@@ -9,20 +9,16 @@ import {
|
||||
Post,
|
||||
UseGuards,
|
||||
} from '@nestjs/common';
|
||||
import { IsIn, IsOptional, IsString, MaxLength, MinLength } from 'class-validator';
|
||||
import { IsOptional, IsString, MaxLength, MinLength } from 'class-validator';
|
||||
import { JwtAuthGuard } from '../auth/jwt-auth.guard';
|
||||
import { CurrentUser } from '../auth/current-user.decorator';
|
||||
import { AuthUser } from '../auth/jwt.strategy';
|
||||
import { SubjectsService } from './subjects.service';
|
||||
|
||||
// 프로토타입: 수학만 허용 — 나중에 sed 한 방으로 해제 가능
|
||||
const ALLOWED_SUBJECT_NAMES = ['수학'] as const;
|
||||
|
||||
class CreateSubjectDto {
|
||||
@IsString()
|
||||
@MinLength(1)
|
||||
@MaxLength(40)
|
||||
@IsIn(ALLOWED_SUBJECT_NAMES)
|
||||
name: string;
|
||||
|
||||
@IsOptional()
|
||||
@@ -35,7 +31,6 @@ class UpdateSubjectDto {
|
||||
@IsString()
|
||||
@MinLength(1)
|
||||
@MaxLength(40)
|
||||
@IsIn(ALLOWED_SUBJECT_NAMES)
|
||||
name?: string;
|
||||
|
||||
@IsOptional()
|
||||
|
||||
@@ -6,18 +6,16 @@ export class SubjectsService {
|
||||
constructor(private readonly prisma: PrismaService) {}
|
||||
|
||||
list(userId: number) {
|
||||
// 프로토타입: 수학 과목만 노출 (soft filter — DB 데이터는 유지)
|
||||
return this.prisma.subject.findMany({
|
||||
where: { userId, name: '수학' },
|
||||
where: { userId },
|
||||
include: { tags: { orderBy: { name: 'asc' } } },
|
||||
orderBy: { createdAt: 'asc' },
|
||||
});
|
||||
}
|
||||
|
||||
async getOne(userId: number, id: number) {
|
||||
// 프로토타입: 수학 과목만 노출 (soft filter — DB 데이터는 유지)
|
||||
const subject = await this.prisma.subject.findFirst({
|
||||
where: { id, userId, name: '수학' },
|
||||
where: { id, userId },
|
||||
include: { tags: { orderBy: { name: 'asc' } } },
|
||||
});
|
||||
if (!subject) throw new NotFoundException();
|
||||
|
||||
@@ -95,14 +95,6 @@ function DashboardBody() {
|
||||
setCoachDismissed(window.localStorage.getItem(COACH_DISMISS_KEY) === '1');
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
const purged = localStorage.getItem('reloop-purged-non-math');
|
||||
if (!purged) {
|
||||
api.post('/study-logs/purge-non-math').then(() => {
|
||||
localStorage.setItem('reloop-purged-non-math', 'true');
|
||||
}).catch(() => {});
|
||||
}
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
let cancelled = false;
|
||||
|
||||
@@ -182,7 +182,7 @@ function OnboardingBody() {
|
||||
nickname: trimmedNickname,
|
||||
persona,
|
||||
reviewIntensity: DEFAULT_REVIEW_INTENSITY,
|
||||
focusSubjects: goalType === 'none' ? [] : ['수학'],
|
||||
focusSubjects: goalType === 'math-suneung' ? ['수학'] : [],
|
||||
focusUnits: goalType === 'math-suneung' ? focusUnits : [],
|
||||
onboarded: true,
|
||||
};
|
||||
|
||||
@@ -180,12 +180,6 @@ function SubjectsBody() {
|
||||
setSubjectModalError('과목 이름을 입력해 주세요.');
|
||||
return;
|
||||
}
|
||||
// 프로토타입: 수학 단일 과목 잠금 — 추후 sed 한 방으로 해제
|
||||
if (name !== '수학') {
|
||||
setSubjectModalError('현재는 수학 과목만 추가할 수 있어요.');
|
||||
return;
|
||||
}
|
||||
|
||||
setSubjectModalSaving(true);
|
||||
setSubjectModalError(null);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user