export type InstructorSubmissionCommitFile = { path: string; status: string; patch: string; originalRef?: string | null; modifiedRef?: string | null; }; export type InstructorSubmissionCommit = { hash: string; shortHash: string; message: string; author: string; authorEmail: string; date: string | null; files: InstructorSubmissionCommitFile[]; }; export type InstructorSubmissionItem = { id: string; status: 'NOT_INSTALLED' | 'PENDING' | 'SUBMITTED'; submitted: boolean; submittedAt: string | null; commitCount: number; score: number; course: { id: string; title: string; slug: string; }; week: { id: string; week: number; title: string; isTeamProject: boolean; }; module: { id: string; name: string; folderName: string; sortOrder: number; sourceType?: 'CUSTOM' | 'GIT'; }; student: { id: string; name: string; email: string; group: string; }; team?: { id: string; name: string; members: Array<{ id: string; name: string; email: string; commitCount: number; lastCommittedAt: string | null; }>; } | null; repository: { id: string; owner: string; name: string; url: string; } | null; commits: InstructorSubmissionCommit[]; finalFiles: InstructorSubmissionCommitFile[]; }; export type InstructorSubmissionsResponse = { organization: { id?: string; name: string; } | null; summary: { totalItems: number; submittedItems: number; pendingItems: number; notInstalledItems: number; totalCommits: number; }; courses: Array<{ id: string; title: string; slug: string; weeks: Array<{ id: string; week: number; title: string; isTeamProject: boolean; modules: Array<{ id: string; name: string; folderName: string; sortOrder: number; }>; }>; }>; students: Array<{ id: string; name: string; email: string; group: string; }>; items: InstructorSubmissionItem[]; teamItems: InstructorSubmissionItem[]; }; const LMS_API_BASE_URL = (import.meta.env.VITE_API_BASE_URL ?? 'https://workspace.autonomystudio.net/lms-api/api').replace(/\/+$/, ''); export async function listInstructorSubmissions(accessToken: string): Promise { const response = await fetch(`${LMS_API_BASE_URL}/instructor/submissions`, { headers: { Authorization: `Bearer ${accessToken}`, }, }); if (!response.ok) { throw new Error(`제출 현황을 불러오지 못했습니다. (${response.status})`); } return response.json() as Promise; } export async function getInstructorSubmissionFile( accessToken: string, input: { owner: string; repoName: string; path: string; baseRef?: string | null; headRef?: string | null; }, ): Promise<{ original: string; modified: string; originalAvailable: boolean; modifiedAvailable: boolean; }> { const query = new URLSearchParams({ owner: input.owner, repo: input.repoName, path: input.path, }); if (input.baseRef) query.set('baseRef', input.baseRef); if (input.headRef) query.set('headRef', input.headRef); const response = await fetch(`${LMS_API_BASE_URL}/instructor/submissions/file?${query.toString()}`, { headers: { Authorization: `Bearer ${accessToken}`, }, }); if (!response.ok) { throw new Error(`파일 내용을 불러오지 못했습니다. (${response.status})`); } return response.json() as Promise<{ original: string; modified: string; originalAvailable: boolean; modifiedAvailable: boolean; }>; } export async function saveInstructorSubmissionScore( accessToken: string, input: { studentId: string; weekId: string; score: number }, ): Promise<{ score: number }> { const response = await fetch(`${LMS_API_BASE_URL}/instructor/submissions/score`, { method: 'PUT', headers: { Authorization: `Bearer ${accessToken}`, 'Content-Type': 'application/json', }, body: JSON.stringify(input), }); if (!response.ok) { throw new Error(`점수를 저장하지 못했습니다. (${response.status})`); } return response.json() as Promise<{ score: number }>; }