UNPKG

analytica-frontend-lib

Version:

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

1,321 lines (1,307 loc) 57 kB
import { StatCard } from "./chunk-3ZYHNRVG.mjs"; import { AccordionGroup } from "./chunk-ILCBSWZF.mjs"; import { ToastStore_default } from "./chunk-OAP6DJKU.mjs"; import { CardAccordation } from "./chunk-2ACDTDO4.mjs"; import { getStatusStyles, mock_image_question_default, stripHtmlTags } from "./chunk-C6FDFF3W.mjs"; import { AlternativesList } from "./chunk-TUJ3CWS2.mjs"; import { MultipleChoiceList } from "./chunk-PZ2H7EXH.mjs"; import { HtmlMathRenderer_default } from "./chunk-FN4G43WK.mjs"; import { generateFileId } from "./chunk-WAAIPWQL.mjs"; import { TextArea_default } from "./chunk-LI2FS7M2.mjs"; import { Radio_default } from "./chunk-NLK3GG73.mjs"; import { Badge_default } from "./chunk-OEW3ST4F.mjs"; import { Modal_default } from "./chunk-TNLGS7SB.mjs"; import { Button_default } from "./chunk-LAYB7IKW.mjs"; import { Text_default } from "./chunk-IMCIR6TJ.mjs"; import { cn } from "./chunk-53ICLDGS.mjs"; // src/components/CorrectActivityModal/CorrectActivityModal.tsx import { useState, useRef, useEffect, useCallback } from "react"; import { PencilSimpleIcon } from "@phosphor-icons/react/dist/csr/PencilSimple"; import { PaperclipIcon } from "@phosphor-icons/react/dist/csr/Paperclip"; import { XIcon } from "@phosphor-icons/react/dist/csr/X"; import { ImageIcon } from "@phosphor-icons/react/dist/csr/Image"; // src/utils/studentActivityCorrection/constants.ts var QUESTION_STATUS = { CORRETA: "CORRETA", INCORRETA: "INCORRETA", EM_BRANCO: "EM_BRANCO", /** Reserved for future use - pending teacher evaluation for essay questions */ PENDENTE: "PENDENTE" }; // src/utils/studentActivityCorrection/utils.ts var getIsCorrect = (answerStatus) => { if (answerStatus === "RESPOSTA_CORRETA" /* RESPOSTA_CORRETA */) return true; if (answerStatus === "RESPOSTA_INCORRETA" /* RESPOSTA_INCORRETA */) return false; return null; }; var mapAnswerStatusToQuestionStatus = (answerStatus) => { switch (answerStatus) { case "RESPOSTA_CORRETA" /* RESPOSTA_CORRETA */: return QUESTION_STATUS.CORRETA; case "RESPOSTA_INCORRETA" /* RESPOSTA_INCORRETA */: return QUESTION_STATUS.INCORRETA; case "NAO_RESPONDIDO" /* NAO_RESPONDIDO */: return QUESTION_STATUS.EM_BRANCO; case "PENDENTE_AVALIACAO" /* PENDENTE_AVALIACAO */: return QUESTION_STATUS.PENDENTE; default: return QUESTION_STATUS.EM_BRANCO; } }; var getQuestionStatusBadgeConfig = (status) => { const configs = { [QUESTION_STATUS.CORRETA]: { label: "Correta", bgColor: "bg-success-background", textColor: "text-success-800" }, [QUESTION_STATUS.INCORRETA]: { label: "Incorreta", bgColor: "bg-error-background", textColor: "text-error-800" }, [QUESTION_STATUS.EM_BRANCO]: { label: "Em branco", bgColor: "bg-gray-100", textColor: "text-gray-600" }, [QUESTION_STATUS.PENDENTE]: { label: "Pendente", bgColor: "bg-warning-background", textColor: "text-warning-800" } }; const defaultConfig = { label: "Sem categoria", bgColor: "bg-gray-100", textColor: "text-gray-600" }; return configs[status] ?? defaultConfig; }; var canAutoValidate = (questionData) => { const { result } = questionData; if (result.questionType === "DISSERTATIVA" /* DISSERTATIVA */) { return false; } return true; }; var hasIsCorrect = (op) => { return op.isCorrect != null; }; var validateAlternativa = (selected, options) => { if (selected.size !== 1) return "RESPOSTA_INCORRETA" /* RESPOSTA_INCORRETA */; const [selectedId] = selected; return options.find((o) => o.id === selectedId)?.isCorrect ? "RESPOSTA_CORRETA" /* RESPOSTA_CORRETA */ : "RESPOSTA_INCORRETA" /* RESPOSTA_INCORRETA */; }; var validateMultiplaEscolha = (selected, options) => { const allMatch = options.every((op) => selected.has(op.id) === op.isCorrect); return allMatch ? "RESPOSTA_CORRETA" /* RESPOSTA_CORRETA */ : "RESPOSTA_INCORRETA" /* RESPOSTA_INCORRETA */; }; var validateVerdadeiroFalso = validateMultiplaEscolha; var validators = { ["ALTERNATIVA" /* ALTERNATIVA */]: validateAlternativa, ["MULTIPLA_ESCOLHA" /* MULTIPLA_ESCOLHA */]: validateMultiplaEscolha, ["VERDADEIRO_FALSO" /* VERDADEIRO_FALSO */]: validateVerdadeiroFalso }; var autoValidateQuestion = (questionData) => { const { result } = questionData; if (!canAutoValidate(questionData) || !result.options) return null; const validOptions = result.options.filter(hasIsCorrect); if (validOptions.length === 0) return null; const selected = new Set( result.selectedOptions?.map((o) => o.optionId) ?? [] ); if (selected.size === 0) return "NAO_RESPONDIDO" /* NAO_RESPONDIDO */; const validator = validators[result.questionType]; if (!validator) return null; return validator(selected, validOptions); }; var getQuestionStatusFromData = (questionData) => { const { result } = questionData; if (result.answerStatus === "PENDENTE_AVALIACAO" /* PENDENTE_AVALIACAO */ && canAutoValidate(questionData)) { const autoValidatedStatus = autoValidateQuestion(questionData); if (autoValidatedStatus !== null) { return mapAnswerStatusToQuestionStatus(autoValidatedStatus); } } return mapAnswerStatusToQuestionStatus(result.answerStatus); }; // src/utils/studentActivityCorrection/converter.ts var buildQuestionFromAnswer = (answer) => { return { id: answer.questionId, statement: answer.statement, questionType: answer.questionType, difficultyLevel: answer.difficultyLevel, description: "", examBoard: null, examYear: null, solutionExplanation: answer.solutionExplanation || null, additionalContent: answer.additionalContent || null, answer: null, answerStatus: answer.answerStatus, options: answer.options || [], knowledgeMatrix: answer.knowledgeMatrix.map((matrix) => ({ areaKnowledge: matrix.areaKnowledge || { id: "", name: "" }, subject: matrix.subject || { id: "", name: "", color: "", icon: "" }, topic: matrix.topic || { id: "", name: "" }, subtopic: matrix.subtopic || { id: "", name: "" }, content: matrix.content || { id: "", name: "" } })), correctOptionIds: answer.options?.filter((opt) => opt.isCorrect).map((opt) => opt.id) || [] }; }; var buildEssayCorrection = (answer) => { if (answer.questionType === "DISSERTATIVA" /* DISSERTATIVA */) { return { isCorrect: getIsCorrect(answer.answerStatus), teacherFeedback: answer.teacherFeedback || "" }; } return void 0; }; var convertAnswerToQuestionData = (answer, index) => { const question = buildQuestionFromAnswer(answer); const correction = buildEssayCorrection(answer); return { question, result: answer, questionNumber: index + 1, correction }; }; var convertApiResponseToCorrectionData = (apiResponse, studentId, studentName, observation, attachment) => { const { answers, statistics } = apiResponse.data; const correctCount = statistics.correctAnswers; const incorrectCount = statistics.incorrectAnswers; const blankCount = statistics.totalAnswered - correctCount - incorrectCount; const questions = answers.map( (answer, index) => convertAnswerToQuestionData(answer, index) ); return { studentId, studentName, score: statistics.score ?? null, correctCount, incorrectCount, blankCount, questions, observation, attachment }; }; // src/utils/questionRenderer/alternative/index.tsx import { jsx } from "react/jsx-runtime"; var renderQuestionAlternative = ({ question, result }) => { const hasAutoValidation = result?.options?.some( (op) => op.isCorrect !== void 0 && op.isCorrect !== null ); const alternatives = question.options?.map((option) => { const isCorrectOption = result?.options?.find((op) => op.id === option.id)?.isCorrect || false; const isSelected = result?.selectedOptions?.some( (selectedOption) => selectedOption.optionId === option.id ) || false; const shouldShowCorrectAnswers = result?.answerStatus !== "PENDENTE_AVALIACAO" /* PENDENTE_AVALIACAO */ || hasAutoValidation; let status; if (shouldShowCorrectAnswers) { if (isCorrectOption) { status = "correct" /* CORRECT */; } else if (isSelected && !isCorrectOption) { status = "incorrect" /* INCORRECT */; } else { status = "neutral" /* NEUTRAL */; } } else { status = "neutral" /* NEUTRAL */; } return { label: option.option, value: option.id, status }; }); if (!alternatives || alternatives.length === 0) { return /* @__PURE__ */ jsx("div", { children: /* @__PURE__ */ jsx(Text_default, { size: "sm", weight: "normal", children: "N\xE3o h\xE1 Alternativas" }) }); } return /* @__PURE__ */ jsx("div", { className: "pt-2", children: /* @__PURE__ */ jsx( AlternativesList, { mode: "readonly", name: `question-${question.id}`, layout: "compact", alternatives, selectedValue: result?.selectedOptions?.[0]?.optionId || "" }, `question-${question.id}` ) }); }; // src/utils/questionRenderer/multipleChoice/index.tsx import { jsx as jsx2 } from "react/jsx-runtime"; var renderQuestionMultipleChoice = ({ question, result }) => { const hasAutoValidation = result?.options?.some( (op) => op.isCorrect !== void 0 && op.isCorrect !== null ); const choices = question.options?.map((option) => { const isCorrectOption = result?.options?.find((op) => op.id === option.id)?.isCorrect || false; const isSelected = result?.selectedOptions?.some( (op) => op.optionId === option.id ); const shouldShowCorrectAnswers = result?.answerStatus !== "PENDENTE_AVALIACAO" /* PENDENTE_AVALIACAO */ && result?.answerStatus !== "NAO_RESPONDIDO" /* NAO_RESPONDIDO */ || hasAutoValidation; let status; if (shouldShowCorrectAnswers) { if (isCorrectOption) { status = "correct" /* CORRECT */; } else if (isSelected && !isCorrectOption) { status = "incorrect" /* INCORRECT */; } else { status = "neutral" /* NEUTRAL */; } } else { status = "neutral" /* NEUTRAL */; } return { label: option.option, value: option.id, status }; }); if (!choices || choices.length === 0) { return /* @__PURE__ */ jsx2("div", { children: /* @__PURE__ */ jsx2(Text_default, { size: "sm", weight: "normal", children: "N\xE3o h\xE1 Caixas de sele\xE7\xE3o" }) }); } const selectedValues = result?.selectedOptions?.map((op) => op.optionId) || []; return /* @__PURE__ */ jsx2("div", { className: "pt-2", children: /* @__PURE__ */ jsx2( MultipleChoiceList, { mode: "readonly", name: `question-${question.id}`, choices, selectedValues }, `question-${question.id}` ) }); }; // src/utils/questionRenderer/components/index.tsx import { Fragment, useId } from "react"; import { CheckCircleIcon } from "@phosphor-icons/react/dist/csr/CheckCircle"; import { XCircleIcon } from "@phosphor-icons/react/dist/csr/XCircle"; import { jsx as jsx3, jsxs } from "react/jsx-runtime"; var getStatusBadge = (status) => { switch (status) { case "correct" /* CORRECT */: return /* @__PURE__ */ jsx3(Badge_default, { variant: "solid", action: "success", iconLeft: /* @__PURE__ */ jsx3(CheckCircleIcon, {}), children: "Resposta correta" }); case "incorrect" /* INCORRECT */: return /* @__PURE__ */ jsx3(Badge_default, { variant: "solid", action: "error", iconLeft: /* @__PURE__ */ jsx3(XCircleIcon, {}), children: "Resposta incorreta" }); default: return null; } }; var QuestionContainer = ({ children, className }) => { return /* @__PURE__ */ jsx3( "div", { className: cn( "bg-background rounded-t-xl px-4 pt-4 pb-[80px] h-auto flex flex-col gap-4 mb-auto", className ), children } ); }; var QuestionSubTitle = ({ subTitle }) => { return /* @__PURE__ */ jsx3("div", { className: "px-4 pb-2 pt-6", children: /* @__PURE__ */ jsx3(Text_default, { size: "md", weight: "bold", color: "text-text-950", children: subTitle }) }); }; var FillQuestionContent = ({ question, result }) => { const baseId = useId(); const additionalContent = result?.additionalContent || question.additionalContent || question.statement || ""; const cleanText = stripHtmlTags(additionalContent); const options = result?.options || question.options || []; const optionTextMap = {}; options.forEach((opt) => { optionTextMap[opt.id] = opt.option; }); const fillAnswers = {}; try { const resultWithFill = result; if (resultWithFill?.fillAnswers) { Object.assign(fillAnswers, resultWithFill.fillAnswers); } else if (result?.answer) { const parsed = typeof result.answer === "string" ? JSON.parse(result.answer) : result.answer; if (typeof parsed === "object") { Object.assign(fillAnswers, parsed); } } } catch { } const getOptionTextById = (optionId) => { if (!optionId) return null; return optionTextMap[optionId] ?? null; }; const isAnswerCorrect = (placeholderId) => { const selectedOptionId = fillAnswers[placeholderId]; return selectedOptionId === placeholderId; }; const renderStudentBadge = (placeholderId) => { const selectedOptionId = fillAnswers[placeholderId]; const selectedOptionText = getOptionTextById(selectedOptionId); const isCorrect = isAnswerCorrect(placeholderId); if (!selectedOptionId) { return /* @__PURE__ */ jsx3("span", { className: "inline-block align-middle mx-1 my-1", children: /* @__PURE__ */ jsx3( Badge_default, { variant: "solid", action: "error", iconRight: /* @__PURE__ */ jsx3(XCircleIcon, {}), size: "large", className: "py-1 px-2", children: "N\xE3o respondido" } ) }); } const displayText = selectedOptionText ?? "Op\xE7\xE3o n\xE3o encontrada"; return /* @__PURE__ */ jsx3("span", { className: "inline-block align-middle mx-1 my-1", children: /* @__PURE__ */ jsx3( Badge_default, { variant: "solid", action: isCorrect ? "success" : "error", iconRight: isCorrect ? /* @__PURE__ */ jsx3(CheckCircleIcon, {}) : /* @__PURE__ */ jsx3(XCircleIcon, {}), size: "large", className: "py-1 px-2", children: displayText } ) }); }; const renderCorrectAnswer = (placeholderId) => { const correctOptionText = getOptionTextById(placeholderId); const displayText = correctOptionText ?? "[Resposta n\xE3o dispon\xEDvel]"; return /* @__PURE__ */ jsx3("span", { className: "inline mx-1 text-success-600 font-semibold border-b-2 border-success-600", children: displayText }); }; const renderTextWithElements = (text, isCorrectAnswer) => { const elements = []; let lastIndex = 0; const nextId = () => elements.length; const regex = /\{([a-zA-Z0-9-]+)\}/g; let match; while ((match = regex.exec(text)) !== null) { const [fullMatch, placeholderId] = match; const startIndex = match.index; if (startIndex > lastIndex) { elements.push({ element: text.slice(lastIndex, startIndex), id: `${baseId}-text-${nextId()}` }); } if (isCorrectAnswer) { elements.push({ element: renderCorrectAnswer(placeholderId), id: `${baseId}-answer-sheet-${nextId()}` }); } else { elements.push({ element: renderStudentBadge(placeholderId), id: `${baseId}-student-${nextId()}` }); } lastIndex = match.index + fullMatch.length; } if (lastIndex < text.length) { elements.push({ element: text.slice(lastIndex), id: `${baseId}-text-${nextId()}` }); } return elements; }; if (!additionalContent) { return /* @__PURE__ */ jsx3("div", { className: "pt-2", children: /* @__PURE__ */ jsx3(Text_default, { size: "md", color: "text-text-600", weight: "normal", children: "Nenhum conte\xFAdo dispon\xEDvel para esta quest\xE3o." }) }); } return /* @__PURE__ */ jsxs("div", { className: "pt-2 space-y-4", children: [ /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [ /* @__PURE__ */ jsx3(Text_default, { size: "xs", weight: "normal", color: "text-text-500", children: "Resposta do aluno:" }), /* @__PURE__ */ jsx3("div", { className: "p-3 bg-background-50 rounded-lg border border-border-100", children: /* @__PURE__ */ jsx3( Text_default, { as: "div", size: "md", color: "text-text-900", weight: "normal", className: "leading-[2.5] *:inline", children: renderTextWithElements(cleanText, false).map((element) => /* @__PURE__ */ jsx3(Fragment, { children: element.element }, element.id)) } ) }) ] }), /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [ /* @__PURE__ */ jsx3(Text_default, { size: "xs", weight: "normal", color: "text-text-500", children: "Gabarito:" }), /* @__PURE__ */ jsx3("div", { className: "p-3 bg-background-50 rounded-lg border border-border-100", children: /* @__PURE__ */ jsx3( Text_default, { as: "div", size: "md", color: "text-text-900", weight: "normal", className: "leading-[2.5] *:inline", children: renderTextWithElements(cleanText, true).map((element) => /* @__PURE__ */ jsx3(Fragment, { children: element.element }, element.id)) } ) }) ] }) ] }); }; // src/utils/questionRenderer/trueOrFalse/index.tsx import { Fragment as Fragment2, jsx as jsx4, jsxs as jsxs2 } from "react/jsx-runtime"; var renderQuestionTrueOrFalse = ({ question, result }) => { const options = question.options || []; const getLetterByIndex = (index) => String.fromCodePoint(97 + index); const shouldShowStatus = result?.answerStatus !== "PENDENTE_AVALIACAO" /* PENDENTE_AVALIACAO */ && result?.answerStatus !== "NAO_RESPONDIDO" /* NAO_RESPONDIDO */; return /* @__PURE__ */ jsx4("div", { className: "pt-2", children: /* @__PURE__ */ jsx4("div", { className: "flex flex-col gap-3.5", children: options.map((option, index) => { const studentSelection = result?.selectedOptions?.find( (op) => op.optionId === option.id ); const correctAnswerOption = result?.options?.find( (op) => op.id === option.id ); const hasCorrectAnswer = correctAnswerOption?.isCorrect !== void 0; const statementIsTrue = correctAnswerOption?.isCorrect ?? false; const hasAnswered = studentSelection !== void 0; const studentMarkedTrue = studentSelection?.isCorrect ?? false; const isStudentCorrect = hasCorrectAnswer && hasAnswered && studentMarkedTrue === statementIsTrue; const canShowCorrectness = shouldShowStatus && hasCorrectAnswer; const variantCorrect = isStudentCorrect ? "correct" /* CORRECT */ : "incorrect" /* INCORRECT */; const studentAnswer = studentMarkedTrue ? "V" /* VERDADEIRO */ : "F" /* FALSO */; const correctAnswer = statementIsTrue ? "V" /* VERDADEIRO */ : "F" /* FALSO */; return /* @__PURE__ */ jsxs2( "section", { className: "flex flex-col gap-2", children: [ /* @__PURE__ */ jsxs2( "div", { className: cn( "flex flex-row justify-between items-center gap-2 p-2 rounded-md border", canShowCorrectness && hasAnswered ? getStatusStyles(variantCorrect) : "" ), children: [ /* @__PURE__ */ jsxs2(Text_default, { as: "span", size: "sm", weight: "normal", color: "text-text-900", children: [ getLetterByIndex(index).concat(") "), /* @__PURE__ */ jsx4(HtmlMathRenderer_default, { content: option.option, inline: true }) ] }), canShowCorrectness && hasAnswered && /* @__PURE__ */ jsx4("div", { className: "flex-shrink-0", children: getStatusBadge( isStudentCorrect ? "correct" /* CORRECT */ : "incorrect" /* INCORRECT */ ) }) ] } ), canShowCorrectness && /* @__PURE__ */ jsx4("span", { className: "flex flex-row gap-2 items-center flex-wrap", children: hasAnswered ? /* @__PURE__ */ jsxs2(Fragment2, { children: [ /* @__PURE__ */ jsxs2(Text_default, { size: "2xs", weight: "normal", color: "text-text-800", children: [ "Resposta selecionada: ", studentAnswer ] }), !isStudentCorrect && /* @__PURE__ */ jsxs2(Text_default, { size: "2xs", weight: "normal", color: "text-text-800", children: [ "| Resposta correta: ", correctAnswer ] }) ] }) : /* @__PURE__ */ jsxs2(Text_default, { size: "2xs", weight: "normal", color: "text-text-800", children: [ "N\xE3o respondida | Resposta correta: ", correctAnswer ] }) }) ] }, option.id || `option-${index}` ); }) }) }); }; // src/utils/questionRenderer/dissertative/index.tsx import { jsx as jsx5, jsxs as jsxs3 } from "react/jsx-runtime"; var renderQuestionDissertative = ({ result }) => { const localAnswer = result?.answer || ""; return /* @__PURE__ */ jsxs3("div", { className: "pt-2 space-y-4", children: [ /* @__PURE__ */ jsxs3("div", { className: "space-y-2", children: [ /* @__PURE__ */ jsx5(Text_default, { size: "sm", weight: "normal", color: "text-text-950", children: "Resposta do aluno" }), /* @__PURE__ */ jsx5("div", { className: "p-3 bg-background-50 rounded-lg border border-border-100", children: localAnswer ? /* @__PURE__ */ jsx5( HtmlMathRenderer_default, { content: localAnswer, className: "text-sm text-text-700" } ) : /* @__PURE__ */ jsx5(Text_default, { size: "sm", weight: "normal", color: "text-text-700", children: "Nenhuma resposta fornecida" }) }) ] }), result?.answerStatus === "RESPOSTA_INCORRETA" /* RESPOSTA_INCORRETA */ && result?.teacherFeedback && /* @__PURE__ */ jsxs3("div", { className: "space-y-2", children: [ /* @__PURE__ */ jsx5(Text_default, { size: "xs", weight: "normal", color: "text-text-500", children: "Observa\xE7\xE3o do professor:" }), /* @__PURE__ */ jsx5("div", { className: "p-3 bg-background-50 rounded-lg border border-border-100", children: /* @__PURE__ */ jsx5( HtmlMathRenderer_default, { content: result.teacherFeedback, className: "text-sm text-text-700" } ) }) ] }) ] }); }; // src/utils/questionRenderer/fill/index.tsx import { jsx as jsx6 } from "react/jsx-runtime"; var renderQuestionFill = ({ question, result }) => { return /* @__PURE__ */ jsx6(FillQuestionContent, { question, result }); }; // src/utils/questionRenderer/image/index.tsx import { Fragment as Fragment3, jsx as jsx7, jsxs as jsxs4 } from "react/jsx-runtime"; var renderQuestionImage = ({ result }) => { const correctPositionRelative = { x: 0.48, y: 0.45 }; const correctRadiusRelative = 0.1; let userPositionRelative = null; try { if (result?.answer) { const parsed = typeof result.answer === "string" ? JSON.parse(result.answer) : result.answer; if (parsed && typeof parsed.x === "number" && typeof parsed.y === "number") { userPositionRelative = { x: parsed.x, y: parsed.y }; } } } catch { userPositionRelative = null; } const isCorrect = userPositionRelative ? Math.sqrt( Math.pow(userPositionRelative.x - correctPositionRelative.x, 2) + Math.pow(userPositionRelative.y - correctPositionRelative.y, 2) ) <= correctRadiusRelative : false; const getUserCircleColorClasses = () => { if (!userPositionRelative) return ""; return isCorrect ? "bg-success-600/70 border-white" : "bg-indicator-error/70 border-white"; }; const getPositionDescription = (x, y) => { const xPercent = Math.round(x * 100); const yPercent = Math.round(y * 100); return `${xPercent}% da esquerda, ${yPercent}% do topo`; }; const getSpatialRelationship = () => { if (!userPositionRelative) { return `\xC1rea correta localizada em ${getPositionDescription( correctPositionRelative.x, correctPositionRelative.y )}. Nenhuma resposta do aluno fornecida.`; } const deltaX = userPositionRelative.x - correctPositionRelative.x; const deltaY = userPositionRelative.y - correctPositionRelative.y; const distance = Math.sqrt(Math.pow(deltaX, 2) + Math.pow(deltaY, 2)); const distancePercent = Math.round(distance * 100); let direction; if (Math.abs(deltaX) > Math.abs(deltaY)) { direction = deltaX > 0 ? "\xE0 direita" : "\xE0 esquerda"; } else { direction = deltaY > 0 ? "abaixo" : "acima"; } const correctPos = getPositionDescription( correctPositionRelative.x, correctPositionRelative.y ); const userPos = getPositionDescription( userPositionRelative.x, userPositionRelative.y ); return `\xC1rea correta localizada em ${correctPos}. Resposta do aluno em ${userPos}. A resposta do aluno est\xE1 ${distancePercent}% de dist\xE2ncia ${direction} da \xE1rea correta. ${isCorrect ? "A resposta est\xE1 dentro da \xE1rea de toler\xE2ncia e \xE9 considerada correta." : "A resposta est\xE1 fora da \xE1rea de toler\xE2ncia e \xE9 considerada incorreta."}`; }; const correctPositionDescription = getPositionDescription( correctPositionRelative.x, correctPositionRelative.y ); const imageAltText = `Quest\xE3o de imagem com \xE1rea correta localizada em ${correctPositionDescription}`; return /* @__PURE__ */ jsxs4("div", { className: "pt-2 space-y-4", children: [ /* @__PURE__ */ jsxs4("div", { className: "flex items-center gap-4 text-xs", children: [ /* @__PURE__ */ jsxs4("div", { className: "flex items-center gap-2", children: [ /* @__PURE__ */ jsx7("div", { className: "w-3 h-3 rounded-full bg-indicator-primary/70 border border-[#F8CC2E]" }), /* @__PURE__ */ jsx7(Text_default, { size: "sm", weight: "normal", color: "text-text-600", children: "\xC1rea correta" }) ] }), userPositionRelative && /* @__PURE__ */ jsxs4(Fragment3, { children: [ /* @__PURE__ */ jsxs4("div", { className: "flex items-center gap-2", children: [ /* @__PURE__ */ jsx7("div", { className: "w-3 h-3 rounded-full bg-success-600/70 border border-white" }), /* @__PURE__ */ jsx7(Text_default, { size: "sm", weight: "normal", color: "text-text-600", children: "Resposta correta" }) ] }), /* @__PURE__ */ jsxs4("div", { className: "flex items-center gap-2", children: [ /* @__PURE__ */ jsx7("div", { className: "w-3 h-3 rounded-full bg-indicator-error/70 border border-white" }), /* @__PURE__ */ jsx7(Text_default, { size: "sm", weight: "normal", color: "text-text-600", children: "Resposta incorreta" }) ] }) ] }) ] }), /* @__PURE__ */ jsxs4("div", { className: "relative w-full", children: [ /* @__PURE__ */ jsx7("div", { className: "sr-only", children: getSpatialRelationship() }), /* @__PURE__ */ jsx7( "img", { src: mock_image_question_default, alt: imageAltText, className: "w-full h-auto rounded-md" } ), /* @__PURE__ */ jsx7( "div", { role: "img", "aria-label": `\xC1rea correta marcada em ${correctPositionDescription}`, className: "absolute rounded-full bg-indicator-primary/70 border-4 border-[#F8CC2E] pointer-events-none", style: { minWidth: "50px", maxWidth: "160px", width: "15%", aspectRatio: "1 / 1", left: `calc(${correctPositionRelative.x * 100}% - 7.5%)`, top: `calc(${correctPositionRelative.y * 100}% - 15%)` }, children: /* @__PURE__ */ jsxs4( Text_default, { size: "sm", weight: "normal", color: "text-text-600", className: "sr-only", children: [ "C\xEDrculo amarelo indicando a \xE1rea correta da resposta, posicionado em", " ", correctPositionDescription ] } ) } ), userPositionRelative && /* @__PURE__ */ jsx7( "div", { role: "img", "aria-label": `Resposta do aluno marcada em ${getPositionDescription( userPositionRelative.x, userPositionRelative.y )}, ${isCorrect ? "correta" : "incorreta"}`, className: `absolute rounded-full border-4 pointer-events-none ${getUserCircleColorClasses()}`, style: { minWidth: "30px", maxWidth: "52px", width: "5%", aspectRatio: "1 / 1", left: `calc(${userPositionRelative.x * 100}% - 2.5%)`, top: `calc(${userPositionRelative.y * 100}% - 2.5%)` }, children: /* @__PURE__ */ jsxs4( Text_default, { size: "sm", weight: "normal", color: "text-text-600", className: "sr-only", children: [ "C\xEDrculo ", isCorrect ? "verde" : "vermelho", " indicando a resposta do aluno, posicionado em", " ", getPositionDescription( userPositionRelative.x, userPositionRelative.y ), ". A resposta est\xE1", " ", Math.round( Math.sqrt( Math.pow( userPositionRelative.x - correctPositionRelative.x, 2 ) + Math.pow( userPositionRelative.y - correctPositionRelative.y, 2 ) ) * 100 ), "% de dist\xE2ncia da \xE1rea correta e \xE9 considerada", " ", isCorrect ? "correta" : "incorreta", "." ] } ) } ) ] }) ] }); }; // src/utils/questionRenderer/connectDots/index.tsx import { Fragment as Fragment4, jsx as jsx8, jsxs as jsxs5 } from "react/jsx-runtime"; var renderQuestionConnectDots = ({ paddingBottom } = {}) => { return /* @__PURE__ */ jsxs5(Fragment4, { children: [ /* @__PURE__ */ jsx8(QuestionSubTitle, { subTitle: "Tipo de quest\xE3o: Ligar Pontos" }), /* @__PURE__ */ jsx8(QuestionContainer, { className: cn("", paddingBottom), children: /* @__PURE__ */ jsx8("div", { className: "space-y-4", children: /* @__PURE__ */ jsx8(Text_default, { size: "md", weight: "normal", color: "text-text-600", children: "Tipo de quest\xE3o: Ligar Pontos (n\xE3o implementado)" }) }) }) ] }); }; // src/utils/questionRenderer/index.tsx var questionRendererMap = { ["ALTERNATIVA" /* ALTERNATIVA */]: renderQuestionAlternative, ["MULTIPLA_ESCOLHA" /* MULTIPLA_ESCOLHA */]: renderQuestionMultipleChoice, ["VERDADEIRO_FALSO" /* VERDADEIRO_FALSO */]: renderQuestionTrueOrFalse, ["DISSERTATIVA" /* DISSERTATIVA */]: renderQuestionDissertative, ["PREENCHER_LACUNAS" /* PREENCHER_LACUNAS */]: renderQuestionFill, ["IMAGEM" /* IMAGEM */]: renderQuestionImage, ["RELACIONAR" /* RELACIONAR */]: renderQuestionConnectDots }; var renderFromMap = (renderers, questionType) => { if (!questionType) return null; const renderer = renderers[questionType]; return renderer ? renderer() : null; }; // src/components/CorrectActivityModal/CorrectActivityModal.tsx import { Fragment as Fragment5, jsx as jsx9, jsxs as jsxs6 } from "react/jsx-runtime"; var EssayCorrectionField = { IsCorrect: "isCorrect", TeacherFeedback: "teacherFeedback" }; var CorrectActivityModal = ({ isOpen, onClose, data, isViewOnly = false, onObservationSubmit, onQuestionCorrectionSubmit, answerSheetImageUrl, onViewScannedAnswerSheet }) => { const [observation, setObservation] = useState(""); const [isObservationExpanded, setIsObservationExpanded] = useState(false); const [isObservationSaved, setIsObservationSaved] = useState(false); const [savedObservation, setSavedObservation] = useState(""); const [attachedFiles, setAttachedFiles] = useState([]); const [savedFiles, setSavedFiles] = useState([]); const [existingAttachment, setExistingAttachment] = useState( null ); const fileInputRef = useRef(null); const addToast = ToastStore_default((state) => state.addToast); const [essayCorrections, setEssayCorrections] = useState({}); useEffect(() => { if (isOpen) { setObservation(""); setIsObservationExpanded(false); setAttachedFiles([]); setSavedFiles([]); setExistingAttachment(data?.attachment ?? null); if (data?.observation || data?.attachment) { setIsObservationSaved(true); setSavedObservation(data.observation || ""); } else { setIsObservationSaved(false); setSavedObservation(""); } const initialCorrections = {}; data?.questions?.forEach((questionData) => { if (questionData.question.questionType === "DISSERTATIVA" /* DISSERTATIVA */) { initialCorrections[questionData.questionNumber] = { isCorrect: questionData.correction?.isCorrect ?? null, teacherFeedback: questionData.correction?.teacherFeedback || "", isSaving: false, isSaved: questionData.correction?.isCorrect != null }; } }); setEssayCorrections(initialCorrections); } }, [ isOpen, data?.studentId, data?.observation, data?.attachment, data?.questions ]); const handleOpenObservation = () => { setIsObservationExpanded(true); }; const handleFilesAdd = (files) => { const newFile = files[0]; if (newFile) { setAttachedFiles([{ file: newFile, id: generateFileId() }]); } }; const handleFileRemove = (id) => { setAttachedFiles((prev) => prev.filter((f) => f.id !== id)); }; const handleSaveObservation = () => { if (observation.trim() || attachedFiles.length > 0 || existingAttachment) { if (!data?.studentId) { return; } setSavedObservation(observation); setSavedFiles([...attachedFiles]); setIsObservationSaved(true); setIsObservationExpanded(false); onObservationSubmit?.( data.studentId, observation, attachedFiles.map((f) => f.file), existingAttachment ); } }; const handleEditObservation = () => { setObservation(savedObservation); setAttachedFiles([...savedFiles]); setIsObservationSaved(false); setIsObservationExpanded(true); }; const handleSaveEssayCorrection = useCallback( async (questionNumber) => { if (!data?.studentId || !onQuestionCorrectionSubmit) return; const correction = essayCorrections[questionNumber]; if (correction?.isCorrect == null) { return; } setEssayCorrections((prev) => ({ ...prev, [questionNumber]: { ...prev[questionNumber], isSaving: true } })); try { const questionData = data?.questions.find( (q) => q.questionNumber === questionNumber ); if (!questionData) { console.error("Quest\xE3o n\xE3o encontrada:", questionNumber); return; } await onQuestionCorrectionSubmit(data.studentId, { questionId: questionData.question.id, isCorrect: correction.isCorrect, teacherFeedback: correction.teacherFeedback }); setEssayCorrections((prev) => ({ ...prev, [questionNumber]: { ...prev[questionNumber], isSaving: false, isSaved: true } })); addToast({ title: "Corre\xE7\xE3o salva", description: `A corre\xE7\xE3o da quest\xE3o ${questionNumber} foi salva com sucesso.`, variant: "solid", action: "success", position: "top-right" }); } catch (error) { console.error("Erro ao salvar corre\xE7\xE3o da quest\xE3o:", error); setEssayCorrections((prev) => ({ ...prev, [questionNumber]: { ...prev[questionNumber], isSaving: false } })); addToast({ title: "Erro ao salvar corre\xE7\xE3o", description: "N\xE3o foi poss\xEDvel salvar a corre\xE7\xE3o. Tente novamente.", variant: "solid", action: "warning", position: "top-right" }); } }, [ data?.studentId, data?.questions, essayCorrections, onQuestionCorrectionSubmit, addToast ] ); const updateEssayCorrection = useCallback( (questionNumber, field, value) => { setEssayCorrections((prev) => ({ ...prev, [questionNumber]: { ...prev[questionNumber], [field]: value, // Reset isSaved when isCorrect changes so badge doesn't update until saved ...field === EssayCorrectionField.IsCorrect ? { isSaved: false } : {} } })); }, [] ); const getAccordionTitle = (questionType) => { switch (questionType) { case "ALTERNATIVA" /* ALTERNATIVA */: case "MULTIPLA_ESCOLHA" /* MULTIPLA_ESCOLHA */: case "VERDADEIRO_FALSO" /* VERDADEIRO_FALSO */: return "Alternativas"; case "DISSERTATIVA" /* DISSERTATIVA */: return "Resposta"; case "PREENCHER_LACUNAS" /* PREENCHER_LACUNAS */: return "Preencher Lacunas"; case "IMAGEM" /* IMAGEM */: return "Imagem"; case "RELACIONAR" /* RELACIONAR */: return "Ligar Pontos"; default: return "Resposta"; } }; const renderQuestionContent = (questionData) => { const { question, result } = questionData; const questionType = question.questionType; const accordionTitle = getAccordionTitle(questionType); let content; switch (questionType) { case "ALTERNATIVA" /* ALTERNATIVA */: content = renderQuestionAlternative({ question, result }); break; case "MULTIPLA_ESCOLHA" /* MULTIPLA_ESCOLHA */: content = renderQuestionMultipleChoice({ question, result }); break; case "VERDADEIRO_FALSO" /* VERDADEIRO_FALSO */: content = renderQuestionTrueOrFalse({ question, result }); break; case "DISSERTATIVA" /* DISSERTATIVA */: content = /* @__PURE__ */ jsxs6(Fragment5, { children: [ renderQuestionDissertative({ result }), onQuestionCorrectionSubmit && /* @__PURE__ */ jsx9("div", { className: "space-y-4 border-t border-border-100 pt-4 mt-4", children: renderEssayCorrectionFields(questionData) }) ] }); break; case "PREENCHER_LACUNAS" /* PREENCHER_LACUNAS */: content = renderQuestionFill({ question, result }); break; case "IMAGEM" /* IMAGEM */: content = renderQuestionImage({ result }); break; case "RELACIONAR" /* RELACIONAR */: content = renderQuestionConnectDots({ paddingBottom: "" }); break; default: if (question.options && question.options.length > 0) { content = renderQuestionAlternative({ question, result }); } else { content = renderQuestionDissertative({ result }); } } return /* @__PURE__ */ jsx9( CardAccordation, { value: `accordion-${questionData.questionNumber}`, className: "border border-border-100 rounded-lg", trigger: /* @__PURE__ */ jsx9("div", { className: "py-3 pr-2 w-full", children: /* @__PURE__ */ jsx9(Text_default, { className: "text-sm font-bold text-text-950", children: accordionTitle }) }), children: content } ); }; const renderEssayCorrectionFields = (questionData) => { const correction = essayCorrections[questionData.questionNumber] || { isCorrect: null, teacherFeedback: "", isSaving: false }; let radioValue; if (correction.isCorrect === null) { radioValue = void 0; } else if (correction.isCorrect) { radioValue = "true"; } else { radioValue = "false"; } return /* @__PURE__ */ jsxs6(Fragment5, { children: [ /* @__PURE__ */ jsxs6("div", { className: "space-y-2", children: [ /* @__PURE__ */ jsx9(Text_default, { className: "text-sm font-semibold text-text-950", children: "Resposta est\xE1 correta?" }), /* @__PURE__ */ jsxs6("div", { className: "flex gap-4", children: [ /* @__PURE__ */ jsx9( Radio_default, { name: `isCorrect-${questionData.questionNumber}`, value: "true", id: `correct-yes-${questionData.questionNumber}`, label: "Sim", size: "medium", checked: radioValue === "true", onChange: (e) => { if (e.target.checked) { updateEssayCorrection( questionData.questionNumber, EssayCorrectionField.IsCorrect, true ); } } } ), /* @__PURE__ */ jsx9( Radio_default, { name: `isCorrect-${questionData.questionNumber}`, value: "false", id: `correct-no-${questionData.questionNumber}`, label: "N\xE3o", size: "medium", checked: radioValue === "false", onChange: (e) => { if (e.target.checked) { updateEssayCorrection( questionData.questionNumber, EssayCorrectionField.IsCorrect, false ); } } } ) ] }) ] }), /* @__PURE__ */ jsxs6("div", { className: "space-y-2", children: [ /* @__PURE__ */ jsx9(Text_default, { className: "text-sm font-semibold text-text-950", children: "Incluir observa\xE7\xE3o" }), /* @__PURE__ */ jsx9( TextArea_default, { value: correction.teacherFeedback, onChange: (e) => { updateEssayCorrection( questionData.questionNumber, EssayCorrectionField.TeacherFeedback, e.target.value ); }, placeholder: "Escreva uma observa\xE7\xE3o sobre a resposta do aluno", rows: 4, size: "medium" } ) ] }), /* @__PURE__ */ jsx9( Button_default, { size: "small", onClick: () => handleSaveEssayCorrection(questionData.questionNumber), disabled: correction.isCorrect === null || correction.isSaving || !onQuestionCorrectionSubmit, children: correction.isSaving ? "Salvando..." : "Salvar" } ) ] }); }; if (!data) return null; const title = isViewOnly ? "Detalhes da atividade" : "Corrigir atividade"; const formattedScore = data.score == null ? "-" : data.score.toFixed(1); const renderObservationSection = () => { const getFileNameFromUrl = (_url) => "Anexado"; const renderAttachmentInput = () => { if (attachedFiles.length > 0) { return /* @__PURE__ */ jsxs6("div", { className: "flex items-center justify-center gap-2 px-5 h-10 bg-secondary-500 rounded-full min-w-0 max-w-[150px]", children: [ /* @__PURE__ */ jsx9(PaperclipIcon, { size: 18, className: "text-text-800 flex-shrink-0" }), /* @__PURE__ */ jsx9("span", { className: "text-base font-medium text-text-800 truncate", children: attachedFiles[0].file.name }), /* @__PURE__ */ jsx9( "button", { type: "button", onClick: () => handleFileRemove(attachedFiles[0].id), className: "text-text-700 hover:text-text-800 flex-shrink-0", "aria-label": `Remover ${attachedFiles[0].file.name}`, children: /* @__PURE__ */ jsx9(XIcon, { size: 18 }) } ) ] }); } if (existingAttachment) { return /* @__PURE__ */ jsxs6("div", { className: "flex items-center gap-2", children: [ /* @__PURE__ */ jsxs6( "a", { href: existingAttachment, target: "_blank", rel: "noopener noreferrer", className: "flex items-center gap-2 px-5 h-10 bg-secondary-500 rounded-full min-w-0 max-w-[150px] hover:bg-secondary-600 transition-colors", children: [ /* @__PURE__ */ jsx9( PaperclipIcon, { size: 18, className: "text-text-800 flex-shrink-0" } ), /* @__PURE__ */ jsx9("span", { className: "text-base font-medium text-text-800 truncate", children: getFileNameFromUrl(existingAttachment) }) ] } ), /* @__PURE__ */ jsxs6( Button_default, { type: "button", variant: "outline", size: "small", onClick: () => fileInputRef.current?.click(), className: "flex items-center gap-2", children: [ /* @__PURE__ */ jsx9(PaperclipIcon, { size: 18 }), "Trocar" ] } ) ] }); } return /* @__PURE__ */ jsxs6( Button_default, { type: "button", variant: "outline", size: "small", onClick: () => fileInputRef.current?.click(), className: "flex items-center gap-2", children: [ /* @__PURE__ */ jsx9(PaperclipIcon, { size: 18 }), "Anexar" ] } ); }; if (isObservationSaved) { return /* @__PURE__ */ jsxs6("div", { className: "bg-background border border-border-100 rounded-lg p-4 space-y-2", children: [ /* @__PURE__ */ jsxs6("div", { className: "flex flex-col sm:flex-row sm:items-center sm:justify-between gap-3", children: [ /* @__PURE__ */ jsx9(Text_default, { className: "text-sm font-bold text-text-950", children: "Observa\xE7\xE3o" }), /* @__PURE__ */ jsxs6("div", { className: "flex items-center gap-3", children: [ savedFiles.length > 0 && /* @__PURE__ */ jsxs6("div", { className: "flex items-center gap-2 px-5 h-10 bg-secondary-500 rounded-full min-w-0 max-w-[150px]", children: [ /* @__PURE__ */ jsx9( PaperclipIcon, { size: 18, className: "text-text-800 flex-shrink-0" } ), /* @__PURE__ */ jsx9("span", { className: "text-base font-medium text-text-800 truncate", children: savedFiles[0].file.name }) ] }), savedFiles.length === 0 && existingAttachment && /* @__PURE__ */ jsxs6( "a", { href: existingAttachment, target: "_blank", rel: "noopener noreferrer", className: "flex items-center gap-2 px-5 h-10 bg-secondary-500 rounded-full min-w-0 max-w-[150px] hover:bg-secondary-600 transition-colors", children: [ /* @__PURE__ */ jsx9( PaperclipIcon, { size: 18, className: "text-text-800 flex-shrink-0" } ), /* @__PURE__ */ jsx9("span", { className: "text-base font-medium text-text-800 truncate", children: getFileNameFromUrl(existingAttachment) }) ] } ), /* @__PURE__ */ jsxs6( Button_default, { type: "button", variant: "outline", size: "small", onClick: handleEditObservation, className: "flex items-center gap-2 flex-shrink-0", children: [ /* @__PURE__ */ jsx9(PencilSimpleIcon, { size: 16 }), "Editar" ] } ) ] }) ] }), savedObservation && /* @__PURE__ */ jsx9("div", { className: "p-3 bg-background-50 rounded-lg", children: /* @__PURE__ */ jsx9(Text_default, { className: "text-sm text-text-700", children: savedObservation }) }) ] }); } if (isObservationExpanded) { return /* @__PURE__ */ jsxs6("div", { className: "bg-background border border-border-100 rounded-lg p-4 space-y-3", children: [ /* @__PURE__ */ jsx9(Text_default, { className: "text-sm font-bold text-text-950", children: "Observa\xE7\xE3o" }), /* @__PURE__ */ jsx9( "textarea", { value: observation, onChange: (e) => setObservation(e.target.value), placeholder: "Escreva uma observa\xE7\xE3o para o estudante", className: "w-full min-h-[80px] p-3