analytica-frontend-lib 1.1.8 → 1.1.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -18,8 +18,10 @@ var QUESTION_TYPE = /* @__PURE__ */ ((QUESTION_TYPE2) => {
18
18
  return QUESTION_TYPE2;
19
19
  })(QUESTION_TYPE || {});
20
20
  var QUESTION_STATUS = /* @__PURE__ */ ((QUESTION_STATUS2) => {
21
- QUESTION_STATUS2["APROVADO"] = "APROVADO";
22
- QUESTION_STATUS2["REPROVADO"] = "REPROVADO";
21
+ QUESTION_STATUS2["PENDENTE_AVALIACAO"] = "PENDENTE_AVALIACAO";
22
+ QUESTION_STATUS2["RESPOSTA_CORRETA"] = "RESPOSTA_CORRETA";
23
+ QUESTION_STATUS2["RESPOSTA_INCORRETA"] = "RESPOSTA_INCORRETA";
24
+ QUESTION_STATUS2["NAO_RESPONDIDO"] = "NAO_RESPONDIDO";
23
25
  return QUESTION_STATUS2;
24
26
  })(QUESTION_STATUS || {});
25
27
  var ANSWER_STATUS = /* @__PURE__ */ ((ANSWER_STATUS2) => {
@@ -60,6 +62,8 @@ var useQuizStore = create()(
60
62
  isFinished: false,
61
63
  userId: "",
62
64
  variant: "default",
65
+ questionsResult: null,
66
+ currentQuestionResult: null,
63
67
  // Setters
64
68
  setBySimulated: (simulado) => set({ bySimulated: simulado }),
65
69
  setByActivity: (atividade) => set({ byActivity: atividade }),
@@ -68,6 +72,7 @@ var useQuizStore = create()(
68
72
  setUserAnswers: (userAnswers) => set({ userAnswers }),
69
73
  getUserId: () => get().userId,
70
74
  setVariant: (variant) => set({ variant }),
75
+ setQuestionResult: (questionsResult) => set({ questionsResult }),
71
76
  // Navigation
72
77
  goToNextQuestion: () => {
73
78
  const { currentQuestionIndex, getTotalQuestions } = get();
@@ -120,9 +125,9 @@ var useQuizStore = create()(
120
125
  questionId,
121
126
  activityId,
122
127
  userId,
123
- answer: question.type === "DISSERTATIVA" /* DISSERTATIVA */ ? answerId : null,
124
- optionId: question.type === "DISSERTATIVA" /* DISSERTATIVA */ ? null : answerId,
125
- questionType: question.type,
128
+ answer: question.questionType === "DISSERTATIVA" /* DISSERTATIVA */ ? answerId : null,
129
+ optionId: question.questionType === "DISSERTATIVA" /* DISSERTATIVA */ ? null : answerId,
130
+ questionType: question.questionType,
126
131
  answerStatus: "PENDENTE_AVALIACAO" /* PENDENTE_AVALIACAO */
127
132
  };
128
133
  let updatedUserAnswers;
@@ -162,7 +167,7 @@ var useQuizStore = create()(
162
167
  // selectMultipleAnswer is for non-dissertative questions
163
168
  optionId: answerId,
164
169
  // selectMultipleAnswer should only set optionId
165
- questionType: question.type,
170
+ questionType: question.questionType,
166
171
  answerStatus: "PENDENTE_AVALIACAO" /* PENDENTE_AVALIACAO */
167
172
  })
168
173
  );
@@ -189,7 +194,7 @@ var useQuizStore = create()(
189
194
  const question = activeQuiz.quiz.questions.find(
190
195
  (q) => q.id === questionId
191
196
  );
192
- if (!question || question.type !== "DISSERTATIVA" /* DISSERTATIVA */) {
197
+ if (!question || question.questionType !== "DISSERTATIVA" /* DISSERTATIVA */) {
193
198
  console.warn(
194
199
  "selectDissertativeAnswer called for non-dissertative question"
195
200
  );
@@ -239,7 +244,7 @@ var useQuizStore = create()(
239
244
  userId,
240
245
  answer: null,
241
246
  optionId: null,
242
- questionType: currentQuestion.type,
247
+ questionType: currentQuestion.questionType,
243
248
  answerStatus: "PENDENTE_AVALIACAO" /* PENDENTE_AVALIACAO */
244
249
  };
245
250
  let updatedUserAnswers;
@@ -275,9 +280,9 @@ var useQuizStore = create()(
275
280
  questionId,
276
281
  activityId,
277
282
  userId,
278
- answer: question.type === "DISSERTATIVA" /* DISSERTATIVA */ ? answerId || null : null,
279
- optionId: question.type !== "DISSERTATIVA" /* DISSERTATIVA */ ? answerId || null : null,
280
- questionType: question.type,
283
+ answer: question.questionType === "DISSERTATIVA" /* DISSERTATIVA */ ? answerId || null : null,
284
+ optionId: question.questionType !== "DISSERTATIVA" /* DISSERTATIVA */ ? answerId || null : null,
285
+ questionType: question.questionType,
281
286
  answerStatus: "PENDENTE_AVALIACAO" /* PENDENTE_AVALIACAO */
282
287
  };
283
288
  if (existingAnswerIndex !== -1) {
@@ -305,7 +310,10 @@ var useQuizStore = create()(
305
310
  timeElapsed: 0,
306
311
  isStarted: false,
307
312
  isFinished: false,
308
- userId: ""
313
+ userId: "",
314
+ variant: "default",
315
+ questionsResult: null,
316
+ currentQuestionResult: null
309
317
  });
310
318
  },
311
319
  // Timer
@@ -507,6 +515,31 @@ var useQuizStore = create()(
507
515
  (q) => q.id === questionId
508
516
  );
509
517
  return questionIndex + 1;
518
+ },
519
+ // Question Result
520
+ getQuestionResultByQuestionId: (questionId) => {
521
+ const { questionsResult } = get();
522
+ return questionsResult?.answers.find(
523
+ (answer) => answer.questionId === questionId
524
+ ) || null;
525
+ },
526
+ getQuestionResultStatistics: () => {
527
+ const { questionsResult } = get();
528
+ return questionsResult?.statistics || null;
529
+ },
530
+ getQuestionResult: () => {
531
+ const { questionsResult } = get();
532
+ return questionsResult;
533
+ },
534
+ setQuestionsResult: (questionsResult) => {
535
+ set({ questionsResult });
536
+ },
537
+ setCurrentQuestionResult: (currentQuestionResult) => {
538
+ set({ currentQuestionResult });
539
+ },
540
+ getCurrentQuestionResult: () => {
541
+ const { currentQuestionResult } = get();
542
+ return currentQuestionResult;
510
543
  }
511
544
  };
512
545
  },
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/components/Quiz/useQuizStore.ts"],"sourcesContent":["import { create } from 'zustand';\nimport { devtools } from 'zustand/middleware';\n\nexport enum QUESTION_DIFFICULTY {\n FACIL = 'FACIL',\n MEDIO = 'MEDIO',\n DIFICIL = 'DIFICIL',\n}\n\nexport enum QUESTION_TYPE {\n ALTERNATIVA = 'ALTERNATIVA',\n DISSERTATIVA = 'DISSERTATIVA',\n MULTIPLA_CHOICE = 'MULTIPLA_CHOICE',\n VERDADEIRO_FALSO = 'VERDADEIRO_FALSO',\n LIGAR_PONTOS = 'LIGAR_PONTOS',\n PREENCHER = 'PREENCHER',\n IMAGEM = 'IMAGEM',\n}\n\nexport enum QUESTION_STATUS {\n APROVADO = 'APROVADO',\n REPROVADO = 'REPROVADO',\n}\n\nexport enum ANSWER_STATUS {\n RESPOSTA_CORRETA = 'RESPOSTA_CORRETA',\n RESPOSTA_INCORRETA = 'RESPOSTA_INCORRETA',\n PENDENTE_AVALIACAO = 'PENDENTE_AVALIACAO',\n}\n\nexport interface Question {\n id: string;\n questionText: string;\n description: string;\n type: QUESTION_TYPE;\n status: QUESTION_STATUS;\n difficulty: QUESTION_DIFFICULTY;\n examBoard: string;\n examYear: string;\n answerKey: null | string;\n institutionIds: string[];\n knowledgeMatrix: {\n areaKnowledgeId: string;\n subjectId: string;\n topicId: string;\n subtopicId: string;\n contentId: string;\n }[];\n options: {\n id: string;\n option: string;\n isCorrect: boolean;\n }[];\n}\n\ninterface Simulado {\n id: string;\n title: string;\n category: string;\n questions: Question[];\n}\n\ninterface Atividade {\n id: string;\n title: string;\n questions: Question[];\n}\n\ninterface Aula {\n id: string;\n title: string;\n questions: Question[];\n}\n\ninterface UserAnswerItem {\n questionId: string;\n activityId: string;\n userId: string;\n answer: string | null;\n optionId: string | null;\n questionType: QUESTION_TYPE;\n answerStatus: ANSWER_STATUS;\n}\n\ninterface QuizState {\n // Data\n bySimulated?: Simulado;\n byActivity?: Atividade;\n byQuestionary?: Aula;\n\n // UI State\n currentQuestionIndex: number;\n selectedAnswers: Record<string, string>;\n userAnswers: UserAnswerItem[];\n timeElapsed: number;\n isStarted: boolean;\n isFinished: boolean;\n userId: string;\n variant: 'result' | 'default';\n // Actions\n setBySimulated: (simulado: Simulado) => void;\n setByActivity: (atividade: Atividade) => void;\n setByQuestionary: (aula: Aula) => void;\n setUserId: (userId: string) => void;\n setUserAnswers: (userAnswers: UserAnswerItem[]) => void;\n setVariant: (variant: 'result' | 'default') => void;\n // Quiz Navigation\n goToNextQuestion: () => void;\n goToPreviousQuestion: () => void;\n goToQuestion: (index: number) => void;\n getActiveQuiz: () => {\n quiz: Simulado | Atividade | Aula;\n type: 'bySimulated' | 'byActivity' | 'byQuestionary';\n } | null;\n\n // Quiz Actions\n selectAnswer: (questionId: string, answerId: string) => void;\n selectMultipleAnswer: (questionId: string, answerIds: string[]) => void;\n selectDissertativeAnswer: (questionId: string, answer: string) => void;\n skipQuestion: () => void;\n addUserAnswer: (questionId: string, answerId?: string) => void;\n startQuiz: () => void;\n finishQuiz: () => void;\n resetQuiz: () => void;\n\n // Timer\n updateTime: (time: number) => void;\n startTimer: () => void;\n stopTimer: () => void;\n\n // Getters\n getCurrentQuestion: () => Question | null;\n getTotalQuestions: () => number;\n getAnsweredQuestions: () => number;\n getUnansweredQuestions: () => number[];\n getSkippedQuestions: () => number;\n getProgress: () => number;\n isQuestionAnswered: (questionId: string) => boolean;\n isQuestionSkipped: (questionId: string) => boolean;\n getCurrentAnswer: () => UserAnswerItem | undefined;\n getAllCurrentAnswer: () => UserAnswerItem[] | undefined;\n getQuizTitle: () => string;\n formatTime: (seconds: number) => string;\n getUserAnswers: () => UserAnswerItem[];\n getUnansweredQuestionsFromUserAnswers: () => number[];\n getQuestionsGroupedBySubject: () => { [key: string]: Question[] };\n getUserId: () => string;\n setCurrentQuestion: (question: Question) => void;\n\n // New methods for userAnswers\n getQuestionIndex: (questionId: string) => number;\n getUserAnswerByQuestionId: (questionId: string) => UserAnswerItem | null;\n isQuestionAnsweredByUserAnswers: (questionId: string) => boolean;\n getQuestionStatusFromUserAnswers: (\n questionId: string\n ) => 'answered' | 'unanswered' | 'skipped';\n getUserAnswersForActivity: () => UserAnswerItem[];\n\n // Answer status management\n setAnswerStatus: (questionId: string, status: ANSWER_STATUS) => void;\n getAnswerStatus: (questionId: string) => ANSWER_STATUS | null;\n}\n\nexport const useQuizStore = create<QuizState>()(\n devtools(\n (set, get) => {\n let timerInterval: ReturnType<typeof setInterval> | null = null;\n\n const startTimer = () => {\n if (get().isFinished) {\n return;\n }\n\n if (timerInterval) {\n clearInterval(timerInterval);\n }\n\n timerInterval = setInterval(() => {\n const { timeElapsed } = get();\n set({ timeElapsed: timeElapsed + 1 });\n }, 1000);\n };\n\n const stopTimer = () => {\n if (timerInterval) {\n clearInterval(timerInterval);\n timerInterval = null;\n }\n };\n\n return {\n // Initial State\n currentQuestionIndex: 0,\n selectedAnswers: {},\n userAnswers: [],\n timeElapsed: 0,\n isStarted: false,\n isFinished: false,\n userId: '',\n variant: 'default',\n // Setters\n setBySimulated: (simulado) => set({ bySimulated: simulado }),\n setByActivity: (atividade) => set({ byActivity: atividade }),\n setByQuestionary: (aula) => set({ byQuestionary: aula }),\n setUserId: (userId) => set({ userId }),\n setUserAnswers: (userAnswers) => set({ userAnswers }),\n getUserId: () => get().userId,\n setVariant: (variant) => set({ variant }),\n // Navigation\n goToNextQuestion: () => {\n const { currentQuestionIndex, getTotalQuestions } = get();\n const totalQuestions = getTotalQuestions();\n\n if (currentQuestionIndex < totalQuestions - 1) {\n set({ currentQuestionIndex: currentQuestionIndex + 1 });\n }\n },\n\n goToPreviousQuestion: () => {\n const { currentQuestionIndex } = get();\n\n if (currentQuestionIndex > 0) {\n set({ currentQuestionIndex: currentQuestionIndex - 1 });\n }\n },\n\n goToQuestion: (index) => {\n const { getTotalQuestions } = get();\n const totalQuestions = getTotalQuestions();\n\n if (index >= 0 && index < totalQuestions) {\n set({ currentQuestionIndex: index });\n }\n },\n\n getActiveQuiz: () => {\n const { bySimulated, byActivity, byQuestionary } = get();\n if (bySimulated)\n return { quiz: bySimulated, type: 'bySimulated' as const };\n if (byActivity)\n return { quiz: byActivity, type: 'byActivity' as const };\n if (byQuestionary)\n return { quiz: byQuestionary, type: 'byQuestionary' as const };\n return null;\n },\n\n selectAnswer: (questionId, answerId) => {\n const { getActiveQuiz, userAnswers } = get();\n const activeQuiz = getActiveQuiz();\n\n if (!activeQuiz) return;\n\n const activityId = activeQuiz.quiz.id;\n const userId = get().getUserId();\n\n if (!userId || userId === '') {\n console.warn('selectAnswer called before userId is set');\n return;\n }\n\n const question = activeQuiz.quiz.questions.find(\n (q) => q.id === questionId\n );\n if (!question) return;\n\n const existingAnswerIndex = userAnswers.findIndex(\n (answer) => answer.questionId === questionId\n );\n\n const newUserAnswer: UserAnswerItem = {\n questionId,\n activityId,\n userId,\n answer:\n question.type === QUESTION_TYPE.DISSERTATIVA ? answerId : null,\n optionId:\n question.type === QUESTION_TYPE.DISSERTATIVA ? null : answerId,\n questionType: question.type,\n answerStatus: ANSWER_STATUS.PENDENTE_AVALIACAO,\n };\n\n let updatedUserAnswers;\n if (existingAnswerIndex !== -1) {\n updatedUserAnswers = [...userAnswers];\n updatedUserAnswers[existingAnswerIndex] = newUserAnswer;\n } else {\n updatedUserAnswers = [...userAnswers, newUserAnswer];\n }\n\n set({\n userAnswers: updatedUserAnswers,\n });\n },\n\n selectMultipleAnswer: (questionId, answerIds) => {\n const { getActiveQuiz, userAnswers } = get();\n const activeQuiz = getActiveQuiz();\n\n if (!activeQuiz) return;\n\n const activityId = activeQuiz.quiz.id;\n const userId = get().getUserId();\n\n if (!userId || userId === '') {\n console.warn('selectMultipleAnswer called before userId is set');\n return;\n }\n\n const question = activeQuiz.quiz.questions.find(\n (q) => q.id === questionId\n );\n if (!question) return;\n\n // Remove all existing answers for this questionId\n const filteredUserAnswers = userAnswers.filter(\n (answer) => answer.questionId !== questionId\n );\n\n // Create new UserAnswerItem objects for each answerId\n const newUserAnswers: UserAnswerItem[] = answerIds.map(\n (answerId) => ({\n questionId,\n activityId,\n userId,\n answer: null, // selectMultipleAnswer is for non-dissertative questions\n optionId: answerId, // selectMultipleAnswer should only set optionId\n questionType: question.type,\n answerStatus: ANSWER_STATUS.PENDENTE_AVALIACAO,\n })\n );\n\n // Combine filtered answers with new answers\n const updatedUserAnswers = [\n ...filteredUserAnswers,\n ...newUserAnswers,\n ];\n\n set({\n userAnswers: updatedUserAnswers,\n });\n },\n\n selectDissertativeAnswer: (questionId, answer) => {\n const { getActiveQuiz, userAnswers } = get();\n const activeQuiz = getActiveQuiz();\n\n if (!activeQuiz) return;\n\n const activityId = activeQuiz.quiz.id;\n const userId = get().getUserId();\n\n if (!userId || userId === '') {\n console.warn(\n 'selectDissertativeAnswer called before userId is set'\n );\n return;\n }\n\n const question = activeQuiz.quiz.questions.find(\n (q) => q.id === questionId\n );\n if (!question || question.type !== QUESTION_TYPE.DISSERTATIVA) {\n console.warn(\n 'selectDissertativeAnswer called for non-dissertative question'\n );\n return;\n }\n\n const existingAnswerIndex = userAnswers.findIndex(\n (answerItem) => answerItem.questionId === questionId\n );\n\n const newUserAnswer: UserAnswerItem = {\n questionId,\n activityId,\n userId,\n answer: answer,\n optionId: null,\n questionType: QUESTION_TYPE.DISSERTATIVA,\n answerStatus: ANSWER_STATUS.PENDENTE_AVALIACAO,\n };\n\n let updatedUserAnswers;\n if (existingAnswerIndex !== -1) {\n updatedUserAnswers = [...userAnswers];\n updatedUserAnswers[existingAnswerIndex] = newUserAnswer;\n } else {\n updatedUserAnswers = [...userAnswers, newUserAnswer];\n }\n\n set({\n userAnswers: updatedUserAnswers,\n });\n },\n\n skipQuestion: () => {\n const { getCurrentQuestion, userAnswers, getActiveQuiz } = get();\n const currentQuestion = getCurrentQuestion();\n const activeQuiz = getActiveQuiz();\n\n if (!activeQuiz) return;\n\n if (currentQuestion) {\n const activityId = activeQuiz.quiz.id;\n const userId = get().getUserId();\n\n if (!userId || userId === '') {\n console.warn('skipQuestion called before userId is set');\n return;\n }\n\n const existingAnswerIndex = userAnswers.findIndex(\n (answer) => answer.questionId === currentQuestion.id\n );\n\n const newUserAnswer: UserAnswerItem = {\n questionId: currentQuestion.id,\n activityId,\n userId,\n answer: null,\n optionId: null,\n questionType: currentQuestion.type,\n answerStatus: ANSWER_STATUS.PENDENTE_AVALIACAO,\n };\n\n let updatedUserAnswers;\n if (existingAnswerIndex !== -1) {\n // Update existing answer\n updatedUserAnswers = [...userAnswers];\n updatedUserAnswers[existingAnswerIndex] = newUserAnswer;\n } else {\n // Add new answer\n updatedUserAnswers = [...userAnswers, newUserAnswer];\n }\n\n set({\n userAnswers: updatedUserAnswers,\n });\n }\n },\n\n addUserAnswer: (questionId, answerId) => {\n const { getActiveQuiz, userAnswers } = get();\n const activeQuiz = getActiveQuiz();\n\n if (!activeQuiz) return;\n\n // Add to userAnswers array with new structure\n const activityId = activeQuiz.quiz.id;\n const userId = get().getUserId();\n\n if (!userId || userId === '') {\n console.warn('addUserAnswer called before userId is set');\n return;\n }\n\n const question = activeQuiz.quiz.questions.find(\n (q) => q.id === questionId\n );\n if (!question) return;\n\n const existingAnswerIndex = userAnswers.findIndex(\n (answer) => answer.questionId === questionId\n );\n\n const newUserAnswer: UserAnswerItem = {\n questionId,\n activityId,\n userId,\n answer:\n question.type === QUESTION_TYPE.DISSERTATIVA\n ? answerId || null\n : null,\n optionId:\n question.type !== QUESTION_TYPE.DISSERTATIVA\n ? answerId || null\n : null,\n questionType: question.type,\n answerStatus: ANSWER_STATUS.PENDENTE_AVALIACAO,\n };\n\n if (existingAnswerIndex !== -1) {\n // Update existing answer\n const updatedUserAnswers = [...userAnswers];\n updatedUserAnswers[existingAnswerIndex] = newUserAnswer;\n set({ userAnswers: updatedUserAnswers });\n } else {\n // Add new answer\n set({ userAnswers: [...userAnswers, newUserAnswer] });\n }\n },\n\n startQuiz: () => {\n set({ isStarted: true, timeElapsed: 0 });\n startTimer();\n },\n\n finishQuiz: () => {\n set({ isFinished: true });\n stopTimer();\n },\n\n resetQuiz: () => {\n stopTimer();\n set({\n currentQuestionIndex: 0,\n selectedAnswers: {},\n userAnswers: [],\n timeElapsed: 0,\n isStarted: false,\n isFinished: false,\n userId: '',\n });\n },\n\n // Timer\n updateTime: (time) => set({ timeElapsed: time }),\n startTimer,\n stopTimer,\n\n // Getters\n getCurrentQuestion: () => {\n const { currentQuestionIndex, getActiveQuiz } = get();\n const activeQuiz = getActiveQuiz();\n\n if (!activeQuiz) {\n return null;\n }\n\n return activeQuiz.quiz.questions[currentQuestionIndex];\n },\n\n getTotalQuestions: () => {\n const { getActiveQuiz } = get();\n const activeQuiz = getActiveQuiz();\n\n return activeQuiz?.quiz?.questions?.length || 0;\n },\n\n getAnsweredQuestions: () => {\n const { userAnswers } = get();\n return userAnswers.filter(\n (answer) => answer.optionId !== null || answer.answer !== null\n ).length;\n },\n\n getUnansweredQuestions: () => {\n const { getActiveQuiz, userAnswers } = get();\n const activeQuiz = getActiveQuiz();\n if (!activeQuiz) return [];\n\n const unansweredQuestions: number[] = [];\n\n activeQuiz.quiz.questions.forEach((question, index) => {\n const userAnswer = userAnswers.find(\n (answer) => answer.questionId === question.id\n );\n const isAnswered =\n userAnswer &&\n (userAnswer.optionId !== null || userAnswer.answer !== null);\n const isSkipped =\n userAnswer &&\n userAnswer.optionId === null &&\n userAnswer.answer === null;\n\n if (!isAnswered && !isSkipped) {\n unansweredQuestions.push(index + 1); // index + 1 para mostrar número da questão\n }\n });\n return unansweredQuestions;\n },\n\n getSkippedQuestions: () => {\n const { userAnswers } = get();\n return userAnswers.filter(\n (answer) => answer.optionId === null && answer.answer === null\n ).length;\n },\n\n getProgress: () => {\n const { getTotalQuestions, getAnsweredQuestions } = get();\n const total = getTotalQuestions();\n const answered = getAnsweredQuestions();\n\n return total > 0 ? (answered / total) * 100 : 0;\n },\n\n isQuestionAnswered: (questionId) => {\n const { userAnswers } = get();\n const userAnswer = userAnswers.find(\n (answer) => answer.questionId === questionId\n );\n return userAnswer\n ? userAnswer.optionId !== null || userAnswer.answer !== null\n : false;\n },\n\n isQuestionSkipped: (questionId) => {\n const { userAnswers } = get();\n const userAnswer = userAnswers.find(\n (answer) => answer.questionId === questionId\n );\n return userAnswer\n ? userAnswer.optionId === null && userAnswer.answer === null\n : false;\n },\n\n getCurrentAnswer: () => {\n const { getCurrentQuestion, userAnswers } = get();\n const currentQuestion = getCurrentQuestion();\n\n if (!currentQuestion) return undefined;\n\n const userAnswer = userAnswers.find(\n (answer) => answer.questionId === currentQuestion.id\n );\n\n return userAnswer;\n },\n\n getAllCurrentAnswer: () => {\n const { getCurrentQuestion, userAnswers } = get();\n const currentQuestion = getCurrentQuestion();\n\n if (!currentQuestion) return undefined;\n\n const userAnswer = userAnswers.filter(\n (answer) => answer.questionId === currentQuestion.id\n );\n\n return userAnswer;\n },\n\n getQuizTitle: () => {\n const { getActiveQuiz } = get();\n const activeQuiz = getActiveQuiz();\n\n return activeQuiz?.quiz?.title || 'Quiz';\n },\n\n formatTime: (seconds: number) => {\n const minutes = Math.floor(seconds / 60);\n const remainingSeconds = seconds % 60;\n return `${minutes.toString().padStart(2, '0')}:${remainingSeconds.toString().padStart(2, '0')}`;\n },\n\n getUserAnswers: () => {\n const { userAnswers } = get();\n return userAnswers;\n },\n\n getUnansweredQuestionsFromUserAnswers: () => {\n const { getActiveQuiz, userAnswers } = get();\n const activeQuiz = getActiveQuiz();\n if (!activeQuiz) return [];\n\n const unansweredQuestions: number[] = [];\n\n activeQuiz.quiz.questions.forEach((question, index) => {\n const userAnswer = userAnswers.find(\n (answer) => answer.questionId === question.id\n );\n const hasAnswer =\n userAnswer &&\n (userAnswer.optionId !== null || userAnswer.answer !== null);\n const isSkipped =\n userAnswer &&\n userAnswer.optionId === null &&\n userAnswer.answer === null;\n\n // Se não há resposta do usuário OU se a questão foi pulada\n if (!hasAnswer || isSkipped) {\n unansweredQuestions.push(index + 1); // index + 1 para mostrar número da questão\n }\n });\n\n return unansweredQuestions;\n },\n\n getQuestionsGroupedBySubject: () => {\n const { getActiveQuiz } = get();\n const activeQuiz = getActiveQuiz();\n if (!activeQuiz) return {};\n\n const groupedQuestions: { [key: string]: Question[] } = {};\n\n activeQuiz.quiz.questions.forEach((question) => {\n const subjectId =\n question.knowledgeMatrix?.[0]?.subjectId || 'Sem matéria';\n\n if (!groupedQuestions[subjectId]) {\n groupedQuestions[subjectId] = [];\n }\n\n groupedQuestions[subjectId].push(question);\n });\n\n return groupedQuestions;\n },\n\n // New methods for userAnswers\n getUserAnswerByQuestionId: (questionId) => {\n const { userAnswers } = get();\n return (\n userAnswers.find((answer) => answer.questionId === questionId) ||\n null\n );\n },\n isQuestionAnsweredByUserAnswers: (questionId) => {\n const { userAnswers } = get();\n const answer = userAnswers.find(\n (answer) => answer.questionId === questionId\n );\n return answer\n ? answer.optionId !== null || answer.answer !== null\n : false;\n },\n getQuestionStatusFromUserAnswers: (questionId) => {\n const { userAnswers } = get();\n const answer = userAnswers.find(\n (answer) => answer.questionId === questionId\n );\n if (!answer) return 'unanswered';\n if (answer.optionId === null) return 'skipped';\n return 'answered';\n },\n getUserAnswersForActivity: () => {\n const { userAnswers } = get();\n return userAnswers;\n },\n setCurrentQuestion: (question) => {\n const { getActiveQuiz } = get();\n const activeQuiz = getActiveQuiz();\n if (!activeQuiz) return;\n\n const questionIndex = activeQuiz.quiz.questions.findIndex(\n (q) => q.id === question.id\n );\n\n // Validate that the question was found before updating currentQuestionIndex\n if (questionIndex === -1) {\n console.warn(\n `Question with id \"${question.id}\" not found in active quiz`\n );\n return;\n }\n\n set({ currentQuestionIndex: questionIndex });\n },\n\n setAnswerStatus: (questionId, status) => {\n const { userAnswers } = get();\n const existingAnswerIndex = userAnswers.findIndex(\n (answer) => answer.questionId === questionId\n );\n\n if (existingAnswerIndex !== -1) {\n const updatedUserAnswers = [...userAnswers];\n updatedUserAnswers[existingAnswerIndex] = {\n ...updatedUserAnswers[existingAnswerIndex],\n answerStatus: status,\n };\n set({ userAnswers: updatedUserAnswers });\n }\n },\n\n getAnswerStatus: (questionId) => {\n const { userAnswers } = get();\n const userAnswer = userAnswers.find(\n (answer) => answer.questionId === questionId\n );\n return userAnswer ? userAnswer.answerStatus : null;\n },\n getQuestionIndex: (questionId) => {\n const { getActiveQuiz } = get();\n const activeQuiz = getActiveQuiz();\n if (!activeQuiz) return 0;\n\n const questionIndex = activeQuiz.quiz.questions.findIndex(\n (q) => q.id === questionId\n );\n return questionIndex + 1;\n },\n };\n },\n {\n name: 'quiz-store',\n }\n )\n);\n"],"mappings":"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","names":["QUESTION_DIFFICULTY","QUESTION_TYPE","QUESTION_STATUS","ANSWER_STATUS","answer"]}
1
+ {"version":3,"sources":["../../../src/components/Quiz/useQuizStore.ts"],"sourcesContent":["import { create } from 'zustand';\nimport { devtools } from 'zustand/middleware';\n\nexport enum QUESTION_DIFFICULTY {\n FACIL = 'FACIL',\n MEDIO = 'MEDIO',\n DIFICIL = 'DIFICIL',\n}\n\nexport enum QUESTION_TYPE {\n ALTERNATIVA = 'ALTERNATIVA',\n DISSERTATIVA = 'DISSERTATIVA',\n MULTIPLA_CHOICE = 'MULTIPLA_CHOICE',\n VERDADEIRO_FALSO = 'VERDADEIRO_FALSO',\n LIGAR_PONTOS = 'LIGAR_PONTOS',\n PREENCHER = 'PREENCHER',\n IMAGEM = 'IMAGEM',\n}\n\nexport enum QUESTION_STATUS {\n PENDENTE_AVALIACAO = 'PENDENTE_AVALIACAO',\n RESPOSTA_CORRETA = 'RESPOSTA_CORRETA',\n RESPOSTA_INCORRETA = 'RESPOSTA_INCORRETA',\n NAO_RESPONDIDO = 'NAO_RESPONDIDO',\n}\n\nexport enum ANSWER_STATUS {\n RESPOSTA_CORRETA = 'RESPOSTA_CORRETA',\n RESPOSTA_INCORRETA = 'RESPOSTA_INCORRETA',\n PENDENTE_AVALIACAO = 'PENDENTE_AVALIACAO',\n}\n\nexport interface QuestionResult {\n answers: {\n id: string;\n questionId: string;\n answer: string | null;\n optionId: string;\n selectedOptionText: string | null;\n answerStatus: ANSWER_STATUS;\n createdAt: string;\n updatedAt: string;\n statement: string;\n questionType: QUESTION_TYPE;\n correctOption: string;\n difficultyLevel: QUESTION_DIFFICULTY;\n solutionExplanation: string | null;\n options: {\n id: string;\n option: string;\n isCorrect: boolean;\n }[];\n teacherFeedback: string | null;\n attachment: string | null;\n score: number | null;\n gradedAt: string | null;\n gradedBy: string;\n }[];\n statistics: {\n totalAnswered: number;\n correctAnswers: number;\n incorrectAnswers: number;\n pendingAnswers: number;\n score: number;\n };\n}\n\nexport interface Question {\n id: string;\n questionText: string;\n questionType: QUESTION_TYPE;\n difficultyLevel: QUESTION_DIFFICULTY;\n description: string;\n examBoard: string | null;\n examYear: string | null;\n solutionExplanation: string | null;\n answer: null;\n answerStatus: ANSWER_STATUS;\n options: {\n id: string;\n option: string;\n }[];\n correctOptionIds?: string[];\n knowledgeMatrix: {\n areaKnowledgeId: string;\n subjectId: string;\n topicId: string;\n subtopicId: string;\n contentId: string;\n }[];\n}\n\ninterface Simulado {\n id: string;\n title: string;\n type: string;\n subtype: string;\n difficulty: string | null;\n notification: string | null;\n status: string;\n startDate: Date | null;\n finalDate: Date | null;\n canRetry: boolean;\n createdAt: Date | null;\n updatedAt: Date | null;\n questions: Question[];\n}\n\ninterface Atividade {\n id: string;\n title: string;\n questions: Question[];\n}\n\ninterface Aula {\n id: string;\n title: string;\n questions: Question[];\n}\n\ninterface UserAnswerItem {\n questionId: string;\n activityId: string;\n userId: string;\n answer: string | null;\n optionId: string | null;\n questionType: QUESTION_TYPE;\n answerStatus: ANSWER_STATUS;\n}\n\ninterface QuizState {\n // Data\n bySimulated?: Simulado;\n byActivity?: Atividade;\n byQuestionary?: Aula;\n\n // UI State\n currentQuestionIndex: number;\n selectedAnswers: Record<string, string>;\n userAnswers: UserAnswerItem[];\n timeElapsed: number;\n isStarted: boolean;\n isFinished: boolean;\n userId: string;\n variant: 'result' | 'default';\n // Actions\n setBySimulated: (simulado: Simulado) => void;\n setByActivity: (atividade: Atividade) => void;\n setByQuestionary: (aula: Aula) => void;\n setQuestionResult: (questionResult: QuestionResult) => void;\n setUserId: (userId: string) => void;\n setUserAnswers: (userAnswers: UserAnswerItem[]) => void;\n setVariant: (variant: 'result' | 'default') => void;\n // Quiz Navigation\n goToNextQuestion: () => void;\n goToPreviousQuestion: () => void;\n goToQuestion: (index: number) => void;\n getActiveQuiz: () => {\n quiz: Simulado | Atividade | Aula;\n type: 'bySimulated' | 'byActivity' | 'byQuestionary';\n } | null;\n\n // Quiz Actions\n selectAnswer: (questionId: string, answerId: string) => void;\n selectMultipleAnswer: (questionId: string, answerIds: string[]) => void;\n selectDissertativeAnswer: (questionId: string, answer: string) => void;\n skipQuestion: () => void;\n addUserAnswer: (questionId: string, answerId?: string) => void;\n startQuiz: () => void;\n finishQuiz: () => void;\n resetQuiz: () => void;\n\n // Timer\n updateTime: (time: number) => void;\n startTimer: () => void;\n stopTimer: () => void;\n\n // Getters\n getCurrentQuestion: () => Question | null;\n getTotalQuestions: () => number;\n getAnsweredQuestions: () => number;\n getUnansweredQuestions: () => number[];\n getSkippedQuestions: () => number;\n getProgress: () => number;\n isQuestionAnswered: (questionId: string) => boolean;\n isQuestionSkipped: (questionId: string) => boolean;\n getCurrentAnswer: () => UserAnswerItem | undefined;\n getAllCurrentAnswer: () => UserAnswerItem[] | undefined;\n getQuizTitle: () => string;\n formatTime: (seconds: number) => string;\n getUserAnswers: () => UserAnswerItem[];\n getUnansweredQuestionsFromUserAnswers: () => number[];\n getQuestionsGroupedBySubject: () => { [key: string]: Question[] };\n getUserId: () => string;\n setCurrentQuestion: (question: Question) => void;\n\n // New methods for userAnswers\n getQuestionIndex: (questionId: string) => number;\n getUserAnswerByQuestionId: (questionId: string) => UserAnswerItem | null;\n isQuestionAnsweredByUserAnswers: (questionId: string) => boolean;\n getQuestionStatusFromUserAnswers: (\n questionId: string\n ) => 'answered' | 'unanswered' | 'skipped';\n getUserAnswersForActivity: () => UserAnswerItem[];\n // Answer status management\n setAnswerStatus: (questionId: string, status: ANSWER_STATUS) => void;\n getAnswerStatus: (questionId: string) => ANSWER_STATUS | null;\n\n // Question Result\n questionsResult: QuestionResult | null;\n currentQuestionResult: QuestionResult['answers'] | null;\n setQuestionsResult: (questionsResult: QuestionResult) => void;\n setCurrentQuestionResult: (\n currentQuestionResult: QuestionResult['answers']\n ) => void;\n getQuestionResultByQuestionId: (\n questionId: string\n ) => QuestionResult['answers'][number] | null;\n getQuestionResultStatistics: () => QuestionResult['statistics'] | null;\n getQuestionResult: () => QuestionResult | null;\n getCurrentQuestionResult: () => QuestionResult['answers'] | null;\n}\n\nexport const useQuizStore = create<QuizState>()(\n devtools(\n (set, get) => {\n let timerInterval: ReturnType<typeof setInterval> | null = null;\n\n const startTimer = () => {\n if (get().isFinished) {\n return;\n }\n\n if (timerInterval) {\n clearInterval(timerInterval);\n }\n\n timerInterval = setInterval(() => {\n const { timeElapsed } = get();\n set({ timeElapsed: timeElapsed + 1 });\n }, 1000);\n };\n\n const stopTimer = () => {\n if (timerInterval) {\n clearInterval(timerInterval);\n timerInterval = null;\n }\n };\n\n return {\n // Initial State\n currentQuestionIndex: 0,\n selectedAnswers: {},\n userAnswers: [],\n timeElapsed: 0,\n isStarted: false,\n isFinished: false,\n userId: '',\n variant: 'default',\n questionsResult: null,\n currentQuestionResult: null,\n // Setters\n setBySimulated: (simulado) => set({ bySimulated: simulado }),\n setByActivity: (atividade) => set({ byActivity: atividade }),\n setByQuestionary: (aula) => set({ byQuestionary: aula }),\n setUserId: (userId) => set({ userId }),\n setUserAnswers: (userAnswers) => set({ userAnswers }),\n getUserId: () => get().userId,\n setVariant: (variant) => set({ variant }),\n setQuestionResult: (questionsResult) => set({ questionsResult }),\n // Navigation\n goToNextQuestion: () => {\n const { currentQuestionIndex, getTotalQuestions } = get();\n const totalQuestions = getTotalQuestions();\n\n if (currentQuestionIndex < totalQuestions - 1) {\n set({ currentQuestionIndex: currentQuestionIndex + 1 });\n }\n },\n\n goToPreviousQuestion: () => {\n const { currentQuestionIndex } = get();\n\n if (currentQuestionIndex > 0) {\n set({ currentQuestionIndex: currentQuestionIndex - 1 });\n }\n },\n\n goToQuestion: (index) => {\n const { getTotalQuestions } = get();\n const totalQuestions = getTotalQuestions();\n\n if (index >= 0 && index < totalQuestions) {\n set({ currentQuestionIndex: index });\n }\n },\n\n getActiveQuiz: () => {\n const { bySimulated, byActivity, byQuestionary } = get();\n if (bySimulated)\n return { quiz: bySimulated, type: 'bySimulated' as const };\n if (byActivity)\n return { quiz: byActivity, type: 'byActivity' as const };\n if (byQuestionary)\n return { quiz: byQuestionary, type: 'byQuestionary' as const };\n return null;\n },\n\n selectAnswer: (questionId, answerId) => {\n const { getActiveQuiz, userAnswers } = get();\n const activeQuiz = getActiveQuiz();\n\n if (!activeQuiz) return;\n\n const activityId = activeQuiz.quiz.id;\n const userId = get().getUserId();\n\n if (!userId || userId === '') {\n console.warn('selectAnswer called before userId is set');\n return;\n }\n\n const question = activeQuiz.quiz.questions.find(\n (q) => q.id === questionId\n );\n if (!question) return;\n\n const existingAnswerIndex = userAnswers.findIndex(\n (answer) => answer.questionId === questionId\n );\n\n const newUserAnswer: UserAnswerItem = {\n questionId,\n activityId,\n userId,\n answer:\n question.questionType === QUESTION_TYPE.DISSERTATIVA\n ? answerId\n : null,\n optionId:\n question.questionType === QUESTION_TYPE.DISSERTATIVA\n ? null\n : answerId,\n questionType: question.questionType,\n answerStatus: ANSWER_STATUS.PENDENTE_AVALIACAO,\n };\n\n let updatedUserAnswers;\n if (existingAnswerIndex !== -1) {\n updatedUserAnswers = [...userAnswers];\n updatedUserAnswers[existingAnswerIndex] = newUserAnswer;\n } else {\n updatedUserAnswers = [...userAnswers, newUserAnswer];\n }\n\n set({\n userAnswers: updatedUserAnswers,\n });\n },\n\n selectMultipleAnswer: (questionId, answerIds) => {\n const { getActiveQuiz, userAnswers } = get();\n const activeQuiz = getActiveQuiz();\n\n if (!activeQuiz) return;\n\n const activityId = activeQuiz.quiz.id;\n const userId = get().getUserId();\n\n if (!userId || userId === '') {\n console.warn('selectMultipleAnswer called before userId is set');\n return;\n }\n\n const question = activeQuiz.quiz.questions.find(\n (q) => q.id === questionId\n );\n if (!question) return;\n\n // Remove all existing answers for this questionId\n const filteredUserAnswers = userAnswers.filter(\n (answer) => answer.questionId !== questionId\n );\n\n // Create new UserAnswerItem objects for each answerId\n const newUserAnswers: UserAnswerItem[] = answerIds.map(\n (answerId) => ({\n questionId,\n activityId,\n userId,\n answer: null, // selectMultipleAnswer is for non-dissertative questions\n optionId: answerId, // selectMultipleAnswer should only set optionId\n questionType: question.questionType,\n answerStatus: ANSWER_STATUS.PENDENTE_AVALIACAO,\n })\n );\n\n // Combine filtered answers with new answers\n const updatedUserAnswers = [\n ...filteredUserAnswers,\n ...newUserAnswers,\n ];\n\n set({\n userAnswers: updatedUserAnswers,\n });\n },\n\n selectDissertativeAnswer: (questionId, answer) => {\n const { getActiveQuiz, userAnswers } = get();\n const activeQuiz = getActiveQuiz();\n\n if (!activeQuiz) return;\n\n const activityId = activeQuiz.quiz.id;\n const userId = get().getUserId();\n\n if (!userId || userId === '') {\n console.warn(\n 'selectDissertativeAnswer called before userId is set'\n );\n return;\n }\n\n const question = activeQuiz.quiz.questions.find(\n (q) => q.id === questionId\n );\n if (\n !question ||\n question.questionType !== QUESTION_TYPE.DISSERTATIVA\n ) {\n console.warn(\n 'selectDissertativeAnswer called for non-dissertative question'\n );\n return;\n }\n\n const existingAnswerIndex = userAnswers.findIndex(\n (answerItem) => answerItem.questionId === questionId\n );\n\n const newUserAnswer: UserAnswerItem = {\n questionId,\n activityId,\n userId,\n answer: answer,\n optionId: null,\n questionType: QUESTION_TYPE.DISSERTATIVA,\n answerStatus: ANSWER_STATUS.PENDENTE_AVALIACAO,\n };\n\n let updatedUserAnswers;\n if (existingAnswerIndex !== -1) {\n updatedUserAnswers = [...userAnswers];\n updatedUserAnswers[existingAnswerIndex] = newUserAnswer;\n } else {\n updatedUserAnswers = [...userAnswers, newUserAnswer];\n }\n\n set({\n userAnswers: updatedUserAnswers,\n });\n },\n\n skipQuestion: () => {\n const { getCurrentQuestion, userAnswers, getActiveQuiz } = get();\n const currentQuestion = getCurrentQuestion();\n const activeQuiz = getActiveQuiz();\n\n if (!activeQuiz) return;\n\n if (currentQuestion) {\n const activityId = activeQuiz.quiz.id;\n const userId = get().getUserId();\n\n if (!userId || userId === '') {\n console.warn('skipQuestion called before userId is set');\n return;\n }\n\n const existingAnswerIndex = userAnswers.findIndex(\n (answer) => answer.questionId === currentQuestion.id\n );\n\n const newUserAnswer: UserAnswerItem = {\n questionId: currentQuestion.id,\n activityId,\n userId,\n answer: null,\n optionId: null,\n questionType: currentQuestion.questionType,\n answerStatus: ANSWER_STATUS.PENDENTE_AVALIACAO,\n };\n\n let updatedUserAnswers;\n if (existingAnswerIndex !== -1) {\n // Update existing answer\n updatedUserAnswers = [...userAnswers];\n updatedUserAnswers[existingAnswerIndex] = newUserAnswer;\n } else {\n // Add new answer\n updatedUserAnswers = [...userAnswers, newUserAnswer];\n }\n\n set({\n userAnswers: updatedUserAnswers,\n });\n }\n },\n\n addUserAnswer: (questionId, answerId) => {\n const { getActiveQuiz, userAnswers } = get();\n const activeQuiz = getActiveQuiz();\n\n if (!activeQuiz) return;\n\n // Add to userAnswers array with new structure\n const activityId = activeQuiz.quiz.id;\n const userId = get().getUserId();\n\n if (!userId || userId === '') {\n console.warn('addUserAnswer called before userId is set');\n return;\n }\n\n const question = activeQuiz.quiz.questions.find(\n (q) => q.id === questionId\n );\n if (!question) return;\n\n const existingAnswerIndex = userAnswers.findIndex(\n (answer) => answer.questionId === questionId\n );\n\n const newUserAnswer: UserAnswerItem = {\n questionId,\n activityId,\n userId,\n answer:\n question.questionType === QUESTION_TYPE.DISSERTATIVA\n ? answerId || null\n : null,\n optionId:\n question.questionType !== QUESTION_TYPE.DISSERTATIVA\n ? answerId || null\n : null,\n questionType: question.questionType,\n answerStatus: ANSWER_STATUS.PENDENTE_AVALIACAO,\n };\n\n if (existingAnswerIndex !== -1) {\n // Update existing answer\n const updatedUserAnswers = [...userAnswers];\n updatedUserAnswers[existingAnswerIndex] = newUserAnswer;\n set({ userAnswers: updatedUserAnswers });\n } else {\n // Add new answer\n set({ userAnswers: [...userAnswers, newUserAnswer] });\n }\n },\n\n startQuiz: () => {\n set({ isStarted: true, timeElapsed: 0 });\n startTimer();\n },\n\n finishQuiz: () => {\n set({ isFinished: true });\n stopTimer();\n },\n\n resetQuiz: () => {\n stopTimer();\n set({\n currentQuestionIndex: 0,\n selectedAnswers: {},\n userAnswers: [],\n timeElapsed: 0,\n isStarted: false,\n isFinished: false,\n userId: '',\n variant: 'default',\n questionsResult: null,\n currentQuestionResult: null,\n });\n },\n\n // Timer\n updateTime: (time) => set({ timeElapsed: time }),\n startTimer,\n stopTimer,\n\n // Getters\n getCurrentQuestion: () => {\n const { currentQuestionIndex, getActiveQuiz } = get();\n const activeQuiz = getActiveQuiz();\n\n if (!activeQuiz) {\n return null;\n }\n\n return activeQuiz.quiz.questions[currentQuestionIndex];\n },\n\n getTotalQuestions: () => {\n const { getActiveQuiz } = get();\n const activeQuiz = getActiveQuiz();\n\n return activeQuiz?.quiz?.questions?.length || 0;\n },\n\n getAnsweredQuestions: () => {\n const { userAnswers } = get();\n return userAnswers.filter(\n (answer) => answer.optionId !== null || answer.answer !== null\n ).length;\n },\n\n getUnansweredQuestions: () => {\n const { getActiveQuiz, userAnswers } = get();\n const activeQuiz = getActiveQuiz();\n if (!activeQuiz) return [];\n\n const unansweredQuestions: number[] = [];\n\n activeQuiz.quiz.questions.forEach((question, index) => {\n const userAnswer = userAnswers.find(\n (answer) => answer.questionId === question.id\n );\n const isAnswered =\n userAnswer &&\n (userAnswer.optionId !== null || userAnswer.answer !== null);\n const isSkipped =\n userAnswer &&\n userAnswer.optionId === null &&\n userAnswer.answer === null;\n\n if (!isAnswered && !isSkipped) {\n unansweredQuestions.push(index + 1); // index + 1 para mostrar número da questão\n }\n });\n return unansweredQuestions;\n },\n\n getSkippedQuestions: () => {\n const { userAnswers } = get();\n return userAnswers.filter(\n (answer) => answer.optionId === null && answer.answer === null\n ).length;\n },\n\n getProgress: () => {\n const { getTotalQuestions, getAnsweredQuestions } = get();\n const total = getTotalQuestions();\n const answered = getAnsweredQuestions();\n\n return total > 0 ? (answered / total) * 100 : 0;\n },\n\n isQuestionAnswered: (questionId) => {\n const { userAnswers } = get();\n const userAnswer = userAnswers.find(\n (answer) => answer.questionId === questionId\n );\n return userAnswer\n ? userAnswer.optionId !== null || userAnswer.answer !== null\n : false;\n },\n\n isQuestionSkipped: (questionId) => {\n const { userAnswers } = get();\n const userAnswer = userAnswers.find(\n (answer) => answer.questionId === questionId\n );\n return userAnswer\n ? userAnswer.optionId === null && userAnswer.answer === null\n : false;\n },\n\n getCurrentAnswer: () => {\n const { getCurrentQuestion, userAnswers } = get();\n const currentQuestion = getCurrentQuestion();\n\n if (!currentQuestion) return undefined;\n\n const userAnswer = userAnswers.find(\n (answer) => answer.questionId === currentQuestion.id\n );\n\n return userAnswer;\n },\n\n getAllCurrentAnswer: () => {\n const { getCurrentQuestion, userAnswers } = get();\n const currentQuestion = getCurrentQuestion();\n\n if (!currentQuestion) return undefined;\n\n const userAnswer = userAnswers.filter(\n (answer) => answer.questionId === currentQuestion.id\n );\n\n return userAnswer;\n },\n\n getQuizTitle: () => {\n const { getActiveQuiz } = get();\n const activeQuiz = getActiveQuiz();\n\n return activeQuiz?.quiz?.title || 'Quiz';\n },\n\n formatTime: (seconds: number) => {\n const minutes = Math.floor(seconds / 60);\n const remainingSeconds = seconds % 60;\n return `${minutes.toString().padStart(2, '0')}:${remainingSeconds.toString().padStart(2, '0')}`;\n },\n\n getUserAnswers: () => {\n const { userAnswers } = get();\n return userAnswers;\n },\n\n getUnansweredQuestionsFromUserAnswers: () => {\n const { getActiveQuiz, userAnswers } = get();\n const activeQuiz = getActiveQuiz();\n if (!activeQuiz) return [];\n\n const unansweredQuestions: number[] = [];\n\n activeQuiz.quiz.questions.forEach((question, index) => {\n const userAnswer = userAnswers.find(\n (answer) => answer.questionId === question.id\n );\n const hasAnswer =\n userAnswer &&\n (userAnswer.optionId !== null || userAnswer.answer !== null);\n const isSkipped =\n userAnswer &&\n userAnswer.optionId === null &&\n userAnswer.answer === null;\n\n // Se não há resposta do usuário OU se a questão foi pulada\n if (!hasAnswer || isSkipped) {\n unansweredQuestions.push(index + 1); // index + 1 para mostrar número da questão\n }\n });\n\n return unansweredQuestions;\n },\n\n getQuestionsGroupedBySubject: () => {\n const { getActiveQuiz } = get();\n const activeQuiz = getActiveQuiz();\n if (!activeQuiz) return {};\n\n const groupedQuestions: { [key: string]: Question[] } = {};\n\n activeQuiz.quiz.questions.forEach((question) => {\n const subjectId =\n question.knowledgeMatrix?.[0]?.subjectId || 'Sem matéria';\n\n if (!groupedQuestions[subjectId]) {\n groupedQuestions[subjectId] = [];\n }\n\n groupedQuestions[subjectId].push(question);\n });\n\n return groupedQuestions;\n },\n\n // New methods for userAnswers\n getUserAnswerByQuestionId: (questionId) => {\n const { userAnswers } = get();\n return (\n userAnswers.find((answer) => answer.questionId === questionId) ||\n null\n );\n },\n isQuestionAnsweredByUserAnswers: (questionId) => {\n const { userAnswers } = get();\n const answer = userAnswers.find(\n (answer) => answer.questionId === questionId\n );\n return answer\n ? answer.optionId !== null || answer.answer !== null\n : false;\n },\n getQuestionStatusFromUserAnswers: (questionId) => {\n const { userAnswers } = get();\n const answer = userAnswers.find(\n (answer) => answer.questionId === questionId\n );\n if (!answer) return 'unanswered';\n if (answer.optionId === null) return 'skipped';\n return 'answered';\n },\n getUserAnswersForActivity: () => {\n const { userAnswers } = get();\n return userAnswers;\n },\n setCurrentQuestion: (question) => {\n const { getActiveQuiz } = get();\n const activeQuiz = getActiveQuiz();\n if (!activeQuiz) return;\n\n const questionIndex = activeQuiz.quiz.questions.findIndex(\n (q) => q.id === question.id\n );\n\n // Validate that the question was found before updating currentQuestionIndex\n if (questionIndex === -1) {\n console.warn(\n `Question with id \"${question.id}\" not found in active quiz`\n );\n return;\n }\n\n set({ currentQuestionIndex: questionIndex });\n },\n\n setAnswerStatus: (questionId, status) => {\n const { userAnswers } = get();\n const existingAnswerIndex = userAnswers.findIndex(\n (answer) => answer.questionId === questionId\n );\n\n if (existingAnswerIndex !== -1) {\n const updatedUserAnswers = [...userAnswers];\n updatedUserAnswers[existingAnswerIndex] = {\n ...updatedUserAnswers[existingAnswerIndex],\n answerStatus: status,\n };\n set({ userAnswers: updatedUserAnswers });\n }\n },\n\n getAnswerStatus: (questionId) => {\n const { userAnswers } = get();\n const userAnswer = userAnswers.find(\n (answer) => answer.questionId === questionId\n );\n return userAnswer ? userAnswer.answerStatus : null;\n },\n getQuestionIndex: (questionId) => {\n const { getActiveQuiz } = get();\n const activeQuiz = getActiveQuiz();\n if (!activeQuiz) return 0;\n\n const questionIndex = activeQuiz.quiz.questions.findIndex(\n (q) => q.id === questionId\n );\n return questionIndex + 1;\n },\n\n // Question Result\n getQuestionResultByQuestionId: (questionId) => {\n const { questionsResult } = get();\n return (\n questionsResult?.answers.find(\n (answer) => answer.questionId === questionId\n ) || null\n );\n },\n getQuestionResultStatistics: () => {\n const { questionsResult } = get();\n return questionsResult?.statistics || null;\n },\n getQuestionResult: () => {\n const { questionsResult } = get();\n return questionsResult;\n },\n setQuestionsResult: (questionsResult) => {\n set({ questionsResult });\n },\n setCurrentQuestionResult: (currentQuestionResult) => {\n set({ currentQuestionResult });\n },\n getCurrentQuestionResult: () => {\n const { currentQuestionResult } = get();\n return currentQuestionResult;\n },\n };\n },\n {\n name: 'quiz-store',\n }\n )\n);\n"],"mappings":"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","names":["QUESTION_DIFFICULTY","QUESTION_TYPE","QUESTION_STATUS","ANSWER_STATUS","answer"]}
@@ -270,11 +270,11 @@ var VideoPlayer = ({
270
270
  setShowSpeedMenu(!showSpeedMenu);
271
271
  }, [showSpeedMenu]);
272
272
  const toggleCaptions = (0, import_react2.useCallback)(() => {
273
- if (!trackRef.current?.track) return;
273
+ if (!trackRef.current?.track || !subtitles) return;
274
274
  const newShowCaptions = !showCaptions;
275
275
  setShowCaptions(newShowCaptions);
276
- trackRef.current.track.mode = newShowCaptions ? "showing" : "hidden";
277
- }, [showCaptions]);
276
+ trackRef.current.track.mode = newShowCaptions && subtitles ? "showing" : "hidden";
277
+ }, [showCaptions, subtitles]);
278
278
  const handleTimeUpdate = (0, import_react2.useCallback)(() => {
279
279
  if (videoRef.current) {
280
280
  const current = videoRef.current.currentTime;
@@ -303,6 +303,11 @@ var VideoPlayer = ({
303
303
  setDuration(videoRef.current.duration);
304
304
  }
305
305
  }, []);
306
+ (0, import_react2.useEffect)(() => {
307
+ if (trackRef.current?.track) {
308
+ trackRef.current.track.mode = showCaptions && subtitles ? "showing" : "hidden";
309
+ }
310
+ }, [subtitles, showCaptions]);
306
311
  (0, import_react2.useEffect)(() => {
307
312
  const handleVisibilityChange = () => {
308
313
  if (document.hidden && isPlaying && videoRef.current) {
@@ -408,9 +413,9 @@ var VideoPlayer = ({
408
413
  {
409
414
  ref: trackRef,
410
415
  kind: "captions",
411
- src: subtitles || "data:text/vtt;charset=utf-8,WEBVTT%0A%0ANOTE%20No%20captions%20available",
412
- srcLang: "en",
413
- label: subtitles ? "Subtitles" : "No captions available",
416
+ src: subtitles || "data:text/vtt;charset=utf-8,WEBVTT",
417
+ srcLang: "pt-br",
418
+ label: subtitles ? "Legendas em Portugu\xEAs" : "Sem legendas dispon\xEDveis",
414
419
  default: false
415
420
  }
416
421
  )
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/components/VideoPlayer/VideoPlayer.tsx","../../src/utils/utils.ts","../../src/components/IconButton/IconButton.tsx","../../src/components/Text/Text.tsx"],"sourcesContent":["import { useRef, useState, useEffect, useCallback } from 'react';\nimport {\n Play,\n Pause,\n SpeakerHigh,\n SpeakerSlash,\n ArrowsOutSimple,\n ArrowsInSimple,\n ClosedCaptioning,\n DotsThreeVertical,\n} from 'phosphor-react';\nimport { cn } from '../../utils/utils';\nimport IconButton from '../IconButton/IconButton';\nimport Text from '../Text/Text';\n\n/**\n * VideoPlayer component props interface\n */\ninterface VideoPlayerProps {\n /** Video source URL */\n src: string;\n /** Video poster/thumbnail URL */\n poster?: string;\n /** Subtitles URL */\n subtitles?: string;\n /** Video title */\n title?: string;\n /** Video subtitle/description */\n subtitle?: string;\n /** Initial playback time in seconds */\n initialTime?: number;\n /** Callback fired when video time updates (seconds) */\n onTimeUpdate?: (seconds: number) => void;\n /** Callback fired with progress percentage (0-100) */\n onProgress?: (progress: number) => void;\n /** Callback fired when video completes (>95% watched) */\n onVideoComplete?: () => void;\n /** Additional CSS classes */\n className?: string;\n /** Auto-save progress to localStorage */\n autoSave?: boolean;\n /** localStorage key for saving progress */\n storageKey?: string;\n}\n\n/**\n * Format seconds to MM:SS display format\n * @param seconds - Time in seconds\n * @returns Formatted time string\n */\nconst formatTime = (seconds: number): string => {\n if (!seconds || isNaN(seconds)) return '0:00';\n const mins = Math.floor(seconds / 60);\n const secs = Math.floor(seconds % 60);\n return `${mins}:${secs.toString().padStart(2, '0')}`;\n};\n\n/**\n * Video player component with controls and progress tracking\n * Integrates with backend lesson progress system\n *\n * @param props - VideoPlayer component props\n * @returns Video player element with controls\n */\nconst VideoPlayer = ({\n src,\n poster,\n subtitles,\n title,\n subtitle: subtitleText,\n initialTime = 0,\n onTimeUpdate,\n onProgress,\n onVideoComplete,\n className,\n autoSave = true,\n storageKey = 'video-progress',\n}: VideoPlayerProps) => {\n const videoRef = useRef<HTMLVideoElement>(null);\n const [isPlaying, setIsPlaying] = useState(false);\n const [currentTime, setCurrentTime] = useState(0);\n const [duration, setDuration] = useState(0);\n const [isMuted, setIsMuted] = useState(false);\n const [volume, setVolume] = useState(1);\n const [isFullscreen, setIsFullscreen] = useState(false);\n const [showControls, setShowControls] = useState(true);\n const [hasCompleted, setHasCompleted] = useState(false);\n const [showCaptions, setShowCaptions] = useState(false);\n const [playbackRate, setPlaybackRate] = useState(1);\n const [showSpeedMenu, setShowSpeedMenu] = useState(false);\n const lastSaveTimeRef = useRef(0);\n const trackRef = useRef<HTMLTrackElement>(null);\n\n /**\n * Initialize video element properties\n */\n useEffect(() => {\n // Set initial volume\n if (videoRef.current) {\n videoRef.current.volume = volume;\n videoRef.current.muted = isMuted;\n }\n }, [volume, isMuted]);\n\n /**\n * Load saved progress from localStorage\n */\n useEffect(() => {\n if (!autoSave || !storageKey) return;\n\n const raw = localStorage.getItem(`${storageKey}-${src}`);\n const saved = raw !== null ? Number(raw) : NaN;\n const hasValidSaved = Number.isFinite(saved) && saved >= 0;\n const hasValidInitial = Number.isFinite(initialTime) && initialTime >= 0;\n\n let start: number | undefined;\n if (hasValidInitial) {\n start = initialTime;\n } else if (hasValidSaved) {\n start = saved;\n } else {\n start = undefined;\n }\n if (start !== undefined && videoRef.current) {\n videoRef.current.currentTime = start;\n setCurrentTime(start);\n }\n }, [src, storageKey, autoSave, initialTime]);\n\n /**\n * Save progress to localStorage periodically\n */\n const saveProgress = useCallback(() => {\n if (!autoSave || !storageKey) return;\n\n const now = Date.now();\n if (now - lastSaveTimeRef.current > 5000) {\n localStorage.setItem(`${storageKey}-${src}`, currentTime.toString());\n lastSaveTimeRef.current = now;\n }\n }, [autoSave, storageKey, src, currentTime]);\n\n /**\n * Handle play/pause toggle\n */\n const togglePlayPause = useCallback(() => {\n if (videoRef.current) {\n if (isPlaying) {\n videoRef.current.pause();\n } else {\n videoRef.current.play();\n }\n setIsPlaying(!isPlaying);\n }\n }, [isPlaying]);\n\n /**\n * Handle volume change\n */\n const handleVolumeChange = useCallback(\n (newVolume: number) => {\n if (videoRef.current) {\n const volumeValue = newVolume / 100; // Convert 0-100 to 0-1\n videoRef.current.volume = volumeValue;\n setVolume(volumeValue);\n\n // Auto mute/unmute based on volume\n if (volumeValue === 0) {\n videoRef.current.muted = true;\n setIsMuted(true);\n } else if (isMuted) {\n videoRef.current.muted = false;\n setIsMuted(false);\n }\n }\n },\n [isMuted]\n );\n\n /**\n * Handle mute toggle\n */\n const toggleMute = useCallback(() => {\n if (videoRef.current) {\n if (isMuted) {\n // Unmute: restore volume or set to 50% if it was 0\n const restoreVolume = volume > 0 ? volume : 0.5;\n videoRef.current.volume = restoreVolume;\n videoRef.current.muted = false;\n setVolume(restoreVolume);\n setIsMuted(false);\n } else {\n // Mute: set volume to 0 and mute\n videoRef.current.muted = true;\n setIsMuted(true);\n }\n }\n }, [isMuted, volume]);\n\n /**\n * Handle fullscreen toggle\n */\n const toggleFullscreen = useCallback(() => {\n const container = videoRef.current?.parentElement;\n if (!container) return;\n\n if (!isFullscreen) {\n if (container.requestFullscreen) {\n container.requestFullscreen();\n }\n } else if (document.exitFullscreen) {\n document.exitFullscreen();\n }\n setIsFullscreen(!isFullscreen);\n }, [isFullscreen]);\n\n /**\n * Handle playback speed change\n */\n const handleSpeedChange = useCallback((speed: number) => {\n if (videoRef.current) {\n videoRef.current.playbackRate = speed;\n setPlaybackRate(speed);\n setShowSpeedMenu(false);\n }\n }, []);\n\n /**\n * Toggle speed menu visibility\n */\n const toggleSpeedMenu = useCallback(() => {\n setShowSpeedMenu(!showSpeedMenu);\n }, [showSpeedMenu]);\n\n /**\n * Toggle captions visibility\n */\n const toggleCaptions = useCallback(() => {\n if (!trackRef.current?.track) return;\n\n const newShowCaptions = !showCaptions;\n setShowCaptions(newShowCaptions);\n\n // Control track mode programmatically\n trackRef.current.track.mode = newShowCaptions ? 'showing' : 'hidden';\n }, [showCaptions]);\n\n /**\n * Handle time update\n */\n const handleTimeUpdate = useCallback(() => {\n if (videoRef.current) {\n const current = videoRef.current.currentTime;\n setCurrentTime(current);\n\n // Save progress periodically\n saveProgress();\n\n // Fire callbacks\n onTimeUpdate?.(current);\n\n if (duration > 0) {\n const progressPercent = (current / duration) * 100;\n onProgress?.(progressPercent);\n\n // Check for completion (>95% watched)\n if (progressPercent >= 95 && !hasCompleted) {\n setHasCompleted(true);\n onVideoComplete?.();\n }\n }\n }\n }, [\n duration,\n saveProgress,\n onTimeUpdate,\n onProgress,\n onVideoComplete,\n hasCompleted,\n ]);\n\n /**\n * Handle loaded metadata\n */\n const handleLoadedMetadata = useCallback(() => {\n if (videoRef.current) {\n setDuration(videoRef.current.duration);\n }\n }, []);\n\n /**\n * Handle visibility change and blur to pause video when losing focus\n */\n useEffect(() => {\n const handleVisibilityChange = () => {\n if (document.hidden && isPlaying && videoRef.current) {\n videoRef.current.pause();\n setIsPlaying(false);\n }\n };\n\n const handleBlur = () => {\n if (isPlaying && videoRef.current) {\n videoRef.current.pause();\n setIsPlaying(false);\n }\n };\n\n document.addEventListener('visibilitychange', handleVisibilityChange);\n window.addEventListener('blur', handleBlur);\n\n return () => {\n document.removeEventListener('visibilitychange', handleVisibilityChange);\n window.removeEventListener('blur', handleBlur);\n };\n }, [isPlaying]);\n\n const progressPercentage = duration > 0 ? (currentTime / duration) * 100 : 0;\n\n return (\n <div className={cn('flex flex-col', className)}>\n {/* Integrated Header */}\n {(title || subtitleText) && (\n <div className=\"bg-subject-1 rounded-t-xl px-8 py-4 flex items-end justify-between min-h-20\">\n <div className=\"flex flex-col gap-1\">\n {title && (\n <Text\n as=\"h2\"\n size=\"lg\"\n weight=\"bold\"\n color=\"text-text-900\"\n className=\"leading-5 tracking-wide\"\n >\n {title}\n </Text>\n )}\n {subtitleText && (\n <Text\n as=\"p\"\n size=\"sm\"\n weight=\"normal\"\n color=\"text-text-600\"\n className=\"leading-5\"\n >\n {subtitleText}\n </Text>\n )}\n </div>\n </div>\n )}\n\n {/* Video Container */}\n <div\n className={cn(\n 'relative w-full bg-background overflow-hidden group',\n title || subtitleText ? 'rounded-b-xl' : 'rounded-xl'\n )}\n >\n {/* Video Element */}\n <video\n ref={videoRef}\n src={src}\n poster={poster}\n className=\"w-full h-full object-contain\"\n controlsList=\"nodownload\"\n onTimeUpdate={handleTimeUpdate}\n onLoadedMetadata={handleLoadedMetadata}\n onClick={togglePlayPause}\n onKeyDown={(e) => {\n // Show controls when any key is pressed\n if (e.key) {\n setShowControls(true);\n }\n // Space or Enter to play/pause\n if (e.key === ' ' || e.key === 'Enter') {\n e.preventDefault();\n togglePlayPause();\n }\n // Arrow keys for seeking\n if (e.key === 'ArrowLeft' && videoRef.current) {\n e.preventDefault();\n videoRef.current.currentTime -= 10;\n }\n if (e.key === 'ArrowRight' && videoRef.current) {\n e.preventDefault();\n videoRef.current.currentTime += 10;\n }\n // Arrow keys for volume\n if (e.key === 'ArrowUp') {\n e.preventDefault();\n handleVolumeChange(Math.min(100, volume * 100 + 10));\n }\n if (e.key === 'ArrowDown') {\n e.preventDefault();\n handleVolumeChange(Math.max(0, volume * 100 - 10));\n }\n // M for mute\n if (e.key === 'm' || e.key === 'M') {\n e.preventDefault();\n toggleMute();\n }\n // F for fullscreen\n if (e.key === 'f' || e.key === 'F') {\n e.preventDefault();\n toggleFullscreen();\n }\n }}\n tabIndex={0}\n aria-label={title ? `Video: ${title}` : 'Video player'}\n >\n <track\n ref={trackRef}\n kind=\"captions\"\n src={\n subtitles ||\n 'data:text/vtt;charset=utf-8,WEBVTT%0A%0ANOTE%20No%20captions%20available'\n }\n srcLang=\"en\"\n label={subtitles ? 'Subtitles' : 'No captions available'}\n default={false}\n />\n </video>\n\n {/* Center Play Button */}\n {!isPlaying && (\n <div className=\"absolute inset-0 flex items-center justify-center bg-black/30 transition-opacity\">\n <IconButton\n icon={<Play size={32} weight=\"regular\" className=\"ml-1\" />}\n onClick={togglePlayPause}\n aria-label=\"Play video\"\n className=\"!bg-transparent !text-white !w-auto !h-auto hover:!bg-transparent hover:!text-gray-200\"\n />\n </div>\n )}\n\n {/* Top Controls */}\n <div\n className={cn(\n 'absolute top-0 left-0 right-0 p-4 bg-gradient-to-b from-black/70 to-transparent transition-opacity',\n !isPlaying || showControls\n ? 'opacity-100'\n : 'opacity-0 group-hover:opacity-100'\n )}\n >\n <div className=\"ml-auto block\">\n <IconButton\n icon={\n isFullscreen ? (\n <ArrowsInSimple size={24} />\n ) : (\n <ArrowsOutSimple size={24} />\n )\n }\n onClick={toggleFullscreen}\n aria-label={isFullscreen ? 'Exit fullscreen' : 'Enter fullscreen'}\n className=\"!bg-transparent !text-white hover:!bg-white/20\"\n />\n </div>\n </div>\n\n {/* Bottom Controls */}\n <div\n className={cn(\n 'absolute bottom-0 left-0 right-0 bg-gradient-to-t from-black/90 to-transparent transition-opacity',\n !isPlaying || showControls\n ? 'opacity-100'\n : 'opacity-0 group-hover:opacity-100'\n )}\n >\n {/* Progress Bar */}\n <div className=\"px-4 pb-2\">\n <input\n type=\"range\"\n min={0}\n max={duration || 100}\n value={currentTime}\n onChange={(e) => {\n const newTime = parseFloat(e.target.value);\n if (videoRef.current) {\n videoRef.current.currentTime = newTime;\n }\n }}\n className=\"w-full h-1 bg-neutral-600 rounded-full appearance-none cursor-pointer slider:bg-primary-600 focus:outline-none focus:ring-2 focus:ring-primary-500\"\n aria-label=\"Video progress\"\n style={{\n background: `linear-gradient(to right, #2271C4 ${progressPercentage}%, #D5D4D4 ${progressPercentage}%)`,\n }}\n />\n </div>\n\n {/* Control Buttons */}\n <div className=\"flex items-center justify-between px-4 pb-4\">\n {/* Left Controls */}\n <div className=\"flex items-center gap-4\">\n {/* Play/Pause */}\n <IconButton\n icon={isPlaying ? <Pause size={24} /> : <Play size={24} />}\n onClick={togglePlayPause}\n aria-label={isPlaying ? 'Pause' : 'Play'}\n className=\"!bg-transparent !text-white hover:!bg-white/20\"\n />\n\n {/* Volume */}\n <div className=\"flex items-center gap-2\">\n <IconButton\n icon={\n isMuted ? (\n <SpeakerSlash size={24} />\n ) : (\n <SpeakerHigh size={24} />\n )\n }\n onClick={toggleMute}\n aria-label={isMuted ? 'Unmute' : 'Mute'}\n className=\"!bg-transparent !text-white hover:!bg-white/20\"\n />\n\n {/* Volume Slider */}\n <input\n type=\"range\"\n min={0}\n max={100}\n value={Math.round(volume * 100)}\n onChange={(e) => handleVolumeChange(parseInt(e.target.value))}\n className=\"w-20 h-1 bg-neutral-600 rounded-full appearance-none cursor-pointer focus:outline-none focus:ring-2 focus:ring-primary-500\"\n aria-label=\"Volume control\"\n style={{\n background: `linear-gradient(to right, #2271C4 ${volume * 100}%, #D5D4D4 ${volume * 100}%)`,\n }}\n />\n </div>\n\n {/* Captions */}\n {subtitles && (\n <IconButton\n icon={<ClosedCaptioning size={24} />}\n onClick={toggleCaptions}\n aria-label={showCaptions ? 'Hide captions' : 'Show captions'}\n className={cn(\n '!bg-transparent hover:!bg-white/20',\n showCaptions ? '!text-primary-400' : '!text-white'\n )}\n />\n )}\n\n {/* Time Display */}\n <Text size=\"sm\" weight=\"medium\" color=\"text-white\">\n {formatTime(currentTime)} / {formatTime(duration)}\n </Text>\n </div>\n\n {/* Right Controls */}\n <div className=\"flex items-center gap-4\">\n {/* Speed Control */}\n <div className=\"relative\">\n <IconButton\n icon={<DotsThreeVertical size={24} />}\n onClick={toggleSpeedMenu}\n aria-label=\"Playback speed\"\n className=\"!bg-transparent !text-white hover:!bg-white/20\"\n />\n {showSpeedMenu && (\n <div className=\"absolute bottom-12 right-0 bg-black/90 rounded-lg p-2 min-w-20\">\n {[0.5, 0.75, 1, 1.25, 1.5, 2].map((speed) => (\n <button\n key={speed}\n onClick={() => handleSpeedChange(speed)}\n className={`block w-full text-left px-3 py-1 text-sm rounded hover:bg-white/20 transition-colors ${\n playbackRate === speed\n ? 'text-primary-400'\n : 'text-white'\n }`}\n >\n {speed}x\n </button>\n ))}\n </div>\n )}\n </div>\n </div>\n </div>\n </div>\n </div>\n </div>\n );\n};\n\nexport default VideoPlayer;\n","import { clsx, type ClassValue } from 'clsx';\nimport { twMerge } from 'tailwind-merge';\n\nexport function cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs));\n}\n","import { ButtonHTMLAttributes, ReactNode, forwardRef } from 'react';\nimport { cn } from '../../utils/utils';\n\n/**\n * IconButton component props interface\n */\nexport type IconButtonProps = {\n /** Ícone a ser exibido no botão */\n icon: ReactNode;\n /** Tamanho do botão */\n size?: 'sm' | 'md';\n /** Estado de seleção/ativo do botão - permanece ativo até ser clicado novamente ou outro botão ser ativado */\n active?: boolean;\n /** Additional CSS classes to apply */\n className?: string;\n} & ButtonHTMLAttributes<HTMLButtonElement>;\n\n/**\n * IconButton component for Analytica Ensino platforms\n *\n * Um botão compacto apenas com ícone, ideal para menus dropdown,\n * barras de ferramentas e ações secundárias.\n * Oferece dois tamanhos com estilo consistente.\n * Estado ativo permanece até ser clicado novamente ou outro botão ser ativado.\n * Suporta forwardRef para acesso programático ao elemento DOM.\n *\n * @param icon - O ícone a ser exibido no botão\n * @param size - Tamanho do botão (sm, md)\n * @param active - Estado ativo/selecionado do botão\n * @param className - Classes CSS adicionais\n * @param props - Todos os outros atributos HTML padrão de button\n * @returns Um elemento button compacto estilizado apenas com ícone\n *\n * @example\n * ```tsx\n * <IconButton\n * icon={<MoreVerticalIcon />}\n * size=\"sm\"\n * onClick={() => openMenu()}\n * />\n * ```\n *\n * @example\n * ```tsx\n * // Botão ativo em uma barra de ferramentas - permanece ativo até outro clique\n * <IconButton\n * icon={<BoldIcon />}\n * active={isBold}\n * onClick={toggleBold}\n * />\n * ```\n *\n * @example\n * ```tsx\n * // Usando ref para controle programático\n * const buttonRef = useRef<HTMLButtonElement>(null);\n *\n * <IconButton\n * ref={buttonRef}\n * icon={<EditIcon />}\n * size=\"md\"\n * onClick={() => startEditing()}\n * />\n * ```\n */\nconst IconButton = forwardRef<HTMLButtonElement, IconButtonProps>(\n (\n { icon, size = 'md', active = false, className = '', disabled, ...props },\n ref\n ) => {\n // Classes base para todos os estados\n const baseClasses = [\n 'inline-flex',\n 'items-center',\n 'justify-center',\n 'rounded-lg',\n 'font-medium',\n 'bg-transparent',\n 'text-text-950',\n 'cursor-pointer',\n 'hover:bg-primary-600',\n 'hover:text-text',\n 'focus-visible:outline-none',\n 'focus-visible:ring-2',\n 'focus-visible:ring-offset-0',\n 'focus-visible:ring-indicator-info',\n 'disabled:opacity-50',\n 'disabled:cursor-not-allowed',\n 'disabled:pointer-events-none',\n ];\n\n // Classes de tamanho\n const sizeClasses = {\n sm: ['w-6', 'h-6', 'text-sm'],\n md: ['w-10', 'h-10', 'text-base'],\n };\n\n // Classes de estado ativo\n const activeClasses = active\n ? ['!bg-primary-50', '!text-primary-950', 'hover:!bg-primary-100']\n : [];\n\n const allClasses = [\n ...baseClasses,\n ...sizeClasses[size],\n ...activeClasses,\n ].join(' ');\n\n // Garantir acessibilidade com aria-label padrão\n const ariaLabel = props['aria-label'] ?? 'Botão de ação';\n\n return (\n <button\n ref={ref}\n type=\"button\"\n className={cn(allClasses, className)}\n disabled={disabled}\n aria-pressed={active}\n aria-label={ariaLabel}\n {...props}\n >\n <span className=\"flex items-center justify-center\">{icon}</span>\n </button>\n );\n }\n);\n\nIconButton.displayName = 'IconButton';\n\nexport default IconButton;\n","import { ComponentPropsWithoutRef, ElementType, ReactNode } from 'react';\nimport { cn } from '../../utils/utils';\n\n/**\n * Base text component props\n */\ntype BaseTextProps = {\n /** Content to be displayed */\n children?: ReactNode;\n /** Text size variant */\n size?:\n | '2xs'\n | 'xs'\n | 'sm'\n | 'md'\n | 'lg'\n | 'xl'\n | '2xl'\n | '3xl'\n | '4xl'\n | '5xl'\n | '6xl';\n /** Font weight variant */\n weight?:\n | 'hairline'\n | 'light'\n | 'normal'\n | 'medium'\n | 'semibold'\n | 'bold'\n | 'extrabold'\n | 'black';\n /** Color variant - white for light backgrounds, black for dark backgrounds */\n color?: string;\n /** Additional CSS classes to apply */\n className?: string;\n};\n\n/**\n * Polymorphic text component props that ensures type safety based on the 'as' prop\n */\ntype TextProps<T extends ElementType = 'p'> = BaseTextProps & {\n /** HTML tag to render */\n as?: T;\n} & Omit<ComponentPropsWithoutRef<T>, keyof BaseTextProps>;\n\n/**\n * Text component for Analytica Ensino platforms\n *\n * A flexible polymorphic text component with multiple sizes, weights, and colors.\n * Automatically adapts to dark and light themes with full type safety.\n *\n * @param children - The content to display\n * @param size - The text size variant (2xs, xs, sm, md, lg, xl, 2xl, 3xl, 4xl, 5xl, 6xl)\n * @param weight - The font weight variant (hairline, light, normal, medium, semibold, bold, extrabold, black)\n * @param color - The color variant - adapts to theme\n * @param as - The HTML tag to render - determines allowed attributes via TypeScript\n * @param className - Additional CSS classes\n * @param props - HTML attributes valid for the chosen tag only\n * @returns A styled text element with type-safe attributes\n *\n * @example\n * ```tsx\n * <Text size=\"lg\" weight=\"bold\" color=\"text-info-800\">\n * This is a large, bold text\n * </Text>\n *\n * <Text as=\"a\" href=\"/link\" target=\"_blank\">\n * Link with type-safe anchor attributes\n * </Text>\n *\n * <Text as=\"button\" onClick={handleClick} disabled>\n * Button with type-safe button attributes\n * </Text>\n * ```\n */\nconst Text = <T extends ElementType = 'p'>({\n children,\n size = 'md',\n weight = 'normal',\n color = 'text-text-950',\n as,\n className = '',\n ...props\n}: TextProps<T>) => {\n let sizeClasses = '';\n let weightClasses = '';\n\n // Text size classes mapping\n const sizeClassMap = {\n '2xs': 'text-2xs',\n xs: 'text-xs',\n sm: 'text-sm',\n md: 'text-md',\n lg: 'text-lg',\n xl: 'text-xl',\n '2xl': 'text-2xl',\n '3xl': 'text-3xl',\n '4xl': 'text-4xl',\n '5xl': 'text-5xl',\n '6xl': 'text-6xl',\n } as const;\n\n sizeClasses = sizeClassMap[size] ?? sizeClassMap.md;\n\n // Font weight classes mapping\n const weightClassMap = {\n hairline: 'font-hairline',\n light: 'font-light',\n normal: 'font-normal',\n medium: 'font-medium',\n semibold: 'font-semibold',\n bold: 'font-bold',\n extrabold: 'font-extrabold',\n black: 'font-black',\n } as const;\n\n weightClasses = weightClassMap[weight] ?? weightClassMap.normal;\n\n const baseClasses = 'font-primary';\n const Component = as ?? ('p' as ElementType);\n\n return (\n <Component\n className={cn(baseClasses, sizeClasses, weightClasses, color, className)}\n {...props}\n >\n {children}\n </Component>\n );\n};\n\nexport default Text;\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAAAA,gBAAyD;AACzD,4BASO;;;ACVP,kBAAsC;AACtC,4BAAwB;AAEjB,SAAS,MAAM,QAAsB;AAC1C,aAAO,mCAAQ,kBAAK,MAAM,CAAC;AAC7B;;;ACLA,mBAA4D;AAyHpD;AAxDR,IAAM,iBAAa;AAAA,EACjB,CACE,EAAE,MAAM,OAAO,MAAM,SAAS,OAAO,YAAY,IAAI,UAAU,GAAG,MAAM,GACxE,QACG;AAEH,UAAM,cAAc;AAAA,MAClB;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAGA,UAAM,cAAc;AAAA,MAClB,IAAI,CAAC,OAAO,OAAO,SAAS;AAAA,MAC5B,IAAI,CAAC,QAAQ,QAAQ,WAAW;AAAA,IAClC;AAGA,UAAM,gBAAgB,SAClB,CAAC,kBAAkB,qBAAqB,uBAAuB,IAC/D,CAAC;AAEL,UAAM,aAAa;AAAA,MACjB,GAAG;AAAA,MACH,GAAG,YAAY,IAAI;AAAA,MACnB,GAAG;AAAA,IACL,EAAE,KAAK,GAAG;AAGV,UAAM,YAAY,MAAM,YAAY,KAAK;AAEzC,WACE;AAAA,MAAC;AAAA;AAAA,QACC;AAAA,QACA,MAAK;AAAA,QACL,WAAW,GAAG,YAAY,SAAS;AAAA,QACnC;AAAA,QACA,gBAAc;AAAA,QACd,cAAY;AAAA,QACX,GAAG;AAAA,QAEJ,sDAAC,UAAK,WAAU,oCAAoC,gBAAK;AAAA;AAAA,IAC3D;AAAA,EAEJ;AACF;AAEA,WAAW,cAAc;AAEzB,IAAO,qBAAQ;;;ACNX,IAAAC,sBAAA;AA/CJ,IAAM,OAAO,CAA8B;AAAA,EACzC;AAAA,EACA,OAAO;AAAA,EACP,SAAS;AAAA,EACT,QAAQ;AAAA,EACR;AAAA,EACA,YAAY;AAAA,EACZ,GAAG;AACL,MAAoB;AAClB,MAAI,cAAc;AAClB,MAAI,gBAAgB;AAGpB,QAAM,eAAe;AAAA,IACnB,OAAO;AAAA,IACP,IAAI;AAAA,IACJ,IAAI;AAAA,IACJ,IAAI;AAAA,IACJ,IAAI;AAAA,IACJ,IAAI;AAAA,IACJ,OAAO;AAAA,IACP,OAAO;AAAA,IACP,OAAO;AAAA,IACP,OAAO;AAAA,IACP,OAAO;AAAA,EACT;AAEA,gBAAc,aAAa,IAAI,KAAK,aAAa;AAGjD,QAAM,iBAAiB;AAAA,IACrB,UAAU;AAAA,IACV,OAAO;AAAA,IACP,QAAQ;AAAA,IACR,QAAQ;AAAA,IACR,UAAU;AAAA,IACV,MAAM;AAAA,IACN,WAAW;AAAA,IACX,OAAO;AAAA,EACT;AAEA,kBAAgB,eAAe,MAAM,KAAK,eAAe;AAEzD,QAAM,cAAc;AACpB,QAAM,YAAY,MAAO;AAEzB,SACE;AAAA,IAAC;AAAA;AAAA,MACC,WAAW,GAAG,aAAa,aAAa,eAAe,OAAO,SAAS;AAAA,MACtE,GAAG;AAAA,MAEH;AAAA;AAAA,EACH;AAEJ;AAEA,IAAO,eAAQ;;;AHgML,IAAAC,sBAAA;AAlRV,IAAM,aAAa,CAAC,YAA4B;AAC9C,MAAI,CAAC,WAAW,MAAM,OAAO,EAAG,QAAO;AACvC,QAAM,OAAO,KAAK,MAAM,UAAU,EAAE;AACpC,QAAM,OAAO,KAAK,MAAM,UAAU,EAAE;AACpC,SAAO,GAAG,IAAI,IAAI,KAAK,SAAS,EAAE,SAAS,GAAG,GAAG,CAAC;AACpD;AASA,IAAM,cAAc,CAAC;AAAA,EACnB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,UAAU;AAAA,EACV,cAAc;AAAA,EACd;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,WAAW;AAAA,EACX,aAAa;AACf,MAAwB;AACtB,QAAM,eAAW,sBAAyB,IAAI;AAC9C,QAAM,CAAC,WAAW,YAAY,QAAI,wBAAS,KAAK;AAChD,QAAM,CAAC,aAAa,cAAc,QAAI,wBAAS,CAAC;AAChD,QAAM,CAAC,UAAU,WAAW,QAAI,wBAAS,CAAC;AAC1C,QAAM,CAAC,SAAS,UAAU,QAAI,wBAAS,KAAK;AAC5C,QAAM,CAAC,QAAQ,SAAS,QAAI,wBAAS,CAAC;AACtC,QAAM,CAAC,cAAc,eAAe,QAAI,wBAAS,KAAK;AACtD,QAAM,CAAC,cAAc,eAAe,QAAI,wBAAS,IAAI;AACrD,QAAM,CAAC,cAAc,eAAe,QAAI,wBAAS,KAAK;AACtD,QAAM,CAAC,cAAc,eAAe,QAAI,wBAAS,KAAK;AACtD,QAAM,CAAC,cAAc,eAAe,QAAI,wBAAS,CAAC;AAClD,QAAM,CAAC,eAAe,gBAAgB,QAAI,wBAAS,KAAK;AACxD,QAAM,sBAAkB,sBAAO,CAAC;AAChC,QAAM,eAAW,sBAAyB,IAAI;AAK9C,+BAAU,MAAM;AAEd,QAAI,SAAS,SAAS;AACpB,eAAS,QAAQ,SAAS;AAC1B,eAAS,QAAQ,QAAQ;AAAA,IAC3B;AAAA,EACF,GAAG,CAAC,QAAQ,OAAO,CAAC;AAKpB,+BAAU,MAAM;AACd,QAAI,CAAC,YAAY,CAAC,WAAY;AAE9B,UAAM,MAAM,aAAa,QAAQ,GAAG,UAAU,IAAI,GAAG,EAAE;AACvD,UAAM,QAAQ,QAAQ,OAAO,OAAO,GAAG,IAAI;AAC3C,UAAM,gBAAgB,OAAO,SAAS,KAAK,KAAK,SAAS;AACzD,UAAM,kBAAkB,OAAO,SAAS,WAAW,KAAK,eAAe;AAEvE,QAAI;AACJ,QAAI,iBAAiB;AACnB,cAAQ;AAAA,IACV,WAAW,eAAe;AACxB,cAAQ;AAAA,IACV,OAAO;AACL,cAAQ;AAAA,IACV;AACA,QAAI,UAAU,UAAa,SAAS,SAAS;AAC3C,eAAS,QAAQ,cAAc;AAC/B,qBAAe,KAAK;AAAA,IACtB;AAAA,EACF,GAAG,CAAC,KAAK,YAAY,UAAU,WAAW,CAAC;AAK3C,QAAM,mBAAe,2BAAY,MAAM;AACrC,QAAI,CAAC,YAAY,CAAC,WAAY;AAE9B,UAAM,MAAM,KAAK,IAAI;AACrB,QAAI,MAAM,gBAAgB,UAAU,KAAM;AACxC,mBAAa,QAAQ,GAAG,UAAU,IAAI,GAAG,IAAI,YAAY,SAAS,CAAC;AACnE,sBAAgB,UAAU;AAAA,IAC5B;AAAA,EACF,GAAG,CAAC,UAAU,YAAY,KAAK,WAAW,CAAC;AAK3C,QAAM,sBAAkB,2BAAY,MAAM;AACxC,QAAI,SAAS,SAAS;AACpB,UAAI,WAAW;AACb,iBAAS,QAAQ,MAAM;AAAA,MACzB,OAAO;AACL,iBAAS,QAAQ,KAAK;AAAA,MACxB;AACA,mBAAa,CAAC,SAAS;AAAA,IACzB;AAAA,EACF,GAAG,CAAC,SAAS,CAAC;AAKd,QAAM,yBAAqB;AAAA,IACzB,CAAC,cAAsB;AACrB,UAAI,SAAS,SAAS;AACpB,cAAM,cAAc,YAAY;AAChC,iBAAS,QAAQ,SAAS;AAC1B,kBAAU,WAAW;AAGrB,YAAI,gBAAgB,GAAG;AACrB,mBAAS,QAAQ,QAAQ;AACzB,qBAAW,IAAI;AAAA,QACjB,WAAW,SAAS;AAClB,mBAAS,QAAQ,QAAQ;AACzB,qBAAW,KAAK;AAAA,QAClB;AAAA,MACF;AAAA,IACF;AAAA,IACA,CAAC,OAAO;AAAA,EACV;AAKA,QAAM,iBAAa,2BAAY,MAAM;AACnC,QAAI,SAAS,SAAS;AACpB,UAAI,SAAS;AAEX,cAAM,gBAAgB,SAAS,IAAI,SAAS;AAC5C,iBAAS,QAAQ,SAAS;AAC1B,iBAAS,QAAQ,QAAQ;AACzB,kBAAU,aAAa;AACvB,mBAAW,KAAK;AAAA,MAClB,OAAO;AAEL,iBAAS,QAAQ,QAAQ;AACzB,mBAAW,IAAI;AAAA,MACjB;AAAA,IACF;AAAA,EACF,GAAG,CAAC,SAAS,MAAM,CAAC;AAKpB,QAAM,uBAAmB,2BAAY,MAAM;AACzC,UAAM,YAAY,SAAS,SAAS;AACpC,QAAI,CAAC,UAAW;AAEhB,QAAI,CAAC,cAAc;AACjB,UAAI,UAAU,mBAAmB;AAC/B,kBAAU,kBAAkB;AAAA,MAC9B;AAAA,IACF,WAAW,SAAS,gBAAgB;AAClC,eAAS,eAAe;AAAA,IAC1B;AACA,oBAAgB,CAAC,YAAY;AAAA,EAC/B,GAAG,CAAC,YAAY,CAAC;AAKjB,QAAM,wBAAoB,2BAAY,CAAC,UAAkB;AACvD,QAAI,SAAS,SAAS;AACpB,eAAS,QAAQ,eAAe;AAChC,sBAAgB,KAAK;AACrB,uBAAiB,KAAK;AAAA,IACxB;AAAA,EACF,GAAG,CAAC,CAAC;AAKL,QAAM,sBAAkB,2BAAY,MAAM;AACxC,qBAAiB,CAAC,aAAa;AAAA,EACjC,GAAG,CAAC,aAAa,CAAC;AAKlB,QAAM,qBAAiB,2BAAY,MAAM;AACvC,QAAI,CAAC,SAAS,SAAS,MAAO;AAE9B,UAAM,kBAAkB,CAAC;AACzB,oBAAgB,eAAe;AAG/B,aAAS,QAAQ,MAAM,OAAO,kBAAkB,YAAY;AAAA,EAC9D,GAAG,CAAC,YAAY,CAAC;AAKjB,QAAM,uBAAmB,2BAAY,MAAM;AACzC,QAAI,SAAS,SAAS;AACpB,YAAM,UAAU,SAAS,QAAQ;AACjC,qBAAe,OAAO;AAGtB,mBAAa;AAGb,qBAAe,OAAO;AAEtB,UAAI,WAAW,GAAG;AAChB,cAAM,kBAAmB,UAAU,WAAY;AAC/C,qBAAa,eAAe;AAG5B,YAAI,mBAAmB,MAAM,CAAC,cAAc;AAC1C,0BAAgB,IAAI;AACpB,4BAAkB;AAAA,QACpB;AAAA,MACF;AAAA,IACF;AAAA,EACF,GAAG;AAAA,IACD;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,CAAC;AAKD,QAAM,2BAAuB,2BAAY,MAAM;AAC7C,QAAI,SAAS,SAAS;AACpB,kBAAY,SAAS,QAAQ,QAAQ;AAAA,IACvC;AAAA,EACF,GAAG,CAAC,CAAC;AAKL,+BAAU,MAAM;AACd,UAAM,yBAAyB,MAAM;AACnC,UAAI,SAAS,UAAU,aAAa,SAAS,SAAS;AACpD,iBAAS,QAAQ,MAAM;AACvB,qBAAa,KAAK;AAAA,MACpB;AAAA,IACF;AAEA,UAAM,aAAa,MAAM;AACvB,UAAI,aAAa,SAAS,SAAS;AACjC,iBAAS,QAAQ,MAAM;AACvB,qBAAa,KAAK;AAAA,MACpB;AAAA,IACF;AAEA,aAAS,iBAAiB,oBAAoB,sBAAsB;AACpE,WAAO,iBAAiB,QAAQ,UAAU;AAE1C,WAAO,MAAM;AACX,eAAS,oBAAoB,oBAAoB,sBAAsB;AACvE,aAAO,oBAAoB,QAAQ,UAAU;AAAA,IAC/C;AAAA,EACF,GAAG,CAAC,SAAS,CAAC;AAEd,QAAM,qBAAqB,WAAW,IAAK,cAAc,WAAY,MAAM;AAE3E,SACE,8CAAC,SAAI,WAAW,GAAG,iBAAiB,SAAS,GAEzC;AAAA,cAAS,iBACT,6CAAC,SAAI,WAAU,+EACb,wDAAC,SAAI,WAAU,uBACZ;AAAA,eACC;AAAA,QAAC;AAAA;AAAA,UACC,IAAG;AAAA,UACH,MAAK;AAAA,UACL,QAAO;AAAA,UACP,OAAM;AAAA,UACN,WAAU;AAAA,UAET;AAAA;AAAA,MACH;AAAA,MAED,gBACC;AAAA,QAAC;AAAA;AAAA,UACC,IAAG;AAAA,UACH,MAAK;AAAA,UACL,QAAO;AAAA,UACP,OAAM;AAAA,UACN,WAAU;AAAA,UAET;AAAA;AAAA,MACH;AAAA,OAEJ,GACF;AAAA,IAIF;AAAA,MAAC;AAAA;AAAA,QACC,WAAW;AAAA,UACT;AAAA,UACA,SAAS,eAAe,iBAAiB;AAAA,QAC3C;AAAA,QAGA;AAAA;AAAA,YAAC;AAAA;AAAA,cACC,KAAK;AAAA,cACL;AAAA,cACA;AAAA,cACA,WAAU;AAAA,cACV,cAAa;AAAA,cACb,cAAc;AAAA,cACd,kBAAkB;AAAA,cAClB,SAAS;AAAA,cACT,WAAW,CAAC,MAAM;AAEhB,oBAAI,EAAE,KAAK;AACT,kCAAgB,IAAI;AAAA,gBACtB;AAEA,oBAAI,EAAE,QAAQ,OAAO,EAAE,QAAQ,SAAS;AACtC,oBAAE,eAAe;AACjB,kCAAgB;AAAA,gBAClB;AAEA,oBAAI,EAAE,QAAQ,eAAe,SAAS,SAAS;AAC7C,oBAAE,eAAe;AACjB,2BAAS,QAAQ,eAAe;AAAA,gBAClC;AACA,oBAAI,EAAE,QAAQ,gBAAgB,SAAS,SAAS;AAC9C,oBAAE,eAAe;AACjB,2BAAS,QAAQ,eAAe;AAAA,gBAClC;AAEA,oBAAI,EAAE,QAAQ,WAAW;AACvB,oBAAE,eAAe;AACjB,qCAAmB,KAAK,IAAI,KAAK,SAAS,MAAM,EAAE,CAAC;AAAA,gBACrD;AACA,oBAAI,EAAE,QAAQ,aAAa;AACzB,oBAAE,eAAe;AACjB,qCAAmB,KAAK,IAAI,GAAG,SAAS,MAAM,EAAE,CAAC;AAAA,gBACnD;AAEA,oBAAI,EAAE,QAAQ,OAAO,EAAE,QAAQ,KAAK;AAClC,oBAAE,eAAe;AACjB,6BAAW;AAAA,gBACb;AAEA,oBAAI,EAAE,QAAQ,OAAO,EAAE,QAAQ,KAAK;AAClC,oBAAE,eAAe;AACjB,mCAAiB;AAAA,gBACnB;AAAA,cACF;AAAA,cACA,UAAU;AAAA,cACV,cAAY,QAAQ,UAAU,KAAK,KAAK;AAAA,cAExC;AAAA,gBAAC;AAAA;AAAA,kBACC,KAAK;AAAA,kBACL,MAAK;AAAA,kBACL,KACE,aACA;AAAA,kBAEF,SAAQ;AAAA,kBACR,OAAO,YAAY,cAAc;AAAA,kBACjC,SAAS;AAAA;AAAA,cACX;AAAA;AAAA,UACF;AAAA,UAGC,CAAC,aACA,6CAAC,SAAI,WAAU,oFACb;AAAA,YAAC;AAAA;AAAA,cACC,MAAM,6CAAC,8BAAK,MAAM,IAAI,QAAO,WAAU,WAAU,QAAO;AAAA,cACxD,SAAS;AAAA,cACT,cAAW;AAAA,cACX,WAAU;AAAA;AAAA,UACZ,GACF;AAAA,UAIF;AAAA,YAAC;AAAA;AAAA,cACC,WAAW;AAAA,gBACT;AAAA,gBACA,CAAC,aAAa,eACV,gBACA;AAAA,cACN;AAAA,cAEA,uDAAC,SAAI,WAAU,iBACb;AAAA,gBAAC;AAAA;AAAA,kBACC,MACE,eACE,6CAAC,wCAAe,MAAM,IAAI,IAE1B,6CAAC,yCAAgB,MAAM,IAAI;AAAA,kBAG/B,SAAS;AAAA,kBACT,cAAY,eAAe,oBAAoB;AAAA,kBAC/C,WAAU;AAAA;AAAA,cACZ,GACF;AAAA;AAAA,UACF;AAAA,UAGA;AAAA,YAAC;AAAA;AAAA,cACC,WAAW;AAAA,gBACT;AAAA,gBACA,CAAC,aAAa,eACV,gBACA;AAAA,cACN;AAAA,cAGA;AAAA,6DAAC,SAAI,WAAU,aACb;AAAA,kBAAC;AAAA;AAAA,oBACC,MAAK;AAAA,oBACL,KAAK;AAAA,oBACL,KAAK,YAAY;AAAA,oBACjB,OAAO;AAAA,oBACP,UAAU,CAAC,MAAM;AACf,4BAAM,UAAU,WAAW,EAAE,OAAO,KAAK;AACzC,0BAAI,SAAS,SAAS;AACpB,iCAAS,QAAQ,cAAc;AAAA,sBACjC;AAAA,oBACF;AAAA,oBACA,WAAU;AAAA,oBACV,cAAW;AAAA,oBACX,OAAO;AAAA,sBACL,YAAY,qCAAqC,kBAAkB,cAAc,kBAAkB;AAAA,oBACrG;AAAA;AAAA,gBACF,GACF;AAAA,gBAGA,8CAAC,SAAI,WAAU,+CAEb;AAAA,gEAAC,SAAI,WAAU,2BAEb;AAAA;AAAA,sBAAC;AAAA;AAAA,wBACC,MAAM,YAAY,6CAAC,+BAAM,MAAM,IAAI,IAAK,6CAAC,8BAAK,MAAM,IAAI;AAAA,wBACxD,SAAS;AAAA,wBACT,cAAY,YAAY,UAAU;AAAA,wBAClC,WAAU;AAAA;AAAA,oBACZ;AAAA,oBAGA,8CAAC,SAAI,WAAU,2BACb;AAAA;AAAA,wBAAC;AAAA;AAAA,0BACC,MACE,UACE,6CAAC,sCAAa,MAAM,IAAI,IAExB,6CAAC,qCAAY,MAAM,IAAI;AAAA,0BAG3B,SAAS;AAAA,0BACT,cAAY,UAAU,WAAW;AAAA,0BACjC,WAAU;AAAA;AAAA,sBACZ;AAAA,sBAGA;AAAA,wBAAC;AAAA;AAAA,0BACC,MAAK;AAAA,0BACL,KAAK;AAAA,0BACL,KAAK;AAAA,0BACL,OAAO,KAAK,MAAM,SAAS,GAAG;AAAA,0BAC9B,UAAU,CAAC,MAAM,mBAAmB,SAAS,EAAE,OAAO,KAAK,CAAC;AAAA,0BAC5D,WAAU;AAAA,0BACV,cAAW;AAAA,0BACX,OAAO;AAAA,4BACL,YAAY,qCAAqC,SAAS,GAAG,cAAc,SAAS,GAAG;AAAA,0BACzF;AAAA;AAAA,sBACF;AAAA,uBACF;AAAA,oBAGC,aACC;AAAA,sBAAC;AAAA;AAAA,wBACC,MAAM,6CAAC,0CAAiB,MAAM,IAAI;AAAA,wBAClC,SAAS;AAAA,wBACT,cAAY,eAAe,kBAAkB;AAAA,wBAC7C,WAAW;AAAA,0BACT;AAAA,0BACA,eAAe,sBAAsB;AAAA,wBACvC;AAAA;AAAA,oBACF;AAAA,oBAIF,8CAAC,gBAAK,MAAK,MAAK,QAAO,UAAS,OAAM,cACnC;AAAA,iCAAW,WAAW;AAAA,sBAAE;AAAA,sBAAI,WAAW,QAAQ;AAAA,uBAClD;AAAA,qBACF;AAAA,kBAGA,6CAAC,SAAI,WAAU,2BAEb,wDAAC,SAAI,WAAU,YACb;AAAA;AAAA,sBAAC;AAAA;AAAA,wBACC,MAAM,6CAAC,2CAAkB,MAAM,IAAI;AAAA,wBACnC,SAAS;AAAA,wBACT,cAAW;AAAA,wBACX,WAAU;AAAA;AAAA,oBACZ;AAAA,oBACC,iBACC,6CAAC,SAAI,WAAU,kEACZ,WAAC,KAAK,MAAM,GAAG,MAAM,KAAK,CAAC,EAAE,IAAI,CAAC,UACjC;AAAA,sBAAC;AAAA;AAAA,wBAEC,SAAS,MAAM,kBAAkB,KAAK;AAAA,wBACtC,WAAW,wFACT,iBAAiB,QACb,qBACA,YACN;AAAA,wBAEC;AAAA;AAAA,0BAAM;AAAA;AAAA;AAAA,sBARF;AAAA,oBASP,CACD,GACH;AAAA,qBAEJ,GACF;AAAA,mBACF;AAAA;AAAA;AAAA,UACF;AAAA;AAAA;AAAA,IACF;AAAA,KACF;AAEJ;AAEA,IAAO,sBAAQ;","names":["import_react","import_jsx_runtime","import_jsx_runtime"]}
1
+ {"version":3,"sources":["../../src/components/VideoPlayer/VideoPlayer.tsx","../../src/utils/utils.ts","../../src/components/IconButton/IconButton.tsx","../../src/components/Text/Text.tsx"],"sourcesContent":["import { useRef, useState, useEffect, useCallback } from 'react';\nimport {\n Play,\n Pause,\n SpeakerHigh,\n SpeakerSlash,\n ArrowsOutSimple,\n ArrowsInSimple,\n ClosedCaptioning,\n DotsThreeVertical,\n} from 'phosphor-react';\nimport { cn } from '../../utils/utils';\nimport IconButton from '../IconButton/IconButton';\nimport Text from '../Text/Text';\n\n/**\n * VideoPlayer component props interface\n */\ninterface VideoPlayerProps {\n /** Video source URL */\n src: string;\n /** Video poster/thumbnail URL */\n poster?: string;\n /** Subtitles URL */\n subtitles?: string;\n /** Video title */\n title?: string;\n /** Video subtitle/description */\n subtitle?: string;\n /** Initial playback time in seconds */\n initialTime?: number;\n /** Callback fired when video time updates (seconds) */\n onTimeUpdate?: (seconds: number) => void;\n /** Callback fired with progress percentage (0-100) */\n onProgress?: (progress: number) => void;\n /** Callback fired when video completes (>95% watched) */\n onVideoComplete?: () => void;\n /** Additional CSS classes */\n className?: string;\n /** Auto-save progress to localStorage */\n autoSave?: boolean;\n /** localStorage key for saving progress */\n storageKey?: string;\n}\n\n/**\n * Format seconds to MM:SS display format\n * @param seconds - Time in seconds\n * @returns Formatted time string\n */\nconst formatTime = (seconds: number): string => {\n if (!seconds || isNaN(seconds)) return '0:00';\n const mins = Math.floor(seconds / 60);\n const secs = Math.floor(seconds % 60);\n return `${mins}:${secs.toString().padStart(2, '0')}`;\n};\n\n/**\n * Video player component with controls and progress tracking\n * Integrates with backend lesson progress system\n *\n * @param props - VideoPlayer component props\n * @returns Video player element with controls\n */\nconst VideoPlayer = ({\n src,\n poster,\n subtitles,\n title,\n subtitle: subtitleText,\n initialTime = 0,\n onTimeUpdate,\n onProgress,\n onVideoComplete,\n className,\n autoSave = true,\n storageKey = 'video-progress',\n}: VideoPlayerProps) => {\n const videoRef = useRef<HTMLVideoElement>(null);\n const [isPlaying, setIsPlaying] = useState(false);\n const [currentTime, setCurrentTime] = useState(0);\n const [duration, setDuration] = useState(0);\n const [isMuted, setIsMuted] = useState(false);\n const [volume, setVolume] = useState(1);\n const [isFullscreen, setIsFullscreen] = useState(false);\n const [showControls, setShowControls] = useState(true);\n const [hasCompleted, setHasCompleted] = useState(false);\n const [showCaptions, setShowCaptions] = useState(false);\n const [playbackRate, setPlaybackRate] = useState(1);\n const [showSpeedMenu, setShowSpeedMenu] = useState(false);\n const lastSaveTimeRef = useRef(0);\n const trackRef = useRef<HTMLTrackElement>(null);\n\n /**\n * Initialize video element properties\n */\n useEffect(() => {\n // Set initial volume\n if (videoRef.current) {\n videoRef.current.volume = volume;\n videoRef.current.muted = isMuted;\n }\n }, [volume, isMuted]);\n\n /**\n * Load saved progress from localStorage\n */\n useEffect(() => {\n if (!autoSave || !storageKey) return;\n\n const raw = localStorage.getItem(`${storageKey}-${src}`);\n const saved = raw !== null ? Number(raw) : NaN;\n const hasValidSaved = Number.isFinite(saved) && saved >= 0;\n const hasValidInitial = Number.isFinite(initialTime) && initialTime >= 0;\n\n let start: number | undefined;\n if (hasValidInitial) {\n start = initialTime;\n } else if (hasValidSaved) {\n start = saved;\n } else {\n start = undefined;\n }\n if (start !== undefined && videoRef.current) {\n videoRef.current.currentTime = start;\n setCurrentTime(start);\n }\n }, [src, storageKey, autoSave, initialTime]);\n\n /**\n * Save progress to localStorage periodically\n */\n const saveProgress = useCallback(() => {\n if (!autoSave || !storageKey) return;\n\n const now = Date.now();\n if (now - lastSaveTimeRef.current > 5000) {\n localStorage.setItem(`${storageKey}-${src}`, currentTime.toString());\n lastSaveTimeRef.current = now;\n }\n }, [autoSave, storageKey, src, currentTime]);\n\n /**\n * Handle play/pause toggle\n */\n const togglePlayPause = useCallback(() => {\n if (videoRef.current) {\n if (isPlaying) {\n videoRef.current.pause();\n } else {\n videoRef.current.play();\n }\n setIsPlaying(!isPlaying);\n }\n }, [isPlaying]);\n\n /**\n * Handle volume change\n */\n const handleVolumeChange = useCallback(\n (newVolume: number) => {\n if (videoRef.current) {\n const volumeValue = newVolume / 100; // Convert 0-100 to 0-1\n videoRef.current.volume = volumeValue;\n setVolume(volumeValue);\n\n // Auto mute/unmute based on volume\n if (volumeValue === 0) {\n videoRef.current.muted = true;\n setIsMuted(true);\n } else if (isMuted) {\n videoRef.current.muted = false;\n setIsMuted(false);\n }\n }\n },\n [isMuted]\n );\n\n /**\n * Handle mute toggle\n */\n const toggleMute = useCallback(() => {\n if (videoRef.current) {\n if (isMuted) {\n // Unmute: restore volume or set to 50% if it was 0\n const restoreVolume = volume > 0 ? volume : 0.5;\n videoRef.current.volume = restoreVolume;\n videoRef.current.muted = false;\n setVolume(restoreVolume);\n setIsMuted(false);\n } else {\n // Mute: set volume to 0 and mute\n videoRef.current.muted = true;\n setIsMuted(true);\n }\n }\n }, [isMuted, volume]);\n\n /**\n * Handle fullscreen toggle\n */\n const toggleFullscreen = useCallback(() => {\n const container = videoRef.current?.parentElement;\n if (!container) return;\n\n if (!isFullscreen) {\n if (container.requestFullscreen) {\n container.requestFullscreen();\n }\n } else if (document.exitFullscreen) {\n document.exitFullscreen();\n }\n setIsFullscreen(!isFullscreen);\n }, [isFullscreen]);\n\n /**\n * Handle playback speed change\n */\n const handleSpeedChange = useCallback((speed: number) => {\n if (videoRef.current) {\n videoRef.current.playbackRate = speed;\n setPlaybackRate(speed);\n setShowSpeedMenu(false);\n }\n }, []);\n\n /**\n * Toggle speed menu visibility\n */\n const toggleSpeedMenu = useCallback(() => {\n setShowSpeedMenu(!showSpeedMenu);\n }, [showSpeedMenu]);\n\n /**\n * Toggle captions visibility\n */\n const toggleCaptions = useCallback(() => {\n if (!trackRef.current?.track || !subtitles) return;\n\n const newShowCaptions = !showCaptions;\n setShowCaptions(newShowCaptions);\n\n // Control track mode programmatically - only show if subtitles are available\n trackRef.current.track.mode =\n newShowCaptions && subtitles ? 'showing' : 'hidden';\n }, [showCaptions, subtitles]);\n\n /**\n * Handle time update\n */\n const handleTimeUpdate = useCallback(() => {\n if (videoRef.current) {\n const current = videoRef.current.currentTime;\n setCurrentTime(current);\n\n // Save progress periodically\n saveProgress();\n\n // Fire callbacks\n onTimeUpdate?.(current);\n\n if (duration > 0) {\n const progressPercent = (current / duration) * 100;\n onProgress?.(progressPercent);\n\n // Check for completion (>95% watched)\n if (progressPercent >= 95 && !hasCompleted) {\n setHasCompleted(true);\n onVideoComplete?.();\n }\n }\n }\n }, [\n duration,\n saveProgress,\n onTimeUpdate,\n onProgress,\n onVideoComplete,\n hasCompleted,\n ]);\n\n /**\n * Handle loaded metadata\n */\n const handleLoadedMetadata = useCallback(() => {\n if (videoRef.current) {\n setDuration(videoRef.current.duration);\n }\n }, []);\n\n /**\n * Initialize track mode when track is available\n */\n useEffect(() => {\n if (trackRef.current?.track) {\n // Set initial mode based on showCaptions state and subtitle availability\n trackRef.current.track.mode =\n showCaptions && subtitles ? 'showing' : 'hidden';\n }\n }, [subtitles, showCaptions]);\n\n /**\n * Handle visibility change and blur to pause video when losing focus\n */\n useEffect(() => {\n const handleVisibilityChange = () => {\n if (document.hidden && isPlaying && videoRef.current) {\n videoRef.current.pause();\n setIsPlaying(false);\n }\n };\n\n const handleBlur = () => {\n if (isPlaying && videoRef.current) {\n videoRef.current.pause();\n setIsPlaying(false);\n }\n };\n\n document.addEventListener('visibilitychange', handleVisibilityChange);\n window.addEventListener('blur', handleBlur);\n\n return () => {\n document.removeEventListener('visibilitychange', handleVisibilityChange);\n window.removeEventListener('blur', handleBlur);\n };\n }, [isPlaying]);\n\n const progressPercentage = duration > 0 ? (currentTime / duration) * 100 : 0;\n\n return (\n <div className={cn('flex flex-col', className)}>\n {/* Integrated Header */}\n {(title || subtitleText) && (\n <div className=\"bg-subject-1 rounded-t-xl px-8 py-4 flex items-end justify-between min-h-20\">\n <div className=\"flex flex-col gap-1\">\n {title && (\n <Text\n as=\"h2\"\n size=\"lg\"\n weight=\"bold\"\n color=\"text-text-900\"\n className=\"leading-5 tracking-wide\"\n >\n {title}\n </Text>\n )}\n {subtitleText && (\n <Text\n as=\"p\"\n size=\"sm\"\n weight=\"normal\"\n color=\"text-text-600\"\n className=\"leading-5\"\n >\n {subtitleText}\n </Text>\n )}\n </div>\n </div>\n )}\n\n {/* Video Container */}\n <div\n className={cn(\n 'relative w-full bg-background overflow-hidden group',\n title || subtitleText ? 'rounded-b-xl' : 'rounded-xl'\n )}\n >\n {/* Video Element */}\n <video\n ref={videoRef}\n src={src}\n poster={poster}\n className=\"w-full h-full object-contain\"\n controlsList=\"nodownload\"\n onTimeUpdate={handleTimeUpdate}\n onLoadedMetadata={handleLoadedMetadata}\n onClick={togglePlayPause}\n onKeyDown={(e) => {\n // Show controls when any key is pressed\n if (e.key) {\n setShowControls(true);\n }\n // Space or Enter to play/pause\n if (e.key === ' ' || e.key === 'Enter') {\n e.preventDefault();\n togglePlayPause();\n }\n // Arrow keys for seeking\n if (e.key === 'ArrowLeft' && videoRef.current) {\n e.preventDefault();\n videoRef.current.currentTime -= 10;\n }\n if (e.key === 'ArrowRight' && videoRef.current) {\n e.preventDefault();\n videoRef.current.currentTime += 10;\n }\n // Arrow keys for volume\n if (e.key === 'ArrowUp') {\n e.preventDefault();\n handleVolumeChange(Math.min(100, volume * 100 + 10));\n }\n if (e.key === 'ArrowDown') {\n e.preventDefault();\n handleVolumeChange(Math.max(0, volume * 100 - 10));\n }\n // M for mute\n if (e.key === 'm' || e.key === 'M') {\n e.preventDefault();\n toggleMute();\n }\n // F for fullscreen\n if (e.key === 'f' || e.key === 'F') {\n e.preventDefault();\n toggleFullscreen();\n }\n }}\n tabIndex={0}\n aria-label={title ? `Video: ${title}` : 'Video player'}\n >\n <track\n ref={trackRef}\n kind=\"captions\"\n src={subtitles || 'data:text/vtt;charset=utf-8,WEBVTT'}\n srcLang=\"pt-br\"\n label={\n subtitles ? 'Legendas em Português' : 'Sem legendas disponíveis'\n }\n default={false}\n />\n </video>\n\n {/* Center Play Button */}\n {!isPlaying && (\n <div className=\"absolute inset-0 flex items-center justify-center bg-black/30 transition-opacity\">\n <IconButton\n icon={<Play size={32} weight=\"regular\" className=\"ml-1\" />}\n onClick={togglePlayPause}\n aria-label=\"Play video\"\n className=\"!bg-transparent !text-white !w-auto !h-auto hover:!bg-transparent hover:!text-gray-200\"\n />\n </div>\n )}\n\n {/* Top Controls */}\n <div\n className={cn(\n 'absolute top-0 left-0 right-0 p-4 bg-gradient-to-b from-black/70 to-transparent transition-opacity',\n !isPlaying || showControls\n ? 'opacity-100'\n : 'opacity-0 group-hover:opacity-100'\n )}\n >\n <div className=\"ml-auto block\">\n <IconButton\n icon={\n isFullscreen ? (\n <ArrowsInSimple size={24} />\n ) : (\n <ArrowsOutSimple size={24} />\n )\n }\n onClick={toggleFullscreen}\n aria-label={isFullscreen ? 'Exit fullscreen' : 'Enter fullscreen'}\n className=\"!bg-transparent !text-white hover:!bg-white/20\"\n />\n </div>\n </div>\n\n {/* Bottom Controls */}\n <div\n className={cn(\n 'absolute bottom-0 left-0 right-0 bg-gradient-to-t from-black/90 to-transparent transition-opacity',\n !isPlaying || showControls\n ? 'opacity-100'\n : 'opacity-0 group-hover:opacity-100'\n )}\n >\n {/* Progress Bar */}\n <div className=\"px-4 pb-2\">\n <input\n type=\"range\"\n min={0}\n max={duration || 100}\n value={currentTime}\n onChange={(e) => {\n const newTime = parseFloat(e.target.value);\n if (videoRef.current) {\n videoRef.current.currentTime = newTime;\n }\n }}\n className=\"w-full h-1 bg-neutral-600 rounded-full appearance-none cursor-pointer slider:bg-primary-600 focus:outline-none focus:ring-2 focus:ring-primary-500\"\n aria-label=\"Video progress\"\n style={{\n background: `linear-gradient(to right, #2271C4 ${progressPercentage}%, #D5D4D4 ${progressPercentage}%)`,\n }}\n />\n </div>\n\n {/* Control Buttons */}\n <div className=\"flex items-center justify-between px-4 pb-4\">\n {/* Left Controls */}\n <div className=\"flex items-center gap-4\">\n {/* Play/Pause */}\n <IconButton\n icon={isPlaying ? <Pause size={24} /> : <Play size={24} />}\n onClick={togglePlayPause}\n aria-label={isPlaying ? 'Pause' : 'Play'}\n className=\"!bg-transparent !text-white hover:!bg-white/20\"\n />\n\n {/* Volume */}\n <div className=\"flex items-center gap-2\">\n <IconButton\n icon={\n isMuted ? (\n <SpeakerSlash size={24} />\n ) : (\n <SpeakerHigh size={24} />\n )\n }\n onClick={toggleMute}\n aria-label={isMuted ? 'Unmute' : 'Mute'}\n className=\"!bg-transparent !text-white hover:!bg-white/20\"\n />\n\n {/* Volume Slider */}\n <input\n type=\"range\"\n min={0}\n max={100}\n value={Math.round(volume * 100)}\n onChange={(e) => handleVolumeChange(parseInt(e.target.value))}\n className=\"w-20 h-1 bg-neutral-600 rounded-full appearance-none cursor-pointer focus:outline-none focus:ring-2 focus:ring-primary-500\"\n aria-label=\"Volume control\"\n style={{\n background: `linear-gradient(to right, #2271C4 ${volume * 100}%, #D5D4D4 ${volume * 100}%)`,\n }}\n />\n </div>\n\n {/* Captions */}\n {subtitles && (\n <IconButton\n icon={<ClosedCaptioning size={24} />}\n onClick={toggleCaptions}\n aria-label={showCaptions ? 'Hide captions' : 'Show captions'}\n className={cn(\n '!bg-transparent hover:!bg-white/20',\n showCaptions ? '!text-primary-400' : '!text-white'\n )}\n />\n )}\n\n {/* Time Display */}\n <Text size=\"sm\" weight=\"medium\" color=\"text-white\">\n {formatTime(currentTime)} / {formatTime(duration)}\n </Text>\n </div>\n\n {/* Right Controls */}\n <div className=\"flex items-center gap-4\">\n {/* Speed Control */}\n <div className=\"relative\">\n <IconButton\n icon={<DotsThreeVertical size={24} />}\n onClick={toggleSpeedMenu}\n aria-label=\"Playback speed\"\n className=\"!bg-transparent !text-white hover:!bg-white/20\"\n />\n {showSpeedMenu && (\n <div className=\"absolute bottom-12 right-0 bg-black/90 rounded-lg p-2 min-w-20\">\n {[0.5, 0.75, 1, 1.25, 1.5, 2].map((speed) => (\n <button\n key={speed}\n onClick={() => handleSpeedChange(speed)}\n className={`block w-full text-left px-3 py-1 text-sm rounded hover:bg-white/20 transition-colors ${\n playbackRate === speed\n ? 'text-primary-400'\n : 'text-white'\n }`}\n >\n {speed}x\n </button>\n ))}\n </div>\n )}\n </div>\n </div>\n </div>\n </div>\n </div>\n </div>\n );\n};\n\nexport default VideoPlayer;\n","import { clsx, type ClassValue } from 'clsx';\nimport { twMerge } from 'tailwind-merge';\n\nexport function cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs));\n}\n","import { ButtonHTMLAttributes, ReactNode, forwardRef } from 'react';\nimport { cn } from '../../utils/utils';\n\n/**\n * IconButton component props interface\n */\nexport type IconButtonProps = {\n /** Ícone a ser exibido no botão */\n icon: ReactNode;\n /** Tamanho do botão */\n size?: 'sm' | 'md';\n /** Estado de seleção/ativo do botão - permanece ativo até ser clicado novamente ou outro botão ser ativado */\n active?: boolean;\n /** Additional CSS classes to apply */\n className?: string;\n} & ButtonHTMLAttributes<HTMLButtonElement>;\n\n/**\n * IconButton component for Analytica Ensino platforms\n *\n * Um botão compacto apenas com ícone, ideal para menus dropdown,\n * barras de ferramentas e ações secundárias.\n * Oferece dois tamanhos com estilo consistente.\n * Estado ativo permanece até ser clicado novamente ou outro botão ser ativado.\n * Suporta forwardRef para acesso programático ao elemento DOM.\n *\n * @param icon - O ícone a ser exibido no botão\n * @param size - Tamanho do botão (sm, md)\n * @param active - Estado ativo/selecionado do botão\n * @param className - Classes CSS adicionais\n * @param props - Todos os outros atributos HTML padrão de button\n * @returns Um elemento button compacto estilizado apenas com ícone\n *\n * @example\n * ```tsx\n * <IconButton\n * icon={<MoreVerticalIcon />}\n * size=\"sm\"\n * onClick={() => openMenu()}\n * />\n * ```\n *\n * @example\n * ```tsx\n * // Botão ativo em uma barra de ferramentas - permanece ativo até outro clique\n * <IconButton\n * icon={<BoldIcon />}\n * active={isBold}\n * onClick={toggleBold}\n * />\n * ```\n *\n * @example\n * ```tsx\n * // Usando ref para controle programático\n * const buttonRef = useRef<HTMLButtonElement>(null);\n *\n * <IconButton\n * ref={buttonRef}\n * icon={<EditIcon />}\n * size=\"md\"\n * onClick={() => startEditing()}\n * />\n * ```\n */\nconst IconButton = forwardRef<HTMLButtonElement, IconButtonProps>(\n (\n { icon, size = 'md', active = false, className = '', disabled, ...props },\n ref\n ) => {\n // Classes base para todos os estados\n const baseClasses = [\n 'inline-flex',\n 'items-center',\n 'justify-center',\n 'rounded-lg',\n 'font-medium',\n 'bg-transparent',\n 'text-text-950',\n 'cursor-pointer',\n 'hover:bg-primary-600',\n 'hover:text-text',\n 'focus-visible:outline-none',\n 'focus-visible:ring-2',\n 'focus-visible:ring-offset-0',\n 'focus-visible:ring-indicator-info',\n 'disabled:opacity-50',\n 'disabled:cursor-not-allowed',\n 'disabled:pointer-events-none',\n ];\n\n // Classes de tamanho\n const sizeClasses = {\n sm: ['w-6', 'h-6', 'text-sm'],\n md: ['w-10', 'h-10', 'text-base'],\n };\n\n // Classes de estado ativo\n const activeClasses = active\n ? ['!bg-primary-50', '!text-primary-950', 'hover:!bg-primary-100']\n : [];\n\n const allClasses = [\n ...baseClasses,\n ...sizeClasses[size],\n ...activeClasses,\n ].join(' ');\n\n // Garantir acessibilidade com aria-label padrão\n const ariaLabel = props['aria-label'] ?? 'Botão de ação';\n\n return (\n <button\n ref={ref}\n type=\"button\"\n className={cn(allClasses, className)}\n disabled={disabled}\n aria-pressed={active}\n aria-label={ariaLabel}\n {...props}\n >\n <span className=\"flex items-center justify-center\">{icon}</span>\n </button>\n );\n }\n);\n\nIconButton.displayName = 'IconButton';\n\nexport default IconButton;\n","import { ComponentPropsWithoutRef, ElementType, ReactNode } from 'react';\nimport { cn } from '../../utils/utils';\n\n/**\n * Base text component props\n */\ntype BaseTextProps = {\n /** Content to be displayed */\n children?: ReactNode;\n /** Text size variant */\n size?:\n | '2xs'\n | 'xs'\n | 'sm'\n | 'md'\n | 'lg'\n | 'xl'\n | '2xl'\n | '3xl'\n | '4xl'\n | '5xl'\n | '6xl';\n /** Font weight variant */\n weight?:\n | 'hairline'\n | 'light'\n | 'normal'\n | 'medium'\n | 'semibold'\n | 'bold'\n | 'extrabold'\n | 'black';\n /** Color variant - white for light backgrounds, black for dark backgrounds */\n color?: string;\n /** Additional CSS classes to apply */\n className?: string;\n};\n\n/**\n * Polymorphic text component props that ensures type safety based on the 'as' prop\n */\ntype TextProps<T extends ElementType = 'p'> = BaseTextProps & {\n /** HTML tag to render */\n as?: T;\n} & Omit<ComponentPropsWithoutRef<T>, keyof BaseTextProps>;\n\n/**\n * Text component for Analytica Ensino platforms\n *\n * A flexible polymorphic text component with multiple sizes, weights, and colors.\n * Automatically adapts to dark and light themes with full type safety.\n *\n * @param children - The content to display\n * @param size - The text size variant (2xs, xs, sm, md, lg, xl, 2xl, 3xl, 4xl, 5xl, 6xl)\n * @param weight - The font weight variant (hairline, light, normal, medium, semibold, bold, extrabold, black)\n * @param color - The color variant - adapts to theme\n * @param as - The HTML tag to render - determines allowed attributes via TypeScript\n * @param className - Additional CSS classes\n * @param props - HTML attributes valid for the chosen tag only\n * @returns A styled text element with type-safe attributes\n *\n * @example\n * ```tsx\n * <Text size=\"lg\" weight=\"bold\" color=\"text-info-800\">\n * This is a large, bold text\n * </Text>\n *\n * <Text as=\"a\" href=\"/link\" target=\"_blank\">\n * Link with type-safe anchor attributes\n * </Text>\n *\n * <Text as=\"button\" onClick={handleClick} disabled>\n * Button with type-safe button attributes\n * </Text>\n * ```\n */\nconst Text = <T extends ElementType = 'p'>({\n children,\n size = 'md',\n weight = 'normal',\n color = 'text-text-950',\n as,\n className = '',\n ...props\n}: TextProps<T>) => {\n let sizeClasses = '';\n let weightClasses = '';\n\n // Text size classes mapping\n const sizeClassMap = {\n '2xs': 'text-2xs',\n xs: 'text-xs',\n sm: 'text-sm',\n md: 'text-md',\n lg: 'text-lg',\n xl: 'text-xl',\n '2xl': 'text-2xl',\n '3xl': 'text-3xl',\n '4xl': 'text-4xl',\n '5xl': 'text-5xl',\n '6xl': 'text-6xl',\n } as const;\n\n sizeClasses = sizeClassMap[size] ?? sizeClassMap.md;\n\n // Font weight classes mapping\n const weightClassMap = {\n hairline: 'font-hairline',\n light: 'font-light',\n normal: 'font-normal',\n medium: 'font-medium',\n semibold: 'font-semibold',\n bold: 'font-bold',\n extrabold: 'font-extrabold',\n black: 'font-black',\n } as const;\n\n weightClasses = weightClassMap[weight] ?? weightClassMap.normal;\n\n const baseClasses = 'font-primary';\n const Component = as ?? ('p' as ElementType);\n\n return (\n <Component\n className={cn(baseClasses, sizeClasses, weightClasses, color, className)}\n {...props}\n >\n {children}\n </Component>\n );\n};\n\nexport default Text;\n"],"mappings":"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","names":["import_react","import_jsx_runtime","import_jsx_runtime"]}
@@ -255,11 +255,11 @@ var VideoPlayer = ({
255
255
  setShowSpeedMenu(!showSpeedMenu);
256
256
  }, [showSpeedMenu]);
257
257
  const toggleCaptions = useCallback(() => {
258
- if (!trackRef.current?.track) return;
258
+ if (!trackRef.current?.track || !subtitles) return;
259
259
  const newShowCaptions = !showCaptions;
260
260
  setShowCaptions(newShowCaptions);
261
- trackRef.current.track.mode = newShowCaptions ? "showing" : "hidden";
262
- }, [showCaptions]);
261
+ trackRef.current.track.mode = newShowCaptions && subtitles ? "showing" : "hidden";
262
+ }, [showCaptions, subtitles]);
263
263
  const handleTimeUpdate = useCallback(() => {
264
264
  if (videoRef.current) {
265
265
  const current = videoRef.current.currentTime;
@@ -288,6 +288,11 @@ var VideoPlayer = ({
288
288
  setDuration(videoRef.current.duration);
289
289
  }
290
290
  }, []);
291
+ useEffect(() => {
292
+ if (trackRef.current?.track) {
293
+ trackRef.current.track.mode = showCaptions && subtitles ? "showing" : "hidden";
294
+ }
295
+ }, [subtitles, showCaptions]);
291
296
  useEffect(() => {
292
297
  const handleVisibilityChange = () => {
293
298
  if (document.hidden && isPlaying && videoRef.current) {
@@ -393,9 +398,9 @@ var VideoPlayer = ({
393
398
  {
394
399
  ref: trackRef,
395
400
  kind: "captions",
396
- src: subtitles || "data:text/vtt;charset=utf-8,WEBVTT%0A%0ANOTE%20No%20captions%20available",
397
- srcLang: "en",
398
- label: subtitles ? "Subtitles" : "No captions available",
401
+ src: subtitles || "data:text/vtt;charset=utf-8,WEBVTT",
402
+ srcLang: "pt-br",
403
+ label: subtitles ? "Legendas em Portugu\xEAs" : "Sem legendas dispon\xEDveis",
399
404
  default: false
400
405
  }
401
406
  )