analytica-frontend-lib
Version:
Repositório público dos componentes utilizados nas plataformas da Analytica Ensino
1,321 lines (1,307 loc) • 57 kB
JavaScript
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