UNPKG

analytica-frontend-lib

Version:

Repositório público dos componentes utilizados nas plataformas da Analytica Ensino

1,349 lines (1,333 loc) 932 kB
// src/utils/utils.ts import { clsx } from "clsx"; import { twMerge } from "tailwind-merge"; // src/utils/dropdown.ts var syncDropdownState = (open, isActive, setActiveStates, key) => { if (!open && isActive) { setActiveStates((prev) => ({ ...prev, [key]: false })); } }; // src/utils/activityFilters.ts function getSelectedIdsFromCategories(categories, keys) { const result = {}; for (const [outputKey, categoryKey] of Object.entries(keys)) { const category = categories.find((c) => c.key === categoryKey); result[outputKey] = category?.selectedIds || []; } return result; } function toggleArrayItem(array, item) { return array.includes(item) ? array.filter((i) => i !== item) : [...array, item]; } function toggleSingleValue(currentValue, newValue) { return currentValue === newValue ? null : newValue; } function arraysEqual(a, b, comparator) { if (a.length !== b.length) return false; if (comparator) { const sortedA2 = [...a].sort(comparator); const sortedB2 = [...b].sort(comparator); return sortedA2.every((val, index) => val === sortedB2[index]); } const sortedA = [...a].sort((x, y) => String(x).localeCompare(String(y))); const sortedB = [...b].sort((x, y) => String(x).localeCompare(String(y))); return sortedA.every((val, index) => val === sortedB[index]); } function areFiltersEqual(filters1, filters2) { if (filters1 === filters2) return true; if (!filters1 || !filters2) return false; return arraysEqual(filters1.types, filters2.types) && arraysEqual(filters1.bankIds, filters2.bankIds) && arraysEqual(filters1.yearIds, filters2.yearIds) && arraysEqual(filters1.knowledgeIds, filters2.knowledgeIds) && arraysEqual(filters1.topicIds, filters2.topicIds) && arraysEqual(filters1.subtopicIds, filters2.subtopicIds) && arraysEqual(filters1.contentIds, filters2.contentIds); } // src/components/Quiz/useQuizStore.ts import { create } from "zustand"; import { devtools } from "zustand/middleware"; var QUESTION_DIFFICULTY = /* @__PURE__ */ ((QUESTION_DIFFICULTY2) => { QUESTION_DIFFICULTY2["FACIL"] = "FACIL"; QUESTION_DIFFICULTY2["MEDIO"] = "MEDIO"; QUESTION_DIFFICULTY2["DIFICIL"] = "DIFICIL"; return QUESTION_DIFFICULTY2; })(QUESTION_DIFFICULTY || {}); var QUIZ_TYPE = /* @__PURE__ */ ((QUIZ_TYPE2) => { QUIZ_TYPE2["SIMULADO"] = "SIMULADO"; QUIZ_TYPE2["QUESTIONARIO"] = "QUESTIONARIO"; QUIZ_TYPE2["ATIVIDADE"] = "ATIVIDADE"; return QUIZ_TYPE2; })(QUIZ_TYPE || {}); var QUESTION_TYPE = /* @__PURE__ */ ((QUESTION_TYPE2) => { QUESTION_TYPE2["ALTERNATIVA"] = "ALTERNATIVA"; QUESTION_TYPE2["DISSERTATIVA"] = "DISSERTATIVA"; QUESTION_TYPE2["MULTIPLA_ESCOLHA"] = "MULTIPLA_ESCOLHA"; QUESTION_TYPE2["VERDADEIRO_FALSO"] = "VERDADEIRO_FALSO"; QUESTION_TYPE2["LIGAR_PONTOS"] = "LIGAR_PONTOS"; QUESTION_TYPE2["PREENCHER"] = "PREENCHER"; QUESTION_TYPE2["IMAGEM"] = "IMAGEM"; return QUESTION_TYPE2; })(QUESTION_TYPE || {}); var QUESTION_STATUS = /* @__PURE__ */ ((QUESTION_STATUS3) => { QUESTION_STATUS3["PENDENTE_AVALIACAO"] = "PENDENTE_AVALIACAO"; QUESTION_STATUS3["RESPOSTA_CORRETA"] = "RESPOSTA_CORRETA"; QUESTION_STATUS3["RESPOSTA_INCORRETA"] = "RESPOSTA_INCORRETA"; QUESTION_STATUS3["NAO_RESPONDIDO"] = "NAO_RESPONDIDO"; return QUESTION_STATUS3; })(QUESTION_STATUS || {}); var ANSWER_STATUS = /* @__PURE__ */ ((ANSWER_STATUS2) => { ANSWER_STATUS2["RESPOSTA_CORRETA"] = "RESPOSTA_CORRETA"; ANSWER_STATUS2["RESPOSTA_INCORRETA"] = "RESPOSTA_INCORRETA"; ANSWER_STATUS2["PENDENTE_AVALIACAO"] = "PENDENTE_AVALIACAO"; ANSWER_STATUS2["NAO_RESPONDIDO"] = "NAO_RESPONDIDO"; return ANSWER_STATUS2; })(ANSWER_STATUS || {}); var SUBTYPE_ENUM = /* @__PURE__ */ ((SUBTYPE_ENUM2) => { SUBTYPE_ENUM2["PROVA"] = "PROVA"; SUBTYPE_ENUM2["ENEM_PROVA_1"] = "ENEM_PROVA_1"; SUBTYPE_ENUM2["ENEM_PROVA_2"] = "ENEM_PROVA_2"; SUBTYPE_ENUM2["VESTIBULAR"] = "VESTIBULAR"; SUBTYPE_ENUM2["SIMULADO"] = "SIMULADO"; SUBTYPE_ENUM2["SIMULADAO"] = "SIMULADAO"; return SUBTYPE_ENUM2; })(SUBTYPE_ENUM || {}); var MINUTE_INTERVAL_MS = 6e4; var useQuizStore = create()( devtools( (set, get) => { let timerInterval = null; let minuteCallbackInterval = null; const startTimer = () => { if (get().isFinished) { return; } if (timerInterval) { clearInterval(timerInterval); } timerInterval = setInterval(() => { const { timeElapsed } = get(); set({ timeElapsed: timeElapsed + 1 }); }, 1e3); }; const stopTimer = () => { if (timerInterval) { clearInterval(timerInterval); timerInterval = null; } }; const setMinuteCallback = (callback) => { set({ minuteCallback: callback }); }; const startMinuteCallback = () => { const { minuteCallback, isFinished } = get(); if (isFinished || !minuteCallback) { return; } if (minuteCallbackInterval) { clearInterval(minuteCallbackInterval); } minuteCallbackInterval = setInterval(() => { const { minuteCallback: currentCallback, isFinished: currentIsFinished } = get(); if (currentIsFinished || !currentCallback) { stopMinuteCallback(); return; } currentCallback(); }, MINUTE_INTERVAL_MS); }; const stopMinuteCallback = () => { if (minuteCallbackInterval) { clearInterval(minuteCallbackInterval); minuteCallbackInterval = null; } }; return { // Initial State quiz: null, currentQuestionIndex: 0, selectedAnswers: {}, userAnswers: [], timeElapsed: 0, isStarted: false, isFinished: false, userId: "", variant: "default", minuteCallback: null, dissertativeCharLimit: void 0, questionsResult: null, currentQuestionResult: null, // Setters setQuiz: (quiz) => set({ quiz }), setUserId: (userId) => set({ userId }), setUserAnswers: (userAnswers) => set({ userAnswers }), getUserId: () => get().userId, setVariant: (variant) => set({ variant }), setQuestionResult: (questionsResult) => set({ questionsResult }), setDissertativeCharLimit: (limit) => set({ dissertativeCharLimit: limit }), getDissertativeCharLimit: () => get().dissertativeCharLimit, // Navigation goToNextQuestion: () => { const { currentQuestionIndex, getTotalQuestions } = get(); const totalQuestions = getTotalQuestions(); if (currentQuestionIndex < totalQuestions - 1) { set({ currentQuestionIndex: currentQuestionIndex + 1 }); } }, goToPreviousQuestion: () => { const { currentQuestionIndex } = get(); if (currentQuestionIndex > 0) { set({ currentQuestionIndex: currentQuestionIndex - 1 }); } }, goToQuestion: (index) => { const { getTotalQuestions } = get(); const totalQuestions = getTotalQuestions(); if (index >= 0 && index < totalQuestions) { set({ currentQuestionIndex: index }); } }, selectAnswer: (questionId, answerId) => { const { quiz, userAnswers } = get(); if (!quiz) return; const activityId = quiz.id; const userId = get().getUserId(); if (!userId || userId === "") { return; } const question = quiz.questions.find((q) => q.id === questionId); if (!question) return; const existingAnswerIndex = userAnswers.findIndex( (answer) => answer.questionId === questionId ); const newUserAnswer = { questionId, activityId, userId, answer: question.questionType === "DISSERTATIVA" /* DISSERTATIVA */ ? answerId : null, optionId: question.questionType === "DISSERTATIVA" /* DISSERTATIVA */ ? null : answerId, questionType: question.questionType, answerStatus: "PENDENTE_AVALIACAO" /* PENDENTE_AVALIACAO */ }; let updatedUserAnswers; if (existingAnswerIndex !== -1) { updatedUserAnswers = [...userAnswers]; updatedUserAnswers[existingAnswerIndex] = newUserAnswer; } else { updatedUserAnswers = [...userAnswers, newUserAnswer]; } set({ userAnswers: updatedUserAnswers }); }, selectMultipleAnswer: (questionId, answerIds) => { const { quiz, userAnswers } = get(); if (!quiz) return; const activityId = quiz.id; const userId = get().getUserId(); if (!userId || userId === "") { return; } const question = quiz.questions.find((q) => q.id === questionId); if (!question) return; const filteredUserAnswers = userAnswers.filter( (answer) => answer.questionId !== questionId ); const newUserAnswers = answerIds.map( (answerId) => ({ questionId, activityId, userId, answer: null, // selectMultipleAnswer is for non-dissertative questions optionId: answerId, // selectMultipleAnswer should only set optionId questionType: question.questionType, answerStatus: "PENDENTE_AVALIACAO" /* PENDENTE_AVALIACAO */ }) ); const updatedUserAnswers = [ ...filteredUserAnswers, ...newUserAnswers ]; set({ userAnswers: updatedUserAnswers }); }, selectDissertativeAnswer: (questionId, answer) => { const { quiz, userAnswers, dissertativeCharLimit } = get(); if (!quiz) return; const activityId = quiz.id; const userId = get().getUserId(); if (!userId || userId === "") { return; } const question = quiz.questions.find((q) => q.id === questionId); if (!question || question.questionType !== "DISSERTATIVA" /* DISSERTATIVA */) { return; } let validatedAnswer = answer; if (dissertativeCharLimit !== void 0 && answer.length > dissertativeCharLimit) { validatedAnswer = answer.substring(0, dissertativeCharLimit); } const existingAnswerIndex = userAnswers.findIndex( (answerItem) => answerItem.questionId === questionId ); const newUserAnswer = { questionId, activityId, userId, answer: validatedAnswer, optionId: null, questionType: "DISSERTATIVA" /* DISSERTATIVA */, answerStatus: "PENDENTE_AVALIACAO" /* PENDENTE_AVALIACAO */ }; let updatedUserAnswers; if (existingAnswerIndex !== -1) { updatedUserAnswers = [...userAnswers]; updatedUserAnswers[existingAnswerIndex] = newUserAnswer; } else { updatedUserAnswers = [...userAnswers, newUserAnswer]; } set({ userAnswers: updatedUserAnswers }); }, skipQuestion: () => { const { getCurrentQuestion, userAnswers, quiz } = get(); const currentQuestion = getCurrentQuestion(); if (!quiz) return; if (currentQuestion) { const activityId = quiz.id; const userId = get().getUserId(); if (!userId || userId === "") { return; } const existingAnswerIndex = userAnswers.findIndex( (answer) => answer.questionId === currentQuestion.id ); const newUserAnswer = { questionId: currentQuestion.id, activityId, userId, answer: null, optionId: null, questionType: currentQuestion.questionType, answerStatus: "PENDENTE_AVALIACAO" /* PENDENTE_AVALIACAO */ }; let updatedUserAnswers; if (existingAnswerIndex !== -1) { updatedUserAnswers = [...userAnswers]; updatedUserAnswers[existingAnswerIndex] = newUserAnswer; } else { updatedUserAnswers = [...userAnswers, newUserAnswer]; } set({ userAnswers: updatedUserAnswers }); } }, skipCurrentQuestionIfUnanswered: () => { const { getCurrentQuestion, getCurrentAnswer, skipQuestion } = get(); const currentQuestion = getCurrentQuestion(); const currentAnswer = getCurrentAnswer(); if (!currentQuestion) return; if (!currentAnswer || currentAnswer.optionId === null && currentAnswer.answer === null) { skipQuestion(); } }, addUserAnswer: (questionId, answerId) => { const { quiz, userAnswers } = get(); if (!quiz) return; const activityId = quiz.id; const userId = get().getUserId(); if (!userId || userId === "") { return; } const question = quiz.questions.find((q) => q.id === questionId); if (!question) return; const existingAnswerIndex = userAnswers.findIndex( (answer) => answer.questionId === questionId ); const newUserAnswer = { questionId, activityId, userId, answer: question.questionType === "DISSERTATIVA" /* DISSERTATIVA */ ? answerId || null : null, optionId: question.questionType !== "DISSERTATIVA" /* DISSERTATIVA */ ? answerId || null : null, questionType: question.questionType, answerStatus: "PENDENTE_AVALIACAO" /* PENDENTE_AVALIACAO */ }; if (existingAnswerIndex !== -1) { const updatedUserAnswers = [...userAnswers]; updatedUserAnswers[existingAnswerIndex] = newUserAnswer; set({ userAnswers: updatedUserAnswers }); } else { set({ userAnswers: [...userAnswers, newUserAnswer] }); } }, startQuiz: () => { set({ isStarted: true, timeElapsed: 0 }); startTimer(); startMinuteCallback(); }, finishQuiz: () => { set({ isFinished: true }); stopTimer(); stopMinuteCallback(); }, resetQuiz: () => { stopTimer(); stopMinuteCallback(); set({ quiz: null, currentQuestionIndex: 0, selectedAnswers: {}, userAnswers: [], timeElapsed: 0, isStarted: false, isFinished: false, userId: "", variant: "default", minuteCallback: null, dissertativeCharLimit: void 0, questionsResult: null, currentQuestionResult: null }); }, // Timer updateTime: (time) => set({ timeElapsed: time }), startTimer, stopTimer, // Minute Callback setMinuteCallback, startMinuteCallback, stopMinuteCallback, // Getters getCurrentQuestion: () => { const { currentQuestionIndex, quiz } = get(); if (!quiz) { return null; } return quiz.questions[currentQuestionIndex]; }, getTotalQuestions: () => { const { quiz } = get(); return quiz?.questions?.length || 0; }, getAnsweredQuestions: () => { const { userAnswers } = get(); return userAnswers.filter( (answer) => answer.optionId !== null || answer.answer !== null ).length; }, getUnansweredQuestions: () => { const { quiz, userAnswers } = get(); if (!quiz) return []; const unansweredQuestions = []; quiz.questions.forEach((question, index) => { const userAnswer = userAnswers.find( (answer) => answer.questionId === question.id ); const isAnswered = userAnswer && (userAnswer.optionId !== null || userAnswer.answer !== null); const isSkipped = userAnswer && userAnswer.optionId === null && userAnswer.answer === null; if (!isAnswered && !isSkipped) { unansweredQuestions.push(index + 1); } }); return unansweredQuestions; }, getSkippedQuestions: () => { const { userAnswers } = get(); return userAnswers.filter( (answer) => answer.optionId === null && answer.answer === null ).length; }, getProgress: () => { const { getTotalQuestions, getAnsweredQuestions } = get(); const total = getTotalQuestions(); const answered = getAnsweredQuestions(); return total > 0 ? answered / total * 100 : 0; }, isQuestionAnswered: (questionId) => { const { userAnswers } = get(); const userAnswer = userAnswers.find( (answer) => answer.questionId === questionId ); return userAnswer ? userAnswer.optionId !== null || userAnswer.answer !== null : false; }, isQuestionSkipped: (questionId) => { const { userAnswers } = get(); const userAnswer = userAnswers.find( (answer) => answer.questionId === questionId ); return userAnswer ? userAnswer.optionId === null && userAnswer.answer === null : false; }, getCurrentAnswer: () => { const { getCurrentQuestion, userAnswers } = get(); const currentQuestion = getCurrentQuestion(); if (!currentQuestion) return void 0; const userAnswer = userAnswers.find( (answer) => answer.questionId === currentQuestion.id ); const hasAnswerContent = (ua) => !!ua && (ua.optionId !== null && ua.optionId !== "" || ua.answer !== null && ua.answer !== ""); if (!hasAnswerContent(userAnswer)) { return void 0; } return userAnswer; }, getAllCurrentAnswer: () => { const { getCurrentQuestion, userAnswers } = get(); const currentQuestion = getCurrentQuestion(); if (!currentQuestion) return void 0; const userAnswer = userAnswers.filter( (answer) => answer.questionId === currentQuestion.id ); return userAnswer; }, getQuizTitle: () => { const { quiz } = get(); return quiz?.title || "Quiz"; }, formatTime: (seconds) => { const minutes = Math.floor(seconds / 60); const remainingSeconds = seconds % 60; return `${minutes.toString().padStart(2, "0")}:${remainingSeconds.toString().padStart(2, "0")}`; }, getUserAnswers: () => { const { userAnswers } = get(); return userAnswers; }, getUnansweredQuestionsFromUserAnswers: () => { const { quiz, userAnswers } = get(); if (!quiz) return []; const unansweredQuestions = []; quiz.questions.forEach((question, index) => { const userAnswer = userAnswers.find( (answer) => answer.questionId === question.id ); const hasAnswer = userAnswer && (userAnswer.optionId !== null || userAnswer.answer !== null); const isSkipped = userAnswer && userAnswer.optionId === null && userAnswer.answer === null; if (!hasAnswer || isSkipped) { unansweredQuestions.push(index + 1); } }); return unansweredQuestions; }, getQuestionsGroupedBySubject: () => { const { getQuestionResult, quiz, variant } = get(); const questions = variant == "result" ? getQuestionResult()?.answers : quiz?.questions; if (!questions) return {}; const groupedQuestions = {}; questions.forEach((question) => { const subjectId = question.knowledgeMatrix?.[0]?.subject?.id || "Sem mat\xE9ria"; if (!groupedQuestions[subjectId]) { groupedQuestions[subjectId] = []; } groupedQuestions[subjectId].push(question); }); return groupedQuestions; }, // New methods for userAnswers getUserAnswerByQuestionId: (questionId) => { const { userAnswers } = get(); return userAnswers.find((answer) => answer.questionId === questionId) || null; }, isQuestionAnsweredByUserAnswers: (questionId) => { const { userAnswers } = get(); const answer = userAnswers.find( (answer2) => answer2.questionId === questionId ); return answer ? answer.optionId !== null || answer.answer !== null : false; }, getQuestionStatusFromUserAnswers: (questionId) => { const { userAnswers } = get(); const answer = userAnswers.find( (answer2) => answer2.questionId === questionId ); if (!answer) return "unanswered"; if (answer.optionId === null) return "skipped"; return "answered"; }, getUserAnswersForActivity: () => { const { userAnswers } = get(); return userAnswers; }, setCurrentQuestion: (question) => { const { quiz, variant, questionsResult } = get(); if (!quiz) return; let questionIndex = 0; if (variant == "result") { if (!questionsResult) return; const questionResult = questionsResult.answers.find((q) => q.id === question.id) ?? questionsResult.answers.find((q) => q.questionId === question.id); if (!questionResult) return; questionIndex = quiz.questions.findIndex( (q) => q.id === questionResult.questionId ); } else { questionIndex = quiz.questions.findIndex( (q) => q.id === question.id ); } if (questionIndex === -1) { return; } set({ currentQuestionIndex: questionIndex }); }, setAnswerStatus: (questionId, status) => { const { userAnswers } = get(); const existingAnswerIndex = userAnswers.findIndex( (answer) => answer.questionId === questionId ); if (existingAnswerIndex !== -1) { const updatedUserAnswers = [...userAnswers]; updatedUserAnswers[existingAnswerIndex] = { ...updatedUserAnswers[existingAnswerIndex], answerStatus: status }; set({ userAnswers: updatedUserAnswers }); } }, getAnswerStatus: (questionId) => { const { userAnswers } = get(); const userAnswer = userAnswers.find( (answer) => answer.questionId === questionId ); return userAnswer ? userAnswer.answerStatus : null; }, getQuestionIndex: (questionId) => { const { questionsResult, variant, quiz } = get(); if (variant == "result") { if (!questionsResult) return 0; let idx = questionsResult.answers.findIndex( (q) => q.questionId === questionId ); if (idx === -1) { idx = questionsResult.answers.findIndex( (q) => q.id === questionId ); } return idx !== -1 ? idx + 1 : 0; } else { if (!quiz) return 0; const idx = quiz.questions.findIndex((q) => q.id === questionId); return idx !== -1 ? idx + 1 : 0; } }, // Question Result getQuestionResultByQuestionId: (questionId) => { const { questionsResult } = get(); const question = questionsResult?.answers.find( (answer) => answer.questionId === questionId ); return question || null; }, getQuestionResultStatistics: () => { const { questionsResult } = get(); return questionsResult?.statistics || null; }, getQuestionResult: () => { const { questionsResult } = get(); return questionsResult; }, setQuestionsResult: (questionsResult) => { set({ questionsResult }); }, setCurrentQuestionResult: (currentQuestionResult) => { set({ currentQuestionResult }); }, getCurrentQuestionResult: () => { const { currentQuestionResult } = get(); return currentQuestionResult; } }; }, { name: "quiz-store" } ) ); // src/utils/questionTypeUtils.ts function mapQuestionTypeToEnum(type, fallback) { const upperType = type.toUpperCase(); const typeMap = { ALTERNATIVA: "ALTERNATIVA" /* ALTERNATIVA */, DISSERTATIVA: "DISSERTATIVA" /* DISSERTATIVA */, MULTIPLA_ESCOLHA: "MULTIPLA_ESCOLHA" /* MULTIPLA_ESCOLHA */, VERDADEIRO_FALSO: "VERDADEIRO_FALSO" /* VERDADEIRO_FALSO */, IMAGEM: "IMAGEM" /* IMAGEM */, LIGAR_PONTOS: "LIGAR_PONTOS" /* LIGAR_PONTOS */, PREENCHER: "PREENCHER" /* PREENCHER */ }; return typeMap[upperType] ?? fallback ?? null; } function mapQuestionTypeToEnumRequired(type, fallback = "ALTERNATIVA" /* ALTERNATIVA */) { return mapQuestionTypeToEnum(type, fallback) ?? fallback; } // src/types/activityDetails.ts var STUDENT_ACTIVITY_STATUS = { CONCLUIDO: "CONCLUIDO", AGUARDANDO_CORRECAO: "AGUARDANDO_CORRECAO", AGUARDANDO_RESPOSTA: "AGUARDANDO_RESPOSTA", NAO_ENTREGUE: "NAO_ENTREGUE" }; var ACTIVITY_AVAILABILITY = { DISPONIVEL: "DISPONIVEL", NAO_INICIADA: "NAO_INICIADA", EXPIRADA: "EXPIRADA" }; // src/utils/activityDetailsUtils.ts var getStatusBadgeConfig = (status) => { const configs = { [STUDENT_ACTIVITY_STATUS.CONCLUIDO]: { label: "Conclu\xEDdo", bgColor: "bg-green-50", textColor: "text-green-800" }, [STUDENT_ACTIVITY_STATUS.AGUARDANDO_CORRECAO]: { label: "Aguardando Corre\xE7\xE3o", bgColor: "bg-yellow-50", textColor: "text-yellow-800" }, [STUDENT_ACTIVITY_STATUS.AGUARDANDO_RESPOSTA]: { label: "Aguardando Resposta", bgColor: "bg-blue-50", textColor: "text-blue-800" }, [STUDENT_ACTIVITY_STATUS.NAO_ENTREGUE]: { label: "N\xE3o Entregue", bgColor: "bg-red-50", textColor: "text-red-800" }, default: { label: "Desconhecido", bgColor: "bg-gray-50", textColor: "text-gray-800" } }; return configs[status] ?? configs.default; }; var formatTimeSpent = (seconds) => { const hours = Math.floor(seconds / 3600); const minutes = Math.floor(seconds % 3600 / 60); const secs = seconds % 60; return `${String(hours).padStart(2, "0")}:${String(minutes).padStart(2, "0")}:${String(secs).padStart(2, "0")}`; }; var formatQuestionNumbers = (numbers) => { if (numbers.length === 0) return "-"; return numbers.map((n) => String(n + 1).padStart(2, "0")).join(", "); }; var formatDateToBrazilian = (dateString) => { const date = new Date(dateString); const day = String(date.getUTCDate()).padStart(2, "0"); const month = String(date.getUTCMonth() + 1).padStart(2, "0"); const year = date.getUTCFullYear(); return `${day}/${month}/${year}`; }; // src/utils/utils.ts function cn(...inputs) { return twMerge(clsx(inputs)); } function getSubjectColorWithOpacity(hexColor, isDark) { if (!hexColor) return void 0; let color = hexColor.replace(/^#/, "").toLowerCase(); if (isDark) { if (color.length === 8) { color = color.slice(0, 6); } return `#${color}`; } else { let resultColor; if (color.length === 6) { resultColor = `#${color}4d`; } else if (color.length === 8) { resultColor = `#${color}`; } else { resultColor = `#${color}`; } return resultColor; } } // src/components/Text/Text.tsx import { jsx } from "react/jsx-runtime"; var Text = ({ children, size = "md", weight = "normal", color = "text-text-950", as, className = "", ...props }) => { let sizeClasses = ""; let weightClasses = ""; const sizeClassMap = { "2xs": "text-2xs", xs: "text-xs", sm: "text-sm", md: "text-md", lg: "text-lg", xl: "text-xl", "2xl": "text-2xl", "3xl": "text-3xl", "4xl": "text-4xl", "5xl": "text-5xl", "6xl": "text-6xl" }; sizeClasses = sizeClassMap[size] ?? sizeClassMap.md; const weightClassMap = { hairline: "font-hairline", light: "font-light", normal: "font-normal", medium: "font-medium", semibold: "font-semibold", bold: "font-bold", extrabold: "font-extrabold", black: "font-black" }; weightClasses = weightClassMap[weight] ?? weightClassMap.normal; const baseClasses = "font-primary"; const Component = as ?? "p"; return /* @__PURE__ */ jsx( Component, { className: cn(baseClasses, sizeClasses, weightClasses, color, className), ...props, children } ); }; var Text_default = Text; // src/components/Button/Button.tsx import { jsx as jsx2, jsxs } from "react/jsx-runtime"; var VARIANT_ACTION_CLASSES = { solid: { primary: "bg-primary-950 text-text border border-primary-950 hover:bg-primary-800 hover:border-primary-800 focus-visible:outline-none focus-visible:bg-primary-950 focus-visible:ring-2 focus-visible:ring-offset-0 focus-visible:ring-indicator-info active:bg-primary-700 active:border-primary-700 disabled:bg-primary-500 disabled:border-primary-500 disabled:opacity-40 disabled:cursor-not-allowed", positive: "bg-success-500 text-text border border-success-500 hover:bg-success-600 hover:border-success-600 focus-visible:outline-none focus-visible:bg-success-500 focus-visible:ring-2 focus-visible:ring-offset-0 focus-visible:ring-indicator-info active:bg-success-700 active:border-success-700 disabled:bg-success-500 disabled:border-success-500 disabled:opacity-40 disabled:cursor-not-allowed", negative: "bg-error-500 text-text border border-error-500 hover:bg-error-600 hover:border-error-600 focus-visible:outline-none focus-visible:bg-error-500 focus-visible:ring-2 focus-visible:ring-offset-0 focus-visible:ring-indicator-info active:bg-error-700 active:border-error-700 disabled:bg-error-500 disabled:border-error-500 disabled:opacity-40 disabled:cursor-not-allowed" }, outline: { primary: "bg-transparent text-primary-950 border border-primary-950 hover:bg-background-50 hover:text-primary-400 hover:border-primary-400 focus-visible:border-0 focus-visible:outline-none focus-visible:text-primary-600 focus-visible:ring-2 focus-visible:ring-offset-0 focus-visible:ring-indicator-info active:text-primary-700 active:border-primary-700 disabled:opacity-40 disabled:cursor-not-allowed", positive: "bg-transparent text-success-500 border border-success-300 hover:bg-background-50 hover:text-success-400 hover:border-success-400 focus-visible:border-0 focus-visible:outline-none focus-visible:text-success-600 focus-visible:ring-2 focus-visible:ring-offset-0 focus-visible:ring-indicator-info active:text-success-700 active:border-success-700 disabled:opacity-40 disabled:cursor-not-allowed", negative: "bg-transparent text-error-500 border border-error-300 hover:bg-background-50 hover:text-error-400 hover:border-error-400 focus-visible:border-0 focus-visible:outline-none focus-visible:text-error-600 focus-visible:ring-2 focus-visible:ring-offset-0 focus-visible:ring-indicator-info active:text-error-700 active:border-error-700 disabled:opacity-40 disabled:cursor-not-allowed" }, link: { primary: "bg-transparent text-primary-950 hover:text-primary-400 focus-visible:outline-none focus-visible:text-primary-600 focus-visible:ring-2 focus-visible:ring-offset-0 focus-visible:ring-indicator-info active:text-primary-700 disabled:opacity-40 disabled:cursor-not-allowed", positive: "bg-transparent text-success-500 hover:text-success-400 focus-visible:outline-none focus-visible:text-success-600 focus-visible:ring-2 focus-visible:ring-offset-0 focus-visible:ring-indicator-info active:text-success-700 disabled:opacity-40 disabled:cursor-not-allowed", negative: "bg-transparent text-error-500 hover:text-error-400 focus-visible:outline-none focus-visible:text-error-600 focus-visible:ring-2 focus-visible:ring-offset-0 focus-visible:ring-indicator-info active:text-error-700 disabled:opacity-40 disabled:cursor-not-allowed" } }; var SIZE_CLASSES = { "extra-small": "text-xs px-3.5 py-2", small: "text-sm px-4 py-2.5", medium: "text-md px-5 py-2.5", large: "text-lg px-6 py-3", "extra-large": "text-lg px-7 py-3.5" }; var Button = ({ children, iconLeft, iconRight, size = "medium", variant = "solid", action = "primary", className = "", disabled, type = "button", ...props }) => { const sizeClasses = SIZE_CLASSES[size]; const variantClasses = VARIANT_ACTION_CLASSES[variant][action]; const baseClasses = "inline-flex items-center justify-center rounded-full cursor-pointer font-medium"; return /* @__PURE__ */ jsxs( "button", { className: cn(baseClasses, variantClasses, sizeClasses, className), disabled, type, ...props, children: [ iconLeft && /* @__PURE__ */ jsx2("span", { className: "mr-2 flex items-center", children: iconLeft }), children, iconRight && /* @__PURE__ */ jsx2("span", { className: "ml-2 flex items-center", children: iconRight }) ] } ); }; var Button_default = Button; // src/components/Badge/Badge.tsx import { Bell } from "phosphor-react"; import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime"; var VARIANT_ACTION_CLASSES2 = { solid: { error: "bg-error-background text-error-700 focus-visible:outline-none", warning: "bg-warning text-warning-800 focus-visible:outline-none", success: "bg-success text-success-800 focus-visible:outline-none", info: "bg-info text-info-800 focus-visible:outline-none", muted: "bg-background-muted text-background-800 focus-visible:outline-none" }, outlined: { error: "bg-error text-error-700 border border-error-300 focus-visible:outline-none", warning: "bg-warning text-warning-800 border border-warning-300 focus-visible:outline-none", success: "bg-success text-success-800 border border-success-300 focus-visible:outline-none", info: "bg-info text-info-800 border border-info-300 focus-visible:outline-none", muted: "bg-background-muted text-background-800 border border-border-300 focus-visible:outline-none" }, exams: { exam1: "bg-exam-1 text-info-700 focus-visible:outline-none", exam2: "bg-exam-2 text-typography-1 focus-visible:outline-none", exam3: "bg-exam-3 text-typography-2 focus-visible:outline-none", exam4: "bg-exam-4 text-success-700 focus-visible:outline-none" }, examsOutlined: { exam1: "bg-exam-1 text-info-700 border border-info-700 focus-visible:outline-none", exam2: "bg-exam-2 text-typography-1 border border-typography-1 focus-visible:outline-none", exam3: "bg-exam-3 text-typography-2 border border-typography-2 focus-visible:outline-none", exam4: "bg-exam-4 text-success-700 border border-success-700 focus-visible:outline-none" }, resultStatus: { negative: "bg-error text-error-800 focus-visible:outline-none", positive: "bg-success text-success-800 focus-visible:outline-none" }, notification: "text-primary" }; var SIZE_CLASSES2 = { small: "text-2xs px-2 py-1", medium: "text-xs px-2 py-1", large: "text-sm px-2 py-1" }; var SIZE_CLASSES_ICON = { small: "size-3", medium: "size-3.5", large: "size-4" }; var Badge = ({ children, iconLeft, iconRight, size = "medium", variant = "solid", action = "error", className = "", notificationActive = false, ...props }) => { const sizeClasses = SIZE_CLASSES2[size]; const sizeClassesIcon = SIZE_CLASSES_ICON[size]; const variantActionMap = VARIANT_ACTION_CLASSES2[variant] || {}; const variantClasses = typeof variantActionMap === "string" ? variantActionMap : variantActionMap[action] ?? variantActionMap.muted ?? ""; const baseClasses = "inline-flex items-center justify-center rounded-xs font-normal gap-1 relative"; const baseClassesIcon = "flex items-center"; if (variant === "notification") { return /* @__PURE__ */ jsxs2( "div", { className: cn(baseClasses, variantClasses, sizeClasses, className), ...props, children: [ /* @__PURE__ */ jsx3(Bell, { size: 24, className: "text-current", "aria-hidden": "true" }), notificationActive && /* @__PURE__ */ jsx3( "span", { "data-testid": "notification-dot", className: "absolute top-[5px] right-[10px] block h-2 w-2 rounded-full bg-indicator-error ring-2 ring-white" } ) ] } ); } return /* @__PURE__ */ jsxs2( "div", { className: cn(baseClasses, variantClasses, sizeClasses, className), ...props, children: [ iconLeft && /* @__PURE__ */ jsx3("span", { className: cn(baseClassesIcon, sizeClassesIcon), children: iconLeft }), children, iconRight && /* @__PURE__ */ jsx3("span", { className: cn(baseClassesIcon, sizeClassesIcon), children: iconRight }) ] } ); }; var Badge_default = Badge; // src/components/Alert/Alert.tsx import { CheckCircle, Info, WarningCircle, XCircle } from "phosphor-react"; import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime"; var VARIANT_ACTION_CLASSES3 = { solid: { default: "bg-background-50 border-transparent", info: "bg-info border-transparent", success: "bg-success border-transparent", warning: "bg-warning border-transparent", error: "bg-error border-transparent" }, outline: { default: "bg-background border border-border-100", info: "bg-background border border-border-100", success: "bg-background border border-border-100", warning: "bg-background border border-border-100", error: "bg-background border border-border-100" } }; var COLOR_CLASSES = { default: "text-text-950", info: "text-info-800", success: "text-success-800", warning: "text-warning-800", error: "text-error-800" }; var ICONS = { default: /* @__PURE__ */ jsx4(CheckCircle, { size: 18 }), info: /* @__PURE__ */ jsx4(Info, { size: 18 }), success: /* @__PURE__ */ jsx4(CheckCircle, { size: 18 }), warning: /* @__PURE__ */ jsx4(WarningCircle, { size: 18 }), error: /* @__PURE__ */ jsx4(XCircle, { size: 18 }) }; var Alert = ({ variant = "solid", title, description, action = "default", className, ...props }) => { const baseClasses = "alert-wrapper flex items-start gap-2 w-[384px] py-3 px-4 font-inherit rounded-md"; const variantClasses = VARIANT_ACTION_CLASSES3[variant][action]; const variantColor = COLOR_CLASSES[action]; const variantIcon = ICONS[action]; const hasHeading = Boolean(title); return /* @__PURE__ */ jsxs3("div", { className: cn(baseClasses, variantClasses, className), ...props, children: [ /* @__PURE__ */ jsx4("span", { className: cn("mt-0.5", variantColor), children: variantIcon }), /* @__PURE__ */ jsxs3("div", { children: [ hasHeading && /* @__PURE__ */ jsx4( Text_default, { size: "md", weight: "medium", color: variantColor, className: "mb-0.5", children: title } ), /* @__PURE__ */ jsx4( Text_default, { size: hasHeading ? "sm" : "md", weight: "normal", color: !hasHeading ? variantColor : "text-text-700", children: description } ) ] }) ] }); }; var Alert_default = Alert; // src/components/LatexRenderer/LatexRenderer.tsx import "katex/dist/katex.min.css"; import { InlineMath, BlockMath } from "react-katex"; import DOMPurify from "dompurify"; import parse, { Element as Element2 } from "html-react-parser"; import { Fragment, jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime"; var sanitizeHtml = (value) => { return DOMPurify.sanitize(value, { ADD_ATTR: ["data-latex", "data-display-mode", "data-math", "data-math-id"] }); }; var decodeHtmlEntities = (str) => { const textarea = document.createElement("textarea"); textarea.innerHTML = str; return textarea.value; }; var cleanLatex = (str) => { return str.replaceAll(/[\u200B-\u200D\uFEFF]/g, "").trim(); }; var createMathReplacer = (mathParts, errorRenderer) => { return (domNode) => { if (domNode instanceof Element2 && domNode.name === "span" && domNode.attribs["data-math-id"]) { const mathId = Number.parseInt(domNode.attribs["data-math-id"], 10); const mathPart = mathParts[mathId]; if (!mathPart) return domNode; if (mathPart.type === "inline") { return /* @__PURE__ */ jsx5( InlineMath, { math: mathPart.latex, renderError: () => errorRenderer(mathPart.latex) }, `math-${mathId}` ); } else { return /* @__PURE__ */ jsx5("div", { className: "my-2.5 text-center", children: /* @__PURE__ */ jsx5( BlockMath, { math: mathPart.latex, renderError: () => errorRenderer(mathPart.latex) } ) }, `math-${mathId}`); } } return domNode; }; }; var LatexRenderer = ({ content, className, style, onError }) => { const renderContentWithMath = (htmlContent) => { if (!htmlContent) return null; let processedContent = htmlContent; const mathParts = []; const mathFormulaPattern = /<span[^>]*class="math-formula"[^>]*data-latex="([^"]*)"[^>]*>[\s\S]*?<\/span>/g; processedContent = processedContent.replaceAll( mathFormulaPattern, (match, latex) => { const isDisplayMode = match.includes('data-display-mode="true"'); const id = mathParts.length; mathParts.push({ id, type: isDisplayMode ? "block" : "inline", latex: cleanLatex(decodeHtmlEntities(latex)) }); return `<span data-math-id="${id}"></span>`; } ); const wrappedMathPattern = /<span[^>]*class="math-expression"[^>]*data-math="([^"]*)"[^>]*>.*?<\/span>/g; processedContent = processedContent.replaceAll( wrappedMathPattern, (match, latex) => { const id = mathParts.length; mathParts.push({ id, type: "inline", latex: cleanLatex(decodeHtmlEntities(latex)) }); return `<span data-math-id="${id}"></span>`; } ); const doubleDollarPattern = /(?<!\\)\$\$([\s\S]+?)\$\$/g; processedContent = processedContent.replaceAll( doubleDollarPattern, (match, latex) => { const id = mathParts.length; mathParts.push({ id, type: "block", latex: cleanLatex(latex) }); return `<span data-math-id="${id}"></span>`; } ); const singleDollarPattern = /(?<!\\)\$([\s\S]+?)\$/g; processedContent = processedContent.replaceAll( singleDollarPattern, (match, latex) => { const id = mathParts.length; mathParts.push({ id, type: "inline", latex: cleanLatex(latex) }); return `<span data-math-id="${id}"></span>`; } ); const latexTagPattern = /(?:<latex>|&lt;latex&gt;)([\s\S]*?)(?:<\/latex>|&lt;\/latex&gt;)/g; processedContent = processedContent.replaceAll( latexTagPattern, (match, latex) => { const id = mathParts.length; mathParts.push({ id, type: "inline", latex: cleanLatex(latex) }); return `<span data-math-id="${id}"></span>`; } ); const latexEnvPattern = /\\begin\{([^}]+)\}([\s\S]*?)\\end\{\1\}/g; processedContent = processedContent.replaceAll(latexEnvPattern, (match) => { const id = mathParts.length; mathParts.push({ id, type: "block", latex: cleanLatex(match) }); return `<span data-math-id="${id}"></span>`; }); const sanitizedContent = sanitizeHtml(processedContent); const defaultErrorRenderer = (latex) => /* @__PURE__ */ jsxs4("span", { className: "text-red-600", children: [ "Math Error: ", latex ] }); const errorRenderer = onError || defaultErrorRenderer; const options = { replace: createMathReplacer(mathParts, errorRenderer) }; return /* @__PURE__ */ jsx5(Fragment, { children: parse(sanitizedContent, options) }); }; return /* @__PURE__ */ jsx5( "div", { className: cn("whitespace-pre-wrap leading-relaxed", className), style, children: renderContentWithMath(content) } ); }; var LatexRenderer_default = LatexRenderer; // src/components/IconButton/IconButton.tsx import { forwardRef } from "react"; import { jsx as jsx6 } from "react/jsx-runtime"; var IconButton = forwardRef( ({ icon, size = "md", active = false, className = "", disabled, ...props }, ref) => { const baseClasses = [ "inline-flex", "items-center", "justify-center", "rounded-lg", "font-medium", "bg-transparent", "text-text-950", "cursor-pointer", "hover:bg-primary-600", "hover:text-text", "focus-visible:outline-none", "focus-visible:ring-2", "focus-visible:ring-offset-0", "focus-visible:ring-indicator-info", "disabled:opacity-50", "disabled:cursor-not-allowed", "disabled:pointer-events-none" ]; const sizeClasses = { sm: ["w-6", "h-6", "text-sm"], md: ["w-10", "h-10", "text-base"] }; const activeClasses = active ? ["!bg-primary-50", "!text-primary-950", "hover:!bg-primary-100"] : []; const allClasses = [ ...baseClasses, ...sizeClasses[size], ...activeClasses ].join(" "); const ariaLabel = props["aria-label"] ?? "Bot\xE3o de a\xE7\xE3o"; return /* @__PURE__ */ jsx6( "button", { ref, type: "button", className: cn(allClasses, className), disabled, "aria-pressed": active, "aria-label": ariaLabel, ...props, children: /* @__PURE__ */ jsx6("span", { className: "flex items-center justify-center", children: icon }) } ); } ); IconButton.displayName = "IconButton"; var IconButton_default = IconButton; // src/components/IconRoundedButton/IconRoundedButton.tsx import { jsx as jsx7 } from "react/jsx-runtime"; var IconRoundedButton = ({ icon, className = "", disabled, ...props }) => { const baseClasses = [ "inline-flex", "items-center", "justify-center", "w-8", "h-8", "rounded-full", "cursor-pointer", "border", "border-background-200", "bg-background", "text-text-950", "hover:shadow-hard-shadow-1", "focus-visible:outline-none", "focus-visible:shadow-hard-shadow-1", "focus-visible:ring-2", "focus-visible:ring-indicator-info", "focus-visible:ring-offset-0", "disabled:opacity-50", "disabled:cursor-not-allowed" ].join(" "); return /* @__PURE__ */ jsx7( "button", { type: "button", className: cn(baseClasses, className), disabled, ...props, children: /* @__PURE__ */ jsx7("span", { className: "flex items-center justify-center w-5 h-5", children: icon }) } ); }; var IconRoundedButton_default = IconRoundedButton; // src/components/NavButton/NavButton.tsx import { forwardRef as forwardRef2 } from "react"; import { jsx as jsx8, jsxs as jsxs5 } from "react/jsx-runtime"; var NavButton = forwardRef2( ({ icon, label, selected = false, className = "", disabled, ...props }, ref) => { const baseClasses = [ "flex", "flex-col", "items-center", "justify-center", "gap-0.5", "px-12", "py-1", "rounded-sm", "cursor-pointer", "text-text-950", "text-xs", "font-medium", "hover:text-text", "hover:bg-primary-600", "focus-visible:outline-none", "focus-visible:ring-2", "focus-visible:ring-offset-0", "focus-visible:ring-indicator-info", "disabled:opacity-50", "disabled:cursor-not-allowed", "disabled:pointer-events-none" ]; const stateClasses = selected ? ["bg-primary-50", "text-primary-950"] : []; const allClasses = [...baseClasses, ...stateClasses].join(" "); return /* @__PURE__ */ jsxs5( "button", { ref, type: "button", className: cn(allClasses, className), disabled, "aria-pressed": selected, ...props, children: [ /* @__PURE__ */ jsx8("span", { className: "flex items-center justify-center w-5 h-5", children: icon }), /* @__PURE__ */ jsx8("span", { className: "whitespace-nowrap", children: label }) ] } ); } ); NavButton.displayName = "NavButton"; var NavButton_default = NavButton; // src/components/SelectionButton/SelectionButton.tsx import { forwardRef as forwardRef3 } from "react"; import { jsx as jsx9, jsxs as jsxs6 } from "react/jsx-runtime"; var SelectionButton = forwardRef3( ({ icon, label, selected = false, className = "", disabled, ...props }, ref) => { const baseClasses = [ "inline-flex", "items-center", "justify-start", "gap-2", "p-4", "rounded-xl", "curso