From 2a3e2ead65ce05b42fd25ab602a1df89ebc739ee Mon Sep 17 00:00:00 2001 From: chyoo Date: Wed, 12 Aug 2026 14:46:19 +0900 Subject: [PATCH] add commits.. --- src/App.tsx | 60 +++++++++++++++++++++++++++++++++++++++++++++- src/lib/courses.ts | 17 +++++++++++++ 2 files changed, 76 insertions(+), 1 deletion(-) diff --git a/src/App.tsx b/src/App.tsx index c070612..f712111 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -3,7 +3,7 @@ import { Navigate, useLocation, useNavigate } from 'react-router-dom'; import { ToastProvider } from './components/ToastProvider'; import { InstructorLayout } from './layouts/InstructorLayout'; import { clearStoredSession, loadStoredSession, logoutInstructor, refreshCurrentUser, refreshInstructorSession } from './lib/auth'; -import { listInstructorCourses, type InstructorCourse } from './lib/courses'; +import { createInitialInstructorCourse, listInstructorCourses, type InstructorCourse } from './lib/courses'; import { AnalyticsPage } from './pages/analytics/AnalyticsPage'; import { CurriculumPage } from './pages/curriculum/CurriculumPage'; import { DashboardPage } from './pages/dashboard/DashboardPage'; @@ -22,6 +22,10 @@ function App() { const [courses, setCourses] = useState([]); const [selectedCourseId, setSelectedCourseId] = useState(() => window.localStorage.getItem('lms-admin-selected-course-id') ?? ''); const [sessionExpired, setSessionExpired] = useState(false); + const [initialCourseOpen, setInitialCourseOpen] = useState(false); + const [initialCourseTitle, setInitialCourseTitle] = useState(''); + const [initialCourseError, setInitialCourseError] = useState(null); + const [creatingInitialCourse, setCreatingInitialCourse] = useState(false); const [chatThreadOpenRequest, setChatThreadOpenRequest] = useState<{ threadId: string; requestId: number } | null>(null); const location = useLocation(); const navigate = useNavigate(); @@ -96,6 +100,7 @@ function App() { .then((payload) => { if (cancelled) return; setCourses(payload.courses); + setInitialCourseOpen(payload.courses.length === 0); setSelectedCourseId((current) => { const next = payload.courses.some((course) => course.id === current) ? current @@ -117,6 +122,28 @@ function App() { if (courseId) window.localStorage.setItem('lms-admin-selected-course-id', courseId); }; + const handleCreateInitialCourse = async () => { + const title = initialCourseTitle.trim(); + if (!title) { + setInitialCourseError('강좌명을 입력해 주세요.'); + return; + } + + setCreatingInitialCourse(true); + setInitialCourseError(null); + try { + const { course } = await createInitialInstructorCourse(session!.accessToken, title); + setCourses([course]); + setSelectedCourseId(course.id); + window.localStorage.setItem('lms-admin-selected-course-id', course.id); + setInitialCourseOpen(false); + } catch (error) { + setInitialCourseError(error instanceof Error ? error.message : '강좌를 생성하지 못했습니다.'); + } finally { + setCreatingInitialCourse(false); + } + }; + useEffect(() => { if (!session?.accessToken || !session.refreshToken) return; @@ -203,6 +230,37 @@ function App() { /> + {initialCourseOpen && ( +
+
{ + event.preventDefault(); + void handleCreateInitialCourse(); + }} + > +

강좌명을 입력하세요

+

NAV2 기반 자율주행 교육 과정 구성을 새 강좌로 복제합니다.

+ setInitialCourseTitle(event.target.value)} + className="mt-5 h-10 w-full rounded-md border border-[#cbd5e1] px-3 text-[14px] font-semibold text-[#111827] outline-none focus:border-[#2563eb]" + placeholder="예: 2026 하반기 자율주행 교육" + disabled={creatingInitialCourse} + /> + {initialCourseError &&

{initialCourseError}

} + +
+
+ )} ); } diff --git a/src/lib/courses.ts b/src/lib/courses.ts index 4642d42..2740847 100644 --- a/src/lib/courses.ts +++ b/src/lib/courses.ts @@ -24,3 +24,20 @@ export async function listInstructorCourses(accessToken: string): Promise<{ cour if (!response.ok) throw new Error(`강좌 목록을 불러오지 못했습니다. (${response.status})`); return response.json() as Promise<{ courses: InstructorCourse[] }>; } + +export async function createInitialInstructorCourse(accessToken: string, title: string): Promise<{ course: InstructorCourse }> { + const response = await fetch(`${LMS_API_BASE_URL}/instructor/courses/initial`, { + method: 'POST', + headers: { + Authorization: `Bearer ${accessToken}`, + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ title }), + }); + + if (!response.ok) { + const payload = await response.json().catch(() => null) as { message?: string } | null; + throw new Error(payload?.message || `강좌를 생성하지 못했습니다. (${response.status})`); + } + return response.json() as Promise<{ course: InstructorCourse }>; +}