276 lines
7.8 KiB
TypeScript
276 lines
7.8 KiB
TypeScript
export type CurriculumWeek = {
|
|
week: number;
|
|
title: string;
|
|
tags: string[];
|
|
progress: number;
|
|
state: string;
|
|
topics: string[];
|
|
duration: string;
|
|
github: string;
|
|
detail: {
|
|
overview: string;
|
|
learningSteps?: Array<{
|
|
title: string;
|
|
description: string;
|
|
videoEnabled?: boolean;
|
|
videoUrl?: string;
|
|
concepts?: string[];
|
|
}>;
|
|
topicDetails: string[];
|
|
topicVideoUrls?: string[];
|
|
practiceContents?: Array<{
|
|
title: string;
|
|
description: string;
|
|
}>;
|
|
references?: CurriculumReference[];
|
|
exampleCodes?: CurriculumReference[];
|
|
deliverable: string;
|
|
requiresSourceSubmission: boolean;
|
|
};
|
|
modules?: CurriculumModule[];
|
|
visualizationPlatforms?: CurriculumVisualizationPlatform[];
|
|
};
|
|
|
|
export type CurriculumModule = {
|
|
id: string;
|
|
sortOrder: number;
|
|
category: string;
|
|
name: string;
|
|
folderName: string;
|
|
templateRepositoryUrl?: string | null;
|
|
dockerImage?: string | null;
|
|
enabled?: boolean;
|
|
};
|
|
|
|
export type CurriculumReference = {
|
|
id: string;
|
|
kind?: string;
|
|
title: string;
|
|
description?: string | null;
|
|
fileName: string;
|
|
contentType?: string | null;
|
|
fileSizeBytes?: number | string | null;
|
|
week?: number | string | null;
|
|
weekNumber?: number | string | null;
|
|
downloadUrl: string;
|
|
createdAt?: string;
|
|
};
|
|
|
|
export type CurriculumVisualizationPlatform = {
|
|
key: string;
|
|
type: string;
|
|
displayName: string;
|
|
description?: string | null;
|
|
config?: unknown;
|
|
};
|
|
|
|
export type CurriculumTagOption = {
|
|
key: string;
|
|
label: string;
|
|
};
|
|
|
|
export type InstructorCurriculumResponse = {
|
|
course: {
|
|
id: string;
|
|
title: string;
|
|
subtitle?: string | null;
|
|
description?: string | null;
|
|
organizationName: string;
|
|
} | null;
|
|
weeks: CurriculumWeek[];
|
|
setting: {
|
|
groupId?: string | null;
|
|
activeWeek: number;
|
|
activeLearningStep?: string | null;
|
|
} | null;
|
|
tagOptions?: CurriculumTagOption[];
|
|
visualizationPlatforms?: CurriculumVisualizationPlatform[];
|
|
};
|
|
|
|
const LMS_API_BASE_URL = (import.meta.env.VITE_API_BASE_URL ?? 'http://localhost:4400/api').replace(/\/+$/, '');
|
|
|
|
export async function getInstructorCurriculum(accessToken: string, groupId?: string | null, courseId?: string | null): Promise<InstructorCurriculumResponse> {
|
|
const params = new URLSearchParams();
|
|
if (groupId) params.set('groupId', groupId);
|
|
if (courseId) params.set('courseId', courseId);
|
|
const query = params.toString() ? `?${params.toString()}` : '';
|
|
const response = await fetch(`${LMS_API_BASE_URL}/instructor/curriculum${query}`, {
|
|
headers: {
|
|
Authorization: `Bearer ${accessToken}`,
|
|
},
|
|
});
|
|
|
|
if (!response.ok) throw new Error(`커리큘럼을 불러오지 못했습니다. (${response.status})`);
|
|
return response.json() as Promise<InstructorCurriculumResponse>;
|
|
}
|
|
|
|
export async function updateInstructorCurriculum(
|
|
accessToken: string,
|
|
input: {
|
|
courseId?: string | null;
|
|
groupId?: string | null;
|
|
activeWeek: number;
|
|
activeLearningStep?: string | null;
|
|
applyToStudents?: boolean;
|
|
completed?: boolean;
|
|
tagLabel?: string;
|
|
title?: string;
|
|
duration?: string;
|
|
overview?: string;
|
|
deliverable?: string;
|
|
requiresSourceSubmission?: boolean;
|
|
topics?: string[];
|
|
learningSteps?: Array<{
|
|
title?: string;
|
|
description?: string;
|
|
videoEnabled?: boolean;
|
|
videoUrl?: string;
|
|
concepts?: string[];
|
|
}>;
|
|
topicDetails?: string[];
|
|
topicVideoUrls?: string[];
|
|
practiceContents?: Array<{
|
|
title?: string;
|
|
description?: string;
|
|
}>;
|
|
disabledModuleIds?: string[];
|
|
visualizationPlatformKeys?: string[];
|
|
},
|
|
): Promise<InstructorCurriculumResponse> {
|
|
const response = await fetch(`${LMS_API_BASE_URL}/instructor/curriculum`, {
|
|
method: 'PUT',
|
|
headers: {
|
|
Authorization: `Bearer ${accessToken}`,
|
|
'content-type': 'application/json',
|
|
},
|
|
body: JSON.stringify(input),
|
|
});
|
|
|
|
if (!response.ok) {
|
|
const payload = await response.json().catch((): null => null);
|
|
const message =
|
|
payload && typeof payload === 'object' && 'message' in payload
|
|
? String(payload.message)
|
|
: `커리큘럼을 적용하지 못했습니다. (${response.status})`;
|
|
throw new Error(message);
|
|
}
|
|
|
|
return response.json() as Promise<InstructorCurriculumResponse>;
|
|
}
|
|
|
|
export async function deleteInstructorCurriculumWeek(
|
|
accessToken: string,
|
|
week: number,
|
|
): Promise<InstructorCurriculumResponse> {
|
|
const response = await fetch(`${LMS_API_BASE_URL}/instructor/curriculum/weeks/${week}`, {
|
|
method: 'DELETE',
|
|
headers: {
|
|
Authorization: `Bearer ${accessToken}`,
|
|
},
|
|
});
|
|
|
|
if (!response.ok) {
|
|
const payload = await response.json().catch((): null => null);
|
|
const message =
|
|
payload && typeof payload === 'object' && 'message' in payload
|
|
? String(payload.message)
|
|
: `주차를 삭제하지 못했습니다. (${response.status})`;
|
|
throw new Error(message);
|
|
}
|
|
|
|
return response.json() as Promise<InstructorCurriculumResponse>;
|
|
}
|
|
|
|
export async function uploadCurriculumReference(
|
|
accessToken: string,
|
|
input: {
|
|
courseId?: string | null;
|
|
week: number;
|
|
title?: string;
|
|
file: File;
|
|
},
|
|
): Promise<InstructorCurriculumResponse> {
|
|
const formData = new FormData();
|
|
formData.set('file', input.file);
|
|
formData.set('courseId', input.courseId ?? '');
|
|
formData.set('title', input.title?.trim() || input.file.name);
|
|
|
|
const response = await fetch(`${LMS_API_BASE_URL}/instructor/curriculum/weeks/${input.week}/references`, {
|
|
method: 'POST',
|
|
headers: {
|
|
Authorization: `Bearer ${accessToken}`,
|
|
},
|
|
body: formData,
|
|
});
|
|
|
|
if (!response.ok) {
|
|
const payload = await response.json().catch((): null => null);
|
|
const message =
|
|
payload && typeof payload === 'object' && 'message' in payload
|
|
? String(payload.message)
|
|
: `참고자료를 업로드하지 못했습니다. (${response.status})`;
|
|
throw new Error(message);
|
|
}
|
|
|
|
return response.json() as Promise<InstructorCurriculumResponse>;
|
|
}
|
|
|
|
export async function uploadCurriculumExampleCode(
|
|
accessToken: string,
|
|
input: {
|
|
courseId?: string | null;
|
|
week: number;
|
|
title?: string;
|
|
file: File;
|
|
},
|
|
): Promise<InstructorCurriculumResponse> {
|
|
const formData = new FormData();
|
|
formData.set('file', input.file);
|
|
formData.set('courseId', input.courseId ?? '');
|
|
formData.set('title', input.title?.trim() || input.file.name);
|
|
|
|
const response = await fetch(`${LMS_API_BASE_URL}/instructor/curriculum/weeks/${input.week}/example-codes`, {
|
|
method: 'POST',
|
|
headers: {
|
|
Authorization: `Bearer ${accessToken}`,
|
|
},
|
|
body: formData,
|
|
});
|
|
|
|
if (!response.ok) {
|
|
const payload = await response.json().catch((): null => null);
|
|
const message =
|
|
payload && typeof payload === 'object' && 'message' in payload
|
|
? String(payload.message)
|
|
: `예제 코드를 업로드하지 못했습니다. (${response.status})`;
|
|
throw new Error(message);
|
|
}
|
|
|
|
return response.json() as Promise<InstructorCurriculumResponse>;
|
|
}
|
|
|
|
export async function deleteCurriculumReference(
|
|
accessToken: string,
|
|
referenceId: string,
|
|
courseId?: string | null,
|
|
): Promise<InstructorCurriculumResponse> {
|
|
const query = courseId ? `?courseId=${encodeURIComponent(courseId)}` : '';
|
|
const response = await fetch(`${LMS_API_BASE_URL}/instructor/curriculum/references/${referenceId}${query}`, {
|
|
method: 'DELETE',
|
|
headers: {
|
|
Authorization: `Bearer ${accessToken}`,
|
|
},
|
|
});
|
|
|
|
if (!response.ok) {
|
|
const payload = await response.json().catch((): null => null);
|
|
const message =
|
|
payload && typeof payload === 'object' && 'message' in payload
|
|
? String(payload.message)
|
|
: `참고자료를 삭제하지 못했습니다. (${response.status})`;
|
|
throw new Error(message);
|
|
}
|
|
|
|
return response.json() as Promise<InstructorCurriculumResponse>;
|
|
}
|