Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 8 additions & 11 deletions web/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,10 @@
"axios": "^1.13.6",
"cropperjs": "^2.1.0",
"date-fns": "^4.1.0",
"dompurify": "^3.3.2",
"dompurify": "^3.3.3",
"fflate": "^0.8.2",
"firebase": "^12.10.0",
"i18next": "^25.8.17",
"i18next": "^25.8.18",
"i18next-browser-languagedetector": "^8.2.1",
"korean-regexp": "^1.0.13",
"marked": "^17.0.4",
Expand Down
15 changes: 11 additions & 4 deletions web/src/locale/en/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,12 @@
"{{count}} reply_other": "{{count}} replies",
"{{count}} result found_one": "{{count}} result found",
"{{count}} result found_other": "{{count}} results found",
"{{count}} watched history found_one": "{{count}} watched history found",
"{{count}} watched history found_other": "{{count}} watched histories found",
"{{count}} watched public media found_one": "{{count}} watched public media found_one",
"{{count}} watched public media found_other": "{{count}} watched public media found_other",
"{{minutes}} minutes": "{{minutes}} minutes",
"{{num}} remains": "{{num}} remains",
"{{platform}} Site Policies": "{{platform}} Site Policies",
"{{realm}} preview": "{{realm}} preview",
"1:1 Inquiry": "1:1 Inquiry",
"Account activation": "Account activation",
"Accuracy Rate": "Accuracy Rate",
Expand Down Expand Up @@ -245,6 +246,7 @@
"Get Started {{sessionKind}}": "Get Started {{sessionKind}}",
"Getting started": "Getting started",
"Getting Started": "Getting Started",
"Go Back": "Go Back",
"goal": "goal",
"Google Authenticator": "Google Authenticator",
"Grade Confirm Due": "Grade Confirm Due",
Expand Down Expand Up @@ -371,7 +373,7 @@
"No related courses.": "No related courses.",
"No similar answer found": "No similar answer found",
"No unread notification": "No unread notification",
"No watched media yet.": "No watched media yet.",
"No watched public media yet.": "No watched public media yet.",
"Not provided": "Not provided",
"Not reviewed / All appeals": "Not reviewed / All appeals",
"Notification": "Notification",
Expand All @@ -390,6 +392,7 @@
"Otp Setup Completed Successfully": "Otp Setup Completed Successfully",
"OTP Verification Required": "OTP Verification Required",
"Outline": "Outline",
"Page Not Found": "Page Not Found",
"Paragraph": "Paragraph",
"Pass": "Pass",
"Passed": "Passed",
Expand All @@ -411,6 +414,7 @@
"Plagiarism threshold": "Plagiarism threshold",
"Plagiarism threshold is {{threshold}}%. Over this threshold, the submission will be rejected.": "Plagiarism threshold is {{threshold}}%. Over this threshold, the submission will be rejected.",
"Please agree to the following policies": "Please agree to the following policies",
"Please check the address and try again.": "Please check the address and try again.",
"Please fill out the form below to join.": "Please fill out the form below to join.",
"Please review carefully before submitting, as grade appeals become final once submitted.": "Please review carefully before submitting, as grade appeals become final once submitted.",
"Please select a rating.": "Please select a rating.",
Expand All @@ -423,7 +427,6 @@
"Post Point: {{count}}_one": "Post Point: {{count}}",
"Post Point: {{count}}_other": "Post Points: {{count}}",
"Preview": "Preview",
"Preview mode": "Preview mode",
"Preview URL": "Preview URL",
"Privacy Policy": "Privacy Policy",
"Private": "Private",
Expand Down Expand Up @@ -467,6 +470,7 @@
"Request email change": "Request email change",
"Request password change": "Request password change",
"required": "required",
"Required": "Required",
"required *": "required *",
"Required *": "Required *",
"Researcher": "Researcher",
Expand Down Expand Up @@ -513,6 +517,7 @@
"Skill factors: {{num}}": "Skill factors: {{num}}",
"Skills": "Skills",
"Solved": "Solved",
"Something went wrong": "Something went wrong",
"SSO Error": "SSO Error",
"Staffs": "Staffs",
"Standard Score": "Standard Score",
Expand Down Expand Up @@ -550,6 +555,7 @@
"The deadline for participating in the discussion is {{date}}.": "The deadline for participating in the discussion is {{date}}.",
"The deadline for submitting the assignment is {{date}}.": "The deadline for submitting the assignment is {{date}}.",
"The final grade confirmation due is {{date}}.": "The final grade confirmation due is {{date}}.",
"The page you are looking for does not exist or you do not have permission to access it.": "The page you are looking for does not exist or you do not have permission to access it.",
"Third Level": "Third Level",
"This action cannot be undone. Are you sure you want to proceed?": "This action cannot be undone. Are you sure you want to proceed?",
"This course has no certificates.": "This course has no certificates.",
Expand All @@ -576,6 +582,7 @@
"Type to search": "Type to search",
"Unable to display timer": "Unable to display timer",
"Unenroll": "Unenroll",
"Unknown Address": "Unknown Address",
"Unlimited": "Unlimited",
"Unpublished": "Unpublished",
"Update": "Update",
Expand Down
15 changes: 11 additions & 4 deletions web/src/locale/ko/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,12 @@
"{{count}} post_other": "{{count}} 게시글",
"{{count}} Question_other": "{{count}} 문제",
"{{count}} reply_other": "{{count}} 답글",
"{{count}} result found_other": "{{count}}개의 결과를 찾았습니다.",
"{{count}} watched history found_other": "{{count}} 시청 기록",
"{{count}} result found_other": "{{count}} 결과를 찾았습니다.",
"{{count}} watched public media found_other": "{{count}}개 시청된 공개 미디어를 찾았습니다.",
"{{minutes}} minutes": "{{minutes}}분",
"{{num}} remains": "{{num}} 남음",
"{{platform}} Site Policies": "{{platform}} 사이트 정책",
"{{realm}} preview": "{{realm}} 미리보기",
"1:1 Inquiry": "1:1 문의",
"Account activation": "계정 활성화",
"Accuracy Rate": "정답률",
Expand Down Expand Up @@ -234,6 +235,7 @@
"Get Started {{sessionKind}}": "{{sessionKind}} 시작하기",
"Getting started": "시작하기",
"Getting Started": "시작하기",
"Go Back": "돌아가기",
"goal": "목표",
"Google Authenticator": "Google 인증",
"Grade Confirm Due": "성적 확인 마감",
Expand Down Expand Up @@ -358,7 +360,7 @@
"No related courses.": "연관된 과정이 없습니다.",
"No similar answer found": "유사한 답안이 없습니다.",
"No unread notification": "읽지 않은 알림이 없습니다",
"No watched media yet.": "시청한 미디어가 없습니다.",
"No watched public media yet.": "아직 시청된 공개 미디어가 없습니다.",
"Not provided": "제공되지 않음",
"Not reviewed / All appeals": "검토 안 됨 / 모든 이의신청",
"Notification": "알림",
Expand All @@ -377,6 +379,7 @@
"Otp Setup Completed Successfully": "OTP 설정 완료",
"OTP Verification Required": "OTP 인증 필요",
"Outline": "개요",
"Page Not Found": "페이지를 찾을 수 없습니다",
"Paragraph": "단락",
"Pass": "합격",
"Passed": "합격",
Expand All @@ -398,6 +401,7 @@
"Plagiarism threshold": "표절 기준",
"Plagiarism threshold is {{threshold}}%. Over this threshold, the submission will be rejected.": "표절 검사 기준은 {{threshold}}%입니다. 이 수치보다 높으면 제출할 수 없습니다.",
"Please agree to the following policies": "다음 정책에 동의해주세요",
"Please check the address and try again.": "주소를 확인하고 다시 시도해주세요.",
"Please fill out the form below to join.": "가입하시려면 아래 양식을 작성해주세요.",
"Please review carefully before submitting, as grade appeals become final once submitted.": "제출 전 내용을 확인하세요. 제출 후 성적 이의신청은 최종 확정됩니다.",
"Please select a rating.": "평점을 선택하세요.",
Expand All @@ -409,7 +413,6 @@
"Post point": "게시글 점수",
"Post Point: {{count}}_other": "게시글 점수: {{count}}",
"Preview": "미리보기",
"Preview mode": "미리보기 모드",
"Preview URL": "미리보기 URL",
"Privacy Policy": "개인정보 정책",
"Private": "비공개",
Expand Down Expand Up @@ -452,6 +455,7 @@
"Request email change": "이메일 변경 요청",
"Request password change": "비밀번호 변경 요청",
"required": "필수",
"Required": "필수",
"required *": "필수 *",
"Required *": "필수 *",
"Researcher": "조사자",
Expand Down Expand Up @@ -498,6 +502,7 @@
"Skill factors: {{num}}": "스킬 요소: {{num}}개",
"Skills": "스킬",
"Solved": "해결됨",
"Something went wrong": "문제가 발생했습니다",
"SSO Error": "SSO 오류",
"Staffs": "스태프",
"Standard Score": "기준 점수",
Expand Down Expand Up @@ -535,6 +540,7 @@
"The deadline for participating in the discussion is {{date}}.": "토론 참여 마감일: {{date}}",
"The deadline for submitting the assignment is {{date}}.": "과제 제출 마감일: {{date}}",
"The final grade confirmation due is {{date}}.": "최종 성적 확인 마감일: {{date}}",
"The page you are looking for does not exist or you do not have permission to access it.": "찾고 있는 페이지가 없거나 접근할 수 있는 권한이 없습니다.",
"Third Level": "3단계",
"This action cannot be undone. Are you sure you want to proceed?": "이 작업은 취소할 수 없습니다. 진행하시겠습니까?",
"This course has no certificates.": "이 과정은 수료증이 없습니다.",
Expand All @@ -560,6 +566,7 @@
"Type to search": "검색어 입력",
"Unable to display timer": "타이머를 표시할 수 없습니다",
"Unenroll": "등록 취소",
"Unknown Address": "알 수 없는 주소",
"Unlimited": "무제한",
"Unpublished": "미게시",
"Update": "변경하기",
Expand Down
29 changes: 24 additions & 5 deletions web/src/routes/studio/-media/SubtitleSet.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,15 @@
import { IconPlus } from '@tabler/icons-solidjs'
import { batch, createSignal, For, Show } from 'solid-js'
import { unwrap } from 'solid-js/store'
import { type MediaSpec, studioV1CreateMediaQuiz, studioV1DeleteMediaSubtitle, studioV1SaveMediaSubtitle } from '@/api'
import {
type MediaSpec,
studioV1ContentSuggestions,
studioV1CreateMediaQuiz,
studioV1DeleteMediaSubtitle,
studioV1SaveMediaSubtitle,
} from '@/api'
import { LANGUAGES } from '@/config'
import { createCachedStore } from '@/shared/solid/cached-store'
import { useTranslation } from '@/shared/solid/i18n'
import { type State, useEditing } from '../-context/editing'
import { DataAction } from '../-studio/DataAction'
Expand Down Expand Up @@ -83,16 +91,27 @@ const Subtitle = (props: { index: number }) => {
const subtitleDirty = () => state()?.dirty ?? false

const [inCreating, setInCreating] = createSignal(false)

// quiz suggestions cache
const [quizSuggestions, { setStore: setQuizSuggestions }] = createCachedStore(
'studioV1ContentSuggestions',
() => ({ query: { kind: 'quiz' as const } }),
async (options) => (await studioV1ContentSuggestions(options)).data,
)

const createQuiz = async () => {
setInCreating(true)
const lang = staging.subtitles[props.index]!.lang
try {
const { data, error } = await studioV1CreateMediaQuiz({ path: { id: staging.id, lang }, throwOnError: false })
if (!error) {
const { data: id, error } = await studioV1CreateMediaQuiz({ path: { id: staging.id, lang }, throwOnError: false })
if (!error && id) {
// add entry to quiz suggestions
const label = `${staging.title} - ${LANGUAGES.find((l) => l.value === lang)?.label ?? lang}`
setQuizSuggestions('data', quizSuggestions.data!.length, { id, label })
// error will be handled in global error handler
batch(() => {
source.quizzes.push(data!)
staging.quizzes.push(data!)
source.quizzes.push(id)
staging.quizzes.push(id)
})
}
} finally {
Expand Down
Loading