feat(study): mock exam flow from KICE problem sets — 7F.2

This commit is contained in:
reloop
2026-04-12 05:45:50 +09:00
parent cbc3af2a6b
commit 31d62fbe33
9 changed files with 2193 additions and 774 deletions

View File

@@ -0,0 +1,61 @@
'use client';
import type { ProblemSetDetail, ProblemSetSubmissionResponse } from '@/lib/api';
export interface StoredExamResult {
submittedAt: string;
problemSet: {
id: number;
title: string;
year: number;
subjectName: string;
};
summary: ProblemSetSubmissionResponse;
}
export function getExamDurationSeconds(subjectName: string): number {
if (subjectName === '국어') return 80 * 60;
if (subjectName === '영어') return 70 * 60;
if (subjectName === '한국사') return 30 * 60;
if (subjectName.includes('윤리') || subjectName.includes('탐구')) return 30 * 60;
return 60 * 60;
}
export function formatClock(totalSeconds: number): string {
const hours = Math.floor(totalSeconds / 3600);
const minutes = Math.floor((totalSeconds % 3600) / 60);
const seconds = totalSeconds % 60;
return [hours, minutes, seconds].map((value) => String(value).padStart(2, '0')).join(':');
}
export function examResultStorageKey(problemSetId: number): string {
return `reloop.exam.result.${problemSetId}`;
}
export function storeExamResult(
problemSet: Pick<ProblemSetDetail, 'id' | 'title' | 'year' | 'subjectName'>,
summary: ProblemSetSubmissionResponse,
) {
if (typeof window === 'undefined') return;
const payload: StoredExamResult = {
submittedAt: new Date().toISOString(),
problemSet,
summary,
};
window.sessionStorage.setItem(examResultStorageKey(problemSet.id), JSON.stringify(payload));
}
export function readExamResult(problemSetId: number): StoredExamResult | null {
if (typeof window === 'undefined') return null;
const raw = window.sessionStorage.getItem(examResultStorageKey(problemSetId));
if (!raw) return null;
try {
return JSON.parse(raw) as StoredExamResult;
} catch {
return null;
}
}

View File

@@ -11,7 +11,7 @@ import {
MagnifyingGlass, DownloadSimple, CalendarBlank, CalendarCheck, BellRinging, Bell, Sparkle,
Minus, DotsThree, Translate, BookOpenText, SkipForward, Info, SignOut, Camera, Crown,
Export, Gear, ArrowRight, type IconProps as PhIconProps, type IconWeight,
Scales, Feather,
Scales, Feather, Clock, Flag, CheckSquare, Circle, ArrowLeft,
} from '@phosphor-icons/react';
// name → component map. 새 아이콘 추가 시 이 맵에만 등록.
@@ -73,8 +73,13 @@ const ICON_MAP = {
'export': Export,
'gear': Gear,
'arrow-right': ArrowRight,
'arrow-left': ArrowLeft,
'scales': Scales,
'feather': Feather,
'clock': Clock,
'flag': Flag,
'check-square': CheckSquare,
'circle': Circle,
} as const;
export type IconName = keyof typeof ICON_MAP;