feat(study): mock exam flow from KICE problem sets — 7F.2
This commit is contained in:
61
frontend/src/components/exam/shared.ts
Normal file
61
frontend/src/components/exam/shared.ts
Normal 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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user